说一下对spring中IOC和aop的理解
(1)IOC就是控制反转,指创建对象的控制权转移给Spring框架进行管理,并由Spring根据配置文件去创建实例和管理各个实例之间的依赖关系,对象与对象之间松散耦合,也利于功能的复用。DI依赖注入,和控制反转是同一个概念的不同角度的描述,即应用程序在运行时依赖IOC容器来动态注入对象需要的外部依赖。
(2)最直观的表达就是,以前创建对象的主动权和时机都是由自己把控的,IOC让对象的创建不用去new了,可以由spring自动生产,使用java的反射机制,根据配置文件在运行时动态的去创建对象以及管理对象,并调用对象的方法的。
(3)Spring的IOC有三种注入方式:构造器注入、setter方法注入、根据注解注入。
AOP,一般称为面向切面,作为面向对象的一种补充,用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,这个模块被命名为“切面”(Aspect),减少系统中的重复代码,降低了模块间的耦合度,提高系统的可维护性。可用于权限认证、日志、事务处理。
AOP实现的关键在于代理模式,AOP代理主要分为静态代理和动态代理。静态代理的代表为AspectJ;动态代理则以Spring AOP为代表。
(1)AspectJ是静态代理,也称为编译时增强,AOP框架会在编译阶段生成AOP代理类,并将AspectJ(切面)织入到Java字节码中,运行的时候就是增强之后的AOP对象。
(2)Spring AOP使用的动态代理,所谓的动态代理就是说AOP框架不会去修改字节码,而是每次运行时在内存中临时为方法生成一个AOP对象,这个AOP对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,并回调原对象的方法。
Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理。简述一下springmvc的工作流程和组件
一、springmvc的工作流程:
1、用户向服务端发送一次请求,这个请求会先到中央控制器DispatcherServlet。
2、DispatcherServlet接收到请求后会调用HandlerMapping处理器映射器。由此得知,该请求该由哪个Controller来处理(并未调用Controller,只是得知)
3、DispatcherServlet调用HandlerAdapter处理器适配器,告诉处理器适配器应该要去执行哪个Controller
4、HandlerAdapter处理器适配器去执行Controller并得到ModelAndView(数据和视图),并层层返回给DispatcherServlet
5、DispatcherServlet将ModelAndView交给ViewReslover视图解析器解析,然后返回真正的视图。
6、DispatcherServlet将模型数据填充到视图中
7、DispatcherServlet将结果响应给用户
二、springmvc的相关组件:
1.DispatcherServlet:前端控制器,也称为中央控制器,它是整个请求响应的控制中心,组件的调用由它统一调度。
2.HandlerMapping:处理器映射器,它根据用户访问的URL映射到对应的后端处理器Handler。也就是说它知道处理用户请求的后端处理器,但是它并不执行后端处理器,而是将处理器告诉给中央处理器。
3.HandlerAdapter:处理器适配器,它调用后端处理器中的方法,返回逻辑视图ModelAndView对象。
4.ViewResolver:视图解析器,将ModelAndView逻辑视图解析为具体的视图(如JSP)。
5.Handler:后端处理器,对用户具体请求进行处理,也就是我们编写的Controller类。
- spring事务的实现方式是什么
spring事务的本质其实就是数据库对事务的支持,没有数据库的事务支持,spring是无法提供事务功能的。现在一般都使用@transactional注解来使用事务,spring事务的方式是AOP,当事务内部抛出异常后,切面捕获同时回滚事务。因此在事务方法内部不要捕获异常,否则会导致事务失效。
- spring事务的传播机制是什么
1.数据库不支持事务:Spring事务生效的前提是所连接的数据库要支持事务,如果底层的数据库都不支持事务,则Spring的事务肯定会失效。例如,如果使用的数据库为MySQL,并且选用了MyISAM存储弓|擎,则Spring的事务就会失效。
2.方法所在类没有被spring管理:如果事务方法所在的类没有加载到Spring IOC容器中,则Spring事务会失效。
3.方法没有被public修饰:如果事务所在的方法没有被public修饰,此时Spring的事务会失效。
4.同一个类中方法调用:在同一个类中,方法A没有使用事务注解,方法B使用了事务注解。若A调用B,则B的事务会失效。
5.方法内捕获异常:如果使用事务注解的方法内发生异常并在内部捕获处理,则事务不会生效。如果必须捕获处理,则处理完成后需要手动抛出异常。或者处理完成后手动回滚。
手动回滚代码:
TransactionAspectSupport.currentTransactionStatus(.setRollbackOnlyO);
6.抛出错误的异常:@Transactional注解默认回滚RuntimeException类型的异常,可以修改rollbackFor参数改为Exception。
@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
- spring的核心组成部分有哪些
1.Spring Context:提供框架式的Bean访问方式,以及JNDI、定时任务等。
2.Spring Core:核心类库,所有功能都依赖于该类库,提供IOC和DI服务。
3.Spring AOP:AOP服务。
4.Spring Web:提供了基本的面向Web的综合特性,提供对常见框架如Struts2的支持,Spring能够管理这些框架。
5.Spring MVC:提供面向Web应用的Model-View-Controller,即MVC实现。
6.Spring DAO:对JDBC的抽象封装,简化了数据访问异常的处理,并能统一管理JDBC事务。
7.Spring ORM:对现有的ORM框架的支持。
- spring的优点有哪些
(1)spring属于低侵入式设计,代码的污染极低;
(2)spring的DI机制将对象之间的依赖关系交由框架处理,减低组件的耦合性;
(3)Spring提供了AOP技术,支持将一些通用任务,如安全、事务、日志、权限等进行集中式管理,从而提供更好的复用。
(4)spring对于主流的应用框架提供了集成支持。
- spring中bean的作用域有哪些
(1)singleton:默认作用域,单例bean,每个容器中只有一个bean的实例。
(2)prototype:为每一个bean请求创建一个实例。
(3)request:为每一个request请求创建一个实例,在请求完成以后,bean会失效并被垃圾回收器回收。
(4)session:与request范围类似,同一个session会话共享一个实例,不同会话使用不同的实例。
(5)global-session:全局作用域,所有会话共享一个实例。如果想要声明让所有会话共享的存储变量的话,那么这全局变量需要存储在global-session中。
- spring是线程安全的吗
Spring容器本身并没有提供Bean的线程安全策略,因此可以说Spring容器中的Bean本身不具备线程安全的特性,但是具体情况还是要结合Bean的作用域来讨论。
(1)对于prototype作用域的Bean,每次都创建一个新对象,也就是线程之间不存在Bean共享,因此不会有线程安全问题。
(2)对于singleton作用域的Bean,所有的线程都共享一个单例实例的Bean,因此是存在线程安全问题的。但是如果单例Bean是一个无状态Bean,也就是线程中的操作不会对Bean的成员执行查询以外的操作,那么这个单例Bean是线程安全的。比如Controller类、Service类和Dao等,这些Bean大多是无状态的,只关注于方法本身。
可以采用ThreadLocal解决线程安全问题,为每个线程提供一个独立的变量副本,不同线程只操作自己线程的副本变量。
- spring bean的生命周期
(1)实例化Bean:对于BeanFactory容器,当客户向容器请求一个尚未初始化的bean时,或初始化bean的时候需要注入另一个尚未初始化的依赖时,容器就会调用createBean进行实例化。
对于ApplicationContext容器,当容器启动结束后,通过获取BeanDefinition对象中的信息,实例化所有的bean。
(2)设置对象属性(依赖注入):实例化后的对象被封装在BeanWrapper对象中,紧接着,Spring根据BeanDefinition中的信息以及通过BeanWrapper提供的设置属性的接口完成属性设置与依赖注入。
(3)处理Aware接口:Spring会检测该对象是否实现了xxxAware接口,通过Aware类型的接口,可以让我们拿到Spring容器的一些资源:如果这个Bean实现了BeanNameAware接口,会调用它实现的setBeanName(StringbeanId)方法,传入Bean的名字。
如果这个Bean实现了BeanClassLoaderAware接口,调用setBeanClassIoader(方法,传入ClassIoader对象的实例。
如果这个Bean实现了BeanFactoryAware接口,会调用它实现的setBeanFactory0方法,传递的是Spring.工厂自身。
如果这个Bean实现了ApplicationContextAware接口,会调用setApplicationContext(ApplicationContext)方法,传入Spring上下文。
(4)BeanPostProcessor前置处理:如果想对Bean进行一些自定义的前置处理,那么可以让Bean实现了BeanPostProcessor接口,那将会调用postProcessBeforeInitialization(Object obj, String s)方法。
(5)InitializingBean:如果Bean实现了InitializingBean接口,执行afeterPropertiesSet(方法。
(6)init-method:如果Bean在Spring配置文件中配置了init-method属性,则会自动调用其配置的初始化方法。
(7)BeanPostProcessor后置处理:如果这个Bean实现了BeanPostProcessor接口,将会调用postProcessAfterInitialization(Object obj, String s)方法由于这个方法是在Bean初始化结束时调用的,所以可以被应用于内存或缓存技术。
(8)DisposableBean:当Bean不再需要时,会经过清理阶段,如果Bean实现了DisposableBean这个接口,会调用其实现的destroy)方法。
(9)destroy-method:最后,如果这个Bean的Spring配置中配置了destroy-method属性,会自动调用其配置的销毁方法。
- spring框架中都用到了哪些设计模式
(1)工厂模式:Spring使用工厂模式,通过BeanFactory和ApplicationContext来创建对象。
(2)单例模式:Bean默认为单例模式。
(3)策略模式:例如Resource的实现类,针对不同的资源文件,实现了不同方式的资源获取策略。
(4)代理模式:Spring的AOP功能用到了JDK的动态代理和CGLIB字节码生成技术。
(5)模板方法:可以将相同部分的代码放在父类中,而将不同的代码放入不同的子类中,用来解决代码重复的问题。比如RestTemplate,JmsTemplate,JpaTemplate。
(6)适配器模式:Spring AOP的增强或通知(Advice)使用到了适配器模式,Spring MVC中也是用到了适配器模式适配Controller.
(7)观察者模式:Spring事件驱动模型就是观察者模式的一个经典应用。
(8)装饰者模式:可以根据客户的需求能够动态切换不同的数据源。比如我们的项目需要连接多个数据库,客户在每次访问中根据需要会去访问不同的数据库。
- spring aop有哪些名词概念
(1) 连接点(Joinpoint):指程序运行过程中所执行的方法。需要被AOP增强逻辑的方法。
(2)切面(Aspect):被抽取出来的公共模块,可以用来会横切多个对象。Aspect切面可以看成Pointcut切点和Advice通知的结合,一个切面可以由多个切点和通知组成。在Spring AOP中,切面可以在类上使用@AspectJ注解来实现。
(3)切点(Pointcut):切点用于定义要对哪些连接点进行拦截。切点分为execution方式和annotation方式。execution方式可以用路径表达式指定对哪些方法拦截,annotation可以在需要被拦截的方法_上加指定注解。
(4)通知(Advice):指要在连接点上执行的动作,即增强的逻辑,比如权限校验和、日志记录等。通知有各种类型,包括Around、Before、After、After returning、After throwing。
(5)目标对象(Target):包含连接点的对象,也称作被通知(Advice)的对象。由于Spring AOP是通过动态代理实现的,所以这个对象永远是一个代理对象。
(6)织入(Weaving):通过动态代理,在目标对象的方法(即连接点)中执行增强逻辑(Advice)的过程。
(7)引入(Introduction):添加额外的方法或者字段到被通知的类。Spring允许引入新的接口(以及对应的实现)到任何被代理的对象。例如,你可以使用一个引入来使bean实现IsModified接口,以便简化缓存机制。
- spring aop通知类型有哪些
1.前置通知(Before Advice):在连接点(Join point)之前执行的通知。
2.后置通知(After Advice):当连接点退出的时候执行的通知(不论是正常返回还是异常退出)。
3.环绕通知(Around Advice):包围一个连接点的通知,这是最强大的一种通知类型。环绕通知可以在方法调用前后完成自定义的行为。它也可以选择是否继续执行连接点或直接返回它们自己的返回值或抛出异常来结束执行。
4.返回后通知(AfterReturning Advice):在连接点正常完成后执行的通知(如果连接点抛出异常,则不执行)
5.抛出异常后通知(AfterThrowing advice):在方法抛,出异常退出时执行的通知。
- spring常用注解有哪些
1.用于声明bean的注解
@Component:在类定义之前添加@Component注解,他会被spring容器识别,并转为bean。
@Controller:用于控制层注解。
@Service:用于对业务逻辑层进行注解。
2.用于依赖注入的注解
@Autowired:由bean提供,可以作用在变量、setter方法、构造函数上,@Autowired默认是按照类型装配注入的,默认情况下它要求依赖对象必须存在(可以设置它required属性为false)。
@Resource:由JSR-250提供,@Resource默认是按照名称来装配注入的。
@Value:为属性注入值(属性上)。
3.Java配置类相关注解
@Configuration:声明当前类为配置类。
@Bean:注解在方法上,声明当前方法的返回值为一个bean,替代xml中的方式。
@ComponentScan:用于对Component进行描。
4.切面(AOP)相关注解
@Aspect:声明一个切面。
@After:在方法执行之后执行(方法上)。
@Before:在方法执行之前执行(方法上)。
@Around:在方法执行之前与之后执行(方法上)
- @resource和@autowired区别
1、共同点
两者都可以写在字段和setter方法上。两者如果都写在字段上,那么就不需要再写setter方法
2、不同点
@Autowired为Spring提供的注解,@Autowired注解是按照类型(byType)装配依赖对象,默认情况下它要求依赖对象必须存在,如果允许null值,可以设置它的required属性为false。如果我们想使用按照名称(byName)来装配,可以结合@Qualifier注解一起使用。
@Resource默认按照ByName自动注入,只有当找不到与名称匹配的bean才会按照类型来装配注入,由J2EE提供。
- 什么是循环依赖
一个类A依赖类B,而类B又依赖类A,当创建A时需要注入B对象,创建B时又需要注入A对象,如此往复循环无穷尽矣。类似的这样的场景就叫循环依赖。
循环依赖问题在Spring中主要有三种情况:
(1)通过构造方法进行依赖注入时产生的循环依赖问题。
(2)通过setter方法进行依赖注入且是在多例(原型)模式下产生的循环依赖问题。
(3)通过setter方法进行依赖注入且是在单例模式下产生的循环依赖问题。
在Spring中,只有第(3)种方式的循环依赖问题被解决了,其他两种方式在遇到循环依赖问题时都会产生异常。其实也很好解释:
第(1)种构造方法注入的情况下,在new对象的时候就会堵塞住了,其实也就是”先有鸡还是先有蛋“的历史难题。
第(2)种setter方法(多例)的情况下,每一次getBean()时,都会产生一个新的Bean,如此反复下去就会有无穷无尽的Bean产生了,最终就会导致OOM问题的出现。
Spring解决第(3)种循环依赖主要依靠的是三级缓存。
- spring如何通过三级缓存解决循环依赖问题
Spring中有三个缓存,用于存储单例的Bean实例,这三个缓存是彼此互斥的,不会针对同一个Bean的实例同时存储。如果调用getBean,则需要从三个缓存中依次获取指定的Bean实例。
一级缓存:用于存储单例模式下已经创建完成的Bean实例,供外部使用。
二级缓存:用于存储单例模式下正在创建中的Bean实例,此时只是分配了内存,还没有进行属性注入。
三级缓存:通过ObjectFactory对象来存储单例模式下正在创建中的Bean实例,此时只是分配了内存,还没有进行属性注入。针对同一对象只会被调用一次,产生Bean实例后,会放入一级缓存。
如果类A依赖B,类B依赖A,通过三级缓存,在实例化A时,会将没注入属性的A实例提前暴露在三级缓存中。当实例化B,会将没注入属性的B实例提前暴露在三级缓存中。B注入A时,会从三级缓存中获取提前暴露的A对象,A注入B时也是一样。
- 为什么spring解决循环依赖需要三级缓存而不是二级缓存
三级缓存生成对象时会判断是否需要代理,如果需要则返回代理后的对象,如果不需要就返回对象本身。生成对象后放入二级缓存,此后再次调用该对象会从二级缓存中获取。个人认为两级缓存也够用,更多的是从设计角度出发修改成的三级缓存。