1. Spring 概述
Spring 是一个开源的轻量级 Java 开发框架,目的是为了解决企业级应用开发的业务逻辑层和其他层的耦合问题,为开发 Java 应用提供全面的基础架构支持,解决企业级应用开发的复杂性,简化 Java 开发
Spring 核心的两个特性:IOC (控制反转,依赖注入)、AOP
为了降低 Java 开发的复杂性,Spring 采用了以下 4 中策略:
- 基于 POJO 的轻量级和最小侵入性编程
- 通过依赖注入和面向接口实现松耦合
- 基于切面和惯例进行声明式编程
- 通过切面和模版减少样板式代码
2. 什么是 IOC 容器?
IOC 容器是 spring 基础模块的核心,控制反转的理论思想,依赖注入的手段实现,将对象的控制权转移,交由 spring 来创建、管理、装配对象,通过 map 数据结构来存储。整个 bean 的生命周期都由 spring 来管理
IOC 容器的创建过程:
- 调用 prepareRefresh 方法做容器创建前的准备工作,设置其启动日期和活动标志以及执行属性源的任何初始化(属性资源、监听器、事件)。
- 调用 obtainFreshBeanFactory 创建容器 DefaultListableBeanFactory ,设置一些初始化属性(BeanPostProcessor,Aware接口的子类等)
- 通过 BeanDefinitionReader 加载 bean 的定义信息 BeanDefinition
- 调用 invokeBeanFactoryPostProcessors 处理 bean 的定义信息(如:PalceHodlerConfigurerSupport 对占位符的处理)
- 调用 registerBeanPostProcessors 向容器中注册 BeanPostProcessors ,方便后续对 bean 对象完成功能扩展
- 初始化消息资源、事件多播器、添加 bean 的监听器
- 通过反射的方式将 BeanDefinition 实例化 instantiateBean(默认无参构造器,ctor.newInstance()) 为具体的对象(getBeandoGetBeancreateBeandoCreateBean)
- 对象的初始化 initializeBean 过程(填充属性population,调用 aware 子类方法,调用 BeanPostProcessors 的前置处理方法,调用 init-method 方法,调用 BeanPostProcessors 的后置处理方法)
- 生成完整对象,通过 getBean() 获取
- 容器关闭销毁对象
3. Bean 的生命周期
- 调用 instantiateBean 通过无参构造器的 newInstance() 实例化 bean 对象
- 调用 population() 填充属性 (循环依赖)
- 调用 initializeBean() 初始化 bean,
- 调用 invokeAwareMenthod 完成 beanName、beanClassLoader、beanFactory 属性设置
- 调用 applyBeanPostProcessorsBeforeInitialization() 应用 BeanPostProcessors 的前置处理方法
- 调用 invokeInitMenthod()
- 判断是否实现了 InitializingBean 接口,若实现了,则调用 afterPropertiesSet()
- 判断是否有自定义的初始化方法,若有,则调用 invokeCustomInitMehtod()
- 调用 applyBeanPostProcessorsAfterInitialization() 应用 BeanPostProcessors 的后置处理方法 (aop 增强,AbstractAutoProxyCreator)
- 调用 registerDisposableBeanIfNecessary 注册 bean 销毁时的工作:DistructionAwareBeanPostProcessors,DisposableBean 接口,自定义 destroy 方法
4. Spring 是如何解决循环依赖的
循环依赖:一个 bean 对象的成员属性是另一个 bean 对象,如:A 依赖 B,B 依赖 A,bean A 在创建的过程中,进行属性填充时,需要引用 bean B,而 bean B 在创建的过程中进行属性填充时也需要应用 bean A,这样就形成了一个闭环
Spring 是通过三级缓存来解决循环依赖的问题的,ioc 容器中维护了三个 map 结构:singletonObjects(一级缓存)、earlySingletonObejcts(二级缓存)、singletonFactories(三级缓存),一级缓存用来存放完整对象,二级缓存用来存放不完整对象(一般是创建中未初始化完成),三级缓存用来存放 ObjectFactory 函数式接口,通过 getEarlyBeanReference 来判断返回代理对象还是原始对象,保证容器在运行的过程中同名的 bean 只有一个
三级缓存:在 doCreateBean 中执行完 createBeanInstance 之后判断是否为单例、是否创建中、是否允许早期暴露来判断是否通过 addSingletonFactoy 添加到三级缓存中
二级缓存:在 getSingleton 中第一次从三级缓存确定对象是代理对象还是普通对象的时候,同时删除三级缓存
一级缓存:生成完整对象之后,在 addSingleton 中放到一级缓存,删除二三级缓存
5. BeanFactory 和 FactoryBean 的区别
相同点:两者都是用来创建 bean 对象的
不同点:BeanFactory 是 IOC 容器的根接口,为 Spring 工厂提供了规范,使用 BeanFactory 创建 bean 对象需要遵循完整的生命周期流程;而 FactoryBean 提供了三个方法:isSingleton() -- 是否是单例、getObjectType() -- 获取返回对象类型、getObject() -- 自定义创建的过程(new、反射、动态代理),很容易的创建一个由 spring 容器管理的 bean 对象
6. spring 中用到的设计模式
单例模式:spring 中 bean 默认都是单例的
工厂模式:BeanFactory
原型模式:scope 为 prototype
模版方法模式:postProcessBeanFactory,onRefresh
装配者模式:BeanWrapper
策略模式:XmlBeanDefiniationReader
观察者模式:listener,event,multicast
适配器模式:adapter
7. spring aop 原理
aop 面向切面,是 ioc 容器的一个扩展,通过 BeanPostProcessors 的后置处理方法,来对 bean 进行功能扩展实现增强
- 代理对象的创建过程(advice,切面,切点)
- 通过jdk或者cglib的方式来生成代理对象
- 在执行方法调用的时候,会调用到生成的字节码文件中,直接回找到DynamicAdvisoredInterceptor类中的intercept方法,从此方法开始执行
- 根据之前定义好的通知来生成拦截器链
- 从拦截器链中依次获取每一个通知开始进行执行,在执行过程中,为了方便找到下一个通知是哪个,会有一个CglibMethodInvocation的对象,找的时候是从-1的位置一次开始查找并且执行的。
aop 通知:before(前置通知)、after(返回通知)、after_returning(正常返回通知)、after_throwing(异常返回通知)、round(环绕通知)
8. Spring 是如何事务回滚
spring 的事务是由 aop 来实现的,首先要生成具体的代理对象,然后按照 aop 的整套流程来执行具体的操作逻辑,正常情况下要通过通知来完成核心功能,但是事务不是通过通知来实现的,而是通过一个 TransactionInterceptor 来实现的,然后调用 invoke 来实现具体的逻辑
1、先做准备工作,解析各个方法上事务相关的属性,根据具体的属性来判断是否开始新事务
2、当需要开启的时候,获取数据库连接,关闭自动提交功能,开起事务
3、执行具体的sql逻辑操作
4、在操作过程中,如果执行失败了,那么会通过 completeTransactionAfterThrowing 看来完成事务的回滚操作,回滚的具体逻辑是通过 doRollBack 方法来实现的,实现的时候也是要先获取连接对象,通过连接对象来回滚
5、如果执行过程中,没有任何意外情况的发生,那么通过 commitTransactionAfterReturning 来完成事务的提交操作,提交的具体逻辑是通过 doCommit 方法来实现的,实现的时候也是要获取连接,通过连接对象来提交
10. Spring AOP and AspectJ AOP 有什么区别?
AOP实现的关键在于代理模式,AOP代理主要分为静态代理和动态代理。静态代理的代表为AspectJ;动态代理则以Spring AOP为代表。
(1) AspectJ是静态代理的增强,所谓静态代理,就是AOP框架会在编译阶段生成AOP代理类,因此也称为编译时增强,他会在编译阶段将AspectJ(切面)织入到Java字节码中,运行的时候就是增强之后的AOP对象。
(2) Spring AOP使用的动态代理,所谓的动态代理就是说AOP框架不会去修改字节码,而是每次运行时在内存中临时为方法生成一个AOP对象,这个AOP
11. JDK动态代理和CGLIB动态代理的区别
Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理:
-
JDK动态代理只提供接口的代理,不支持类的代理。核心 InvocationHandler 接口和 Proxy 类,InvocationHandler 通过 invoke() 方法反射来调用目标类中的代码,动态地将横切逻辑和业务编织在一起;接着,Proxy 利用 InvocationHandler 动态创建一个符合某一接口的的实例, 生成目标类的代理对象。
-
如果代理类没有实现 InvocationHandler 接口,那么 Spring AOP 会选择使用 CGLIB 来动态代理目标类。CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成指定类的一个子类对象,并覆盖其中特定方法并添加增强代码,从而实现 AOP。CGLIB 是通过继承的方式做的动态代理,因此如果某个类被标记为 final,那么它是无法使用 CGLIB 做动态代理的。
静态代理与动态代理区别在于生成 AOP 代理对象的时机不同,相对来说 AspectJ 的静态代理方式具有更好的性能,但是 AspectJ 需要特定的编译器进行处理,而