目录
1、BeanFactory与ApplicationContext区别
(1)分析Bean的创建是在容器初始化时还是在getBean时
(3)分析InitializingBean之afterPropertiesSet初始化方法的调用
(4)分析BeanFactoryPostProcessor初始化和调用情况
(5)分析BeanPostProcessor初始化和调用情况
(2)进入finishBeanFactoryInitialization
(3)单例bean通过set方法或者@Autowired进行循环依赖
2.1、@EnableTransactionManagement
(2)ProxyTransactionManagementConfiguration组件
(3)AnnotationTransactionAttributeSource
(4)TransactionInterceptor 事务拦截器,部分源码如下
一、Spring概述
1、简介
Spring是分层的full-stack(全栈)轻量级开源框架,以 IoC 和 AOP 为内核,提供了表现层 Spring MVC 和业务层、事务管理等众多的企业级应用技术,还能整合开源世界众多著名的第三方框架和类库,已经成为使用最多的 Java EE 企业应用开源框架。
2、优势
- 方便解藕,简化开发
- 通过Spring提供的IoC容器,可以将对象间的依赖关系交由Spring进行控制,避免硬编码所造成的过度程序耦合。用户也不必再为单例模式类、属性文件解析等这些很底层的需求编写代码,可以更专注于上层的应用。
- AOP编程支持
- 通过Spring的AOP功能,方便进行面向切面的编程,许多不容易用传统OOP实现的功能可以通过AOP轻松应付。
- 声明式事务
- @Transactional
- 可以将我们从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活的进行事务的管理,提高开发效率和质量。
- 方便单元测试
- 可以用非容器依赖的编程方式进行几乎所有的测试工作,测试不再是昂贵的操作,而是随手可做的事情。
- 方便集成各种优秀框架
- Spring可以降低各种框架的使用难度,提供了对各种优秀框架(Struts、Hibernate、Hessian、Quartz等)的直接支持。
- 降低Java API的使用难度
- Spring对JavaEE API(如JDBC、JavaMail、远程调用等)进行了薄薄的封装层,使这些API的使用难度大为降低。
- 源码是经典的java学习范例
- Spring的源代码设计精妙、结构清晰、匠心独用,处处体现着大师对Java设计模式灵活运用以及对Java技术的高深造诣。它的源代码无意是Java技术的最佳实践的范例。
3、核心结构
- Spring核心容器(Core Container) :容器是Spring框架最核心的部分,它管理着Spring应用中bean的创建、配置和管理。在该模块中,包括了Spring bean工厂,它为Spring提供了DI的功能。 基于bean工厂,我们还会发现有多种Spring应用上下文的实现。所有的Spring模块都构建于核心容器之上。
- 面向切面编程(AOP)/Aspects:Spring对面向切面编程提供了丰富的支持。这个模块是Spring应用系统中开发切面的基础,与DI一样,AOP可以帮助应用对象解耦。
- 数据访问与集成(Data Access/Integration):Spring的JDBC和DAO模块封装了大量样板代码,这样可以使得数据库代码变得简洁,也可以更专注于我们的业务,还可以避免数据库资源释放失败而引起的问题。 另外,Spring AOP为数据访问提供了事务管理服务,同时Spring还对ORM进行了集成,如Hibernate、MyBatis等。该模块由JDBC、Transactions、ORM、OXM 和 JMS 等模块组成。
- Web:该模块提供了SpringMVC框架给Web应用,还提供了多种构建和其它应用交互的远程调用方案。 SpringMVC框架在Web层提升了应用的松耦合水平。
- Test:为了使得开发者能够很方便的进行测试,Spring提供了测试模块以致力于Spring应用的测试。通过该模块,Spring为使用Servlet、JNDI等编写单元测试提供了一系列的mock对象实现。
二、核心思想
IOC和AOP不是spring提出的,在spring之前就已经存在,只不过更偏向于理论化,spring在技术层面把这两个思想做了非常好的实现。
1、IOC
1.1、什么是IOC
IoC(Inversion of Control:控制反转/反转控制),注意它是一个技术思想,不是一个技术。
实现描述的事情:Java开发领域对象的创建、管理的问题。
传统开发方式:比如类A依赖于类B,往往会在类A中new一个B的对象。
IoC思想下开发方式:我们不用自己去new对象了,而是由IoC容器(Spring框架)去帮助我们实例化对象并且管理它,我们需要使用哪个对象,去问IoC容器要即可。
我们丧失了一个权利(创建、管理对象的权利),得到了一个福利(不用考虑对象的创建、管理等一系列事情)。
为什么叫做控制反转?
控制:指的是对象创建(实例化、管理)的权利。
反转:控制权交给外部环境了(spring框架、IoC容器)。
1.2、IOC解决了什么问题
IOC解决了对象之间耦合的问题。
传统的方式,UserDao userDao = new UserDaoImpl(),在service层使用UserDao时,使用到的类就需要在类中通过new的方式来创建类对象,并且使用的实现类(UserDaoImpl)是固定的。这个时候如果我重新实现了接口UserDao实现类为UserDaoImplNew,那么我在将该实现类使用于各个类时就需要手动修改UserDao userDao = new UserDaoImpl()这部分编码,而通过spring ioc那么我只需要在使用的地方注入接口对象(UserDao)即可,至于注入哪个实现类通过配置的方式即可完成。
1.3、IOC和DI的区别
DI:Dependancy Injection(依赖注入)
怎么理解:IOC和DI描述的是同一件事情,只不过⻆度不一样罢了。
2、AOP
2.1、什么是AOP
AOP:Aspect oriented Programming面向切面编程/面向方面编程,AOP是OOP的延续,OOP三大特征:封装、继承和多态,OOP是一种垂直继承体系。
OOP编程思想可以解决大多数的代码重复问题,但是有一些情况是处理不了的,比如下面的在顶级父类Animal中的多个方法中相同位置出现了重复代码,OOP就解决不了:
横切逻辑代码
横切逻辑代码存在什么问题:
- 横切代码重复问题
- 横切逻辑代码和业务代码混杂在一起,代码臃肿,维护不方便
AOP出场,AOP独辟蹊径提出横向抽取机制,将横切逻辑代码和业务逻辑代码分析:
代码拆分容易,那么如何在不改变原有业务逻辑的情况下,悄无声息的把横切逻辑代码应用到原有的业务逻辑中,达到和原来一样的效果,这个是比较难的。
2.2、AOP在解决什么问题
在不改变原有业务逻辑情况下,增强横切逻辑代码,根本上解耦合,避免横切逻辑代码重复。
2.3、为什么叫做面向切面编程
「切」:指的是横切逻辑,原有业务逻辑代码我们不能动,只能操作横切逻辑代码,所以面向横切逻辑;
「面」:横切逻辑代码往往要影响的是很多个方法,每一个方法都如同一个点,多个点构成面,有一个面的概念在里面。
三、Spring IOC应用
1、BeanFactory与ApplicationContext区别
BeanFactory是Spring框架中IoC容器的顶层接口,它只是用来定义一些基础功能,定义一些基础规范,而ApplicationContext是它的一个子接口,所以ApplicationContext是具备BeanFactory提供的全部功能的。
通常,我们称BeanFactory为SpringIOC的基础容器,ApplicationContext是容器的高级接口,比BeanFactory要拥有更多的功能,比如说国际化支持和资源访问(xml,java配置类)等等。
启动容器的方式:
- Java环境下启动IoC容器
- ClassPathXmlApplicationContext:从类的根路径下加载配置文件(推荐使用)
- FileSystemXmlApplicationContext:从磁盘路径上加载配置文件
- AnnotationConfigApplicationContext:纯注解模式下启动Spring容器
- Web环境下启动IoC容器
- 从xml启动
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" > <web-app> <display-name>Archetype Created Web Application</display-name> <!--配置Spring ioc容器的配置文件--> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param> <!--使用监听器启动Spring的IOC容器--> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> </web-app>
- 从配置类启动
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" > <web-app> <display-name>Archetype Created Web Application</display-name> <!--告诉ContextloaderListener知道我们使用注解的方式启动ioc容器--> <context-param> <param-name>contextClass</param-name> <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value> </context-param> <!--配置启动类的全限定类名--> <context-param> <param-name>contextConfigLocation</param-name> <param-value>com.lagou.edu.SpringConfig</param-value> </context-param> <!--使用监听器启动Spring的IOC容器--> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> </web-app>
- 从xml启动
2、纯xml方式
(1)头文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
(2)实例化bean的三种方式
a、使用无参构造函数
在默认情况下,它会通过反射调用无参构造函数来创建对象。如果类中没有无参构造函数,将创建失败。
<!--配置service对象-->
<bean id="userService" class="com.lagou.service.impl.TransferServiceImpl"></bean>
b、使用静态方法创建
在实际开发中,我们使用的对象有些时候并不是直接通过构造函数就可以创建出来的,它可能在创建的过程中会做很多额外的操作。此时会提供一个创建对象的方法,恰好这个方法是static修饰的方法,即是此种情况。
<!--使用静态方法创建对象的配置方式-->
<bean id="userService" class="com.lagou.factory.BeanFactory"
factory-method="getTransferService"></bean>
调用工厂类BeanFactory的getTransferService方法来创建对象userService。
c、使用实例化方法创建
此种方式和上面静态方法创建其实类似,区别是用于获取对象的方法不再是static修饰的了,而是类中的一个普通方法。此种方式比静态方法创建的使用几率要高一些。
<!--使用实例方法创建对象的配置方式-->
<bean id="beanFactory" class="com.lagou.factory.instancemethod.BeanFactory"></bean>
<bean id="transferService" factory-bean="beanFactory"
factory- method="getTransferService"></bean>
首先创建BeanFactory的实例对象beanFactory,然后在调用该实例对象beanFactory的getTransferService方法来创建对象transferService
(3)bean的作用范围和生命周期
作用范围
在spring框架管理Bean对象的创建时,Bean对象默认都是单例的,但是它支持配置的方式改变作用范围。作用范围官方提供的说明如下图:
我们在实际开发中经常用到到就是singleton(单例)和prototype(原型,又称为多例)模式,配置方式如下:
<!--配置service对象-->
<bean id="transferService" class="com.lagou.service.impl.TransferServiceImpl"
scope="singleton"></bean>
生命周期
- 单例模式:singleton
- 对象出生:当创建容器时,对象就被创建了。
- 对象活着:只要容器在,对象一直活着。
- 对象死亡:当销毁容器时,对象就被销毁了。
- 一句话总结:单例模式的bean对象生命周期与容器相同。
- 多例模式:prototype
- 对象出生:当使用对象时,创建新的对象实例。
- 对象活着:只要对象在使用中,就一直活着。
- 对象死亡:当对象⻓时间不用时,被java的垃圾回收器回收了。
- 一句话总结:多例模式的bean对象,spring框架只负责创建,不负责销毁。
(4)bean标签属性
在基于xml的IoC配置中,bean标签是最基础的标签。它表示了IoC容器中的一个对象。换句话说,如果一个对象想让spring管理,在XML的配置中都需要使用此标签配置,Bean标签的属性如下:
- id属性:用于给bean提供一个唯一标识。在一个标签内部,标识必须唯一。
- class属性:用于指定创建Bean对象的全限定类名。
- name属性:用于给bean提供一个或多个名称,多个名称用空格分隔。
- factory-bean属性:用于指定创建当前bean对象的工厂bean的唯一标识。当指定了此属性之后,class属性失效。
- factory-method属性:用于指定创建当前bean对象的工厂方法,如配合factory-bean属性使用, 则class属性失效。如配合class属性使用,则方法必须是static的。
- scope属性:用于指定bean对象的作用范围。通常情况下就是singleton。当要用到多例模式时,可以配置为prototype。
- init-method属性:用于指定bean对象的初始化方法,此方法会在bean对象装配后调用。必须是一个无参方法。
- destory-method属性:用于指定bean对象的销毁方法,此方法会在bean对象销毁前执行。它只能为scope是singleton时起作用。
(5)DI依赖注入的xml配置
- 依赖注入分类
- 按照注入的方式分
- 构造函数注入:顾名思义,就是利用带参构造函数实现对类成员的数据赋值。
- set方法注入:它是通过类成员的set方法实现数据的注入。(使用最多的)
- 按照注入的数据类型分
- 基本类型和String注入的数据类型是基本类型或者是字符串类型的数据。
- 其他Bean类型注入的数据类型是对象类型,称为其他Bean的原因是,这个对象是要求出现在IoC容器中的。那么针对当前Bean来说,就是其他Bean了。
- 复杂类型(集合类型)
- 注入的数据类型是Aarry,List,Set,Map,Properties中的一种类型。
- 按照注入的方式分
- 依赖注入的配置实现之构造函数注入
- 顾名思义,就是利用构造函数实现对类成员的赋值。它的使用要求是,类中提供的构造函数参数个数必须和配置的参数个数一致,且数据类型匹配。同时需要注意的是,当没有无参构造时,则必须提供构造函数参数的注入,否则Spring框架会报错。
-
在使用构造函数注入时,涉及的标签是“constructor-arg”,该标签有如下属性:
-
name:用于给构造函数中指定名称的参数赋值
-
index:用于给构造函数中指定索引位置的参数赋值
-
value:用于指定基本类型或者String类型的数据
-
ref:用于指定其他Bean类型的数据。写的是其他bean的唯一标识
-
- 依赖注入的配置实现之set方法注入
- 顾名思义,就是利用字段的set方法实现赋值的注入方式。此种方式在实际开发中是使用最多的注入方式。
-
在使用set方法注入时,需要使用 property 标签,该标签属性如下:
-
name:指定注入时调用的set方法名称。(注:不包含set这三个字母,druid连接池指定属性名称)
-
value:指定注入的数据。它支持基本类型和String类型。
-
ref:指定注入的数据。它支持其他bean类型。写的是其他bean的唯一标识。
-
(6)复杂数据类型注入
它指的是集合类型数据。
集合分为两类,一类是List结构(数组结构),一类是Map接口(键值对) 。
3、xml与注解相结合
注意:
- 实际企业开发中,纯xml模式使用已经很少了
- 引入注解功能,不需要引入额外的jar
- xml+注解结合模式,xml文件依然存在,所以,spring IOC容器的启动仍然从加载xml开始
- 哪些bean的定义写在xml中,哪些bean的定义使用注解?
- 第三方jar中的bean定义在xml,比如Druid数据库连接池,自己开发的bean定义使用注解
(1)xml中标签与注解的对应(IoC)
XML形式 | 对应的注解形式 |
标签 | @Component("accountDao"),注解加在类上bean的id属性内容直接配置在注解后面。如果不配置,默认定义个这个bean的id为类的类名首字母小写;另外,针对分层代码开发提供了@Componenet的三种别名@Controller、@Service、@Repository分别用于控制层类、服务层类、dao层类的bean定义,这四个注解的用法完全一样,只是为了更清晰的区分而已 |
标签的scope属性 | @Scope("prototype"),默认单例,注解加在类上 |
标签的init-method属性 | @PostConstruct,注解加在方法上,该方法就是初始化后调用的方法 |
标签的destory-method属性 | @PreDestory,注解加在方法上,该方法就是销毁前调用的方法 |
(2)DI 依赖注入的注解实现方式
@Autowired(推荐使用)
@Autowired为Spring提供的注解,需要导入包org.springframework.beans.factory.annotation.Autowired
@Autowired采取的策略为按照类型注入
public class TransferServiceImpl {
@Autowired
private AccountDao accountDao;
}
如上代码所示,这样装配回去spring容器中找到类型为AccountDao的类,然后将其注入进来。这样会产生一个问题,当一个类型有多个bean值的时候,会造成无法选择具体注入哪一个的情况,这个时候我们需要配合着@Qualifier使用。
@Qualifier告诉Spring具体去装配哪个对象
public class TransferServiceImpl {
@Autowired
@Qualifier(name="jdbcAccountDaoImpl")
private AccountDao accountDao;
}
这个时候我们就可以通过类型和名称定位到我们想注入的对象。
@Resource
@Resource注解由J2EE提供,需要导入包 javax.annotation.Resource
@Resource 默认按照 ByName自动注入
public class TransferService {
@Resource
private AccountDao accountDao;
@Resource(name="studentDao")
private StudentDao studentDao;
@Resource(type="TeacherDao")
private TeacherDao teacherDao;
@Resource(name="manDao",type="ManDao")
private ManDao manDao;
}
- 如果同时指定了 name 和 type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常。
- 如果指定了 name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常。
- 如果指定了 type,则从上下文中找到类似匹配的唯一bean进行装配,找不到或是找到多个,都会抛出异常。
- 如果既没有指定name,又没有指定type,则自动按照byName方式进行装配。
注意:
@Resource 在jdk11中已经移除,如果要使用,需要单独引入jar包
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</dependency>
4、纯注解模式
改造xml+注解模式,将xml中遗留的内容全部以注解的形式迁移出去,最终删除xml,从Java配置类启动对应注解:
- @Configuration 注解,表名当前类是一个配置类
- @ComponentScan 注解,替代 context:component-scan
- @PropertySource,引入外部属性配置文件
- @Import 引入其他配置类
- @Value 对变量赋值,可以直接赋值,也可以使用 ${} 读取资源配置文件中的信息
- @Bean 将方法返回对象加入 SpringIOC 容器
四、Spring IOC高级特性
1、延迟加载
Bean的延迟加载(延迟创建)。
ApplicationContext容器的默认行为是在启动服务器时将所有 singleton bean 提前进行实例化。提前实例化意味着作为初始化过程的一部分,ApplicationContext 实例会创建并配置所有的singleton bean。
<bean id="testBean" class="cn.lagou.LazyBean" />
<!-- 该bean默认的设置为:立即加载 -->
<bean id="testBean" calss="cn.lagou.LazyBean" lazy-init="false" />
lazy-init="false",立即加载,表示在spring启动时,立刻进行实例化。
如果不想让一个singleton bean在ApplicationContext实现初始化时被提前实例化,那么可以将bean设置为延迟实例化。
<bean id="testBean" calss="cn.lagou.LazyBean" lazy-init="true" />
设置 lazy-init 为 true 的 bean 将不会在 ApplicationContext 启动时提前被实例化,而是第一次向容器通过 getBean 索取 bean 时实例化的。
如果一个设置了立即加载的 bean1,引用了一个延迟加载的 bean2 ,那么 bean1 在容器启动时被实例化,而 bean2 由于被 bean1 引用,所以也被实例化,这种情况也符合延时加载的 bean 在第一次调用时才被实例化的规则。
<beans default-lazy-init="true">
<!-- no beans will be eagerly pre-instantiated... -->
</beans>
如果一个 bean 的 scope 属性为 scope="pototype" 时,即使设置了 lazy-init="false",容器启动时也不会实例化bean,而是调用 getBean 方法实例化的。
应用场景
(1)开启延迟加载一定程度提高容器启动和运转性能。
(2)对于不常使用的Bean设置延迟加载,这样偶尔使用的时候再加载,不必要从一开始就创建该Bean占用资源。
2、FactoryBean 和 BeanFactory
BeanFactory接口是容器的顶级接口,定义了容器的一些基础行为,负责生产和管理Bean的一个工厂,具体使用它下面的子接口类型,比如ApplicationContext。此处我们重点分析FactoryBean,Spring中Bean有两种,一种是普通Bean,一种是工厂Bean(FactoryBean)。FactoryBean可以生成某一个类型的Bean实例(返回给我们),也就是说我们可以借助于它自定义Bean的创建过程。Bean创建的三种方式中的静态方法和实例化方法和FactoryBean作用类似,FactoryBean使用较多,尤其在Spring框架一些组件中会使用,还有其他框架和Spring框架整合时使用。
// 可以让我们自定义Bean的创建过程(完成复杂Bean的定义)
public interface FactoryBean<T> {
@Nullable
// 返回FactoryBean创建的Bean实例,如果isSingleton返回true,
// 则该实例会放到Spring容器的单例对象缓存池中Map
T getObject() throws Exception;
@Nullable
// 返回FactoryBean创建的Bean类型 Class<?> getObjectType();
// 返回作用域是否单例
default boolean isSingleton() {
return true;
}
}
3、后置处理器
Spring提供了两种后处理bean的扩展接口,分别为 BeanPostProcessor 和 BeanFactoryPostProcessor,两者在使用上是有所区别的。
工厂初始化(BeanFactory)—> Bean对象
在BeanFactory初始化之后可以使用BeanFactoryPostProcessor进行后置处理做一些事情。
在Bean对象实例化(并不是Bean的整个生命周期完成)之后可以用BeanPostProcessor进行后置处理做一些事情。
(1)BeanPostProcessor
BeanPostProcessor是针对Bean级别的处理,可以针对某个具体的Bean。
该接口提供了两个方法,分别在Bean的初始化方法前和初始化方法后执行,具体这个初始化方法指的是什么方法,类似我们在定义bean时,定义了init-method所指定的方法。定义一个类实现了BeanPostProcessor,默认是会对整个Spring容器中所有的bean进行处理。如果要对具体的某个bean处理,可以通过方法参数判断,两个类型参数分别为Object和String,第一个参数是每个bean的实例,第二个参数是每个bean的name或者id属性的值。所以我们可以通过第二个参数,来判断我们将要处理的具体的bean。
注意:处理是发生在Spring容器的实例化和依赖注入之后。
(2)BeanFactoryPostProcessor
BeanFactory级别的处理,是针对整个Bean的工厂进行处理,
典型应用:PropertyPlaceholderConfigurer
此接口只提供了一个方法,方法参数为ConfigurableListableBeanFactory,该参数类型定义了一些方法。
其中有个方法名为getBeanDefinition的方法,我们可以根据此方法,找到我们定义bean的BeanDefinition对象。然后我们可以对定义的属性进行修改,以下是BeanDefinition中的方法
方法名字类似我们bean标签的属性,setBeanClassName对应bean标签中的class属性,所以当我们拿到BeanDefinition对象时,我们可以手动修改bean标签中所定义的属性值。
BeanDefinition对象:我们在XML中定义的bean标签,Spring解析bean标签成为一个JavaBean,这个JavaBean就是BeanDefinition。
注意:调用BeanFactoryPostProcessor方法时,这时候bean还没有实例化,此时bean刚被解析成BeanDefinition对象。
五、Spring AOP应用
AOP本质上是在不改变原有业务逻辑的情况下增强横切逻辑(这部分在使用上是有限的)。
横切逻辑代码往往是:权限校验代码、日志代码、事务控制代码、性能监控代码。
1、相关业务术语
Joinpoint(连接点) | 它指的是那些可以用于把增强代码加入到业务主线中的点,那么由上图中我们可 以看出,这些点指的就是方法。在方法执行的前后通过动态代理技术加入增强的 代码。在Spring框架AOP思想的技术实现中,也只支持方法类型的连接点。 |
Pointcut(切入点) | 它指的是那些已经把增强代码加入到业务主线进来之后的连接点。由上图中,我 们看出表现层 transfer 方法就只是连接点,因为判断访问权限的功能并没有对其增强。 |
Advice(通知/增强) | 它指的是切面类中用于提供增强功能的方法。并且不同的方法增强的时机是不一 样的。比如,开启事务肯定要在业务方法执行之前执行,提交事务要在业务方法 正常执行之后执行,而回滚事务要在业务方法执行产生异常之后执行等等。那么 这些就是通知的类型。其分类有:前置通知、后置通知、异常通知、最终通知、环绕通知。 |
Target(目标对象) | 它指的是代理的目标对象。即被代理对象。 |
Proxy(代理) | 它指的是一个类被AOP织入增强后,产生的代理类。即代理对象。 |
Weaving(织入) | 它指的是把增强应用到目标对象来创建新的代理对象的过程。spring采用动态代 理织入,而AspectJ采用编译期织入和类装载期织入。 |
Aspect(切面) | 它指定是增强的代码所关注的方面,把这些相关的增强代码定义到一个类中,这 个类就是切面类。例如,事务切面,它里面定义的方法就是和事务相关的,像开 启事务,提交事务,回滚事务等等,不会定义其他与事务无关的方法。我们前面 的案例中 TrasnactionManager 就是一个切面。 |
2、AOP代理方式的选择
Spring实现AOP思想使用的是动态代理技术。默认情况下,Spring会根据被代理对象是否实现接口来选择使用JDK还是CGLIB。当被代理对象没有实现任何接口时,Spring会选择CGLIB。当被代理对象实现了接口,Spring会选择JDK官方的代理技术,不过我们可以通过配置的方式,让Spring强制使用CGLIB。
3、AOP的配置方式
在Spring的AOP配置中,也和IoC配置一样,支持3类配置方式。
第一类:使用XML配置
第二类:使用XML+注解组合配置
第三类:使用纯注解配置
4、AOP的实现
(1)xml模式
a、坐标
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.1.12.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
b、aop核心配置
<!-- Spring基于XML的AOP配置前期准备:在spring的配置文件中加入aop的约束
xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
Spring基于XML的AOP配置步骤:
第一步:把通知Bean交给Spring管理
第二步:使用aop:config开始aop的配置
第三步:使用aop:aspect配置切面
第四步:使用对应的标签配置通知的类型
<!--把通知bean交给spring来管理-->
<bean id="logUtil" class="com.lagou.utils.LogUtil"></bean>
<!--开始aop的配置-->
<aop:config>
<!--配置切面-->
<aop:aspect id="logAdvice" ref="logUtil">
<!--配置前置通知-->
<aop:before method="printLog" pointcut="execution(public *
com.lagou.service.impl.TransferServiceImpl.updateAccountByCardNo(com.lagou
.pojo.Account))">
</aop:before>
</aop:aspect>
</aop:config>
c、五种通知类型
I、前置通知
<!--
作用:用于配置前置通知。
出现位置: 它只能出现在aop:aspect标签内部
属性:method:用于指定前置通知的方法名称 pointcut:用于指定切入点表达式 pointcut-ref:用于指定切入点表达式的引用
-->
<aop:before method="printLog" pointcut-ref="pointcut1"></aop:before>
执行时机:前置通知永远都会在切入点方法(业务核心方法)执行之前执行。
细节:前置通知可以获取切入点方法的参数,并对其进行增强。
II、后置通知
<!--
作用:用于配置正常执行时通知
出现位置: 它只能出现在aop:aspect标签内部
属性:
method:用于指定后置通知的方法名称
pointcut:用于指定切入点表达式
pointcut-ref:用于指定切入点表达式的引用
-->
<aop:after-returning method="afterReturningPrintLog" pointcut-ref="pt1">
</aop:after-returning>
III、异常通知
<!--
作用:用于配置异常通知。
出现位置:它只能出现在aop:aspect标签内部
属性:
method:用于指定异常通知的方法名称
pointcut:用于指定切入点表达式
pointcut-ref:用于指定切入点表达式的引用
-->
<aop:after-throwing method="afterThrowingPrintLog" pointcut-ref="pt1">
</aop:after-throwing>
执行时机:异常通知的执行时机是在切入点方法(业务核心方法)执行产生异常之后,异常通知执行。如果切入点方法执行没有产生异常,则异常通知不会执行。
细节:异常通知不仅可以获取切入点方法执行的参数,也可以获取切入点方法执行产生的异常信息。
IV、最终通知
<!--
作用:用于指定最终通知。
出现位置: 它只能出现在aop:aspect标签内部
属性:
method:用于指定最终通知的方法名称
pointcut:用于指定切入点表达式
pointcut-ref:用于指定切入点表达式的引用
-->
<aop:after method="afterPrintLog" pointcut-ref="pt1"></aop:after>
执行时机:最终通知的执行时机是在切入点方法(业务核心方法)执行完成之后,切入点方法返回之前执行。 换句话说,无论切入点方法执行是否产生异常,它都会在返回之前执行。
细节:最终通知执行时,可以获取到通知方法的参数。同时它可以做一些清理操作。
V、环绕通知
<!--
作用:用于配置环绕通知。
出现位置: 它只能出现在aop:aspect标签的内部
属性:
method:用于指定环绕通知的方法名称
pointcut:用于指定切入点表达式
pointcut-ref:用于指定切入点表达式的引用
-->
<aop:around method="aroundPrintLog" pointcut-ref="pt1"></aop:around>
**特别说明**
环绕通知,它是有别于前面四种通知类型外的特殊通知。前面四种通知(前置,后置,异常和最终)它们都是指定何时增强的通知类型。而环绕通知,它是Spring框架为我们提供的一种可以通过编码的方式,控制增强代码何时执行的通知类型。它里面借助的ProceedingJoinPoint接口及其实现类,实现手动触发切入点方法的调用。
(2)xml+注解模式
XML中开启Spring对注解AOP的支持:
<!--开启spring对注解aop的支持-->
<aop:aspectj-autoproxy/>
示例:
/**
* 模拟记录日志 * @author 应癫
*/
@Component
@Aspect
public class LogUtil {
/**
* 我们在xml中已经使用了通用切入点表达式,供多个切面使用,那么在注解中如何使用呢?
* 第一步:编写一个方法
* 第二步:在方法使用@Pointcut注解
* 第三步:给注解的value属性提供切入点表达式
* 细节:
* 1.在引用切入点表达式时,必须是方法名+(),例如"pointcut()"。
* 2.在当前切面中使用,可以直接写方法名。在其他切面中使用必须是全限定方法名。
*/
@Pointcut("execution(* com.lagou.service.impl.*.*(..))")
public void pointcut() {
}
@Before("pointcut()")
public void beforePrintLog(JoinPoint jp) {
Object[] args = jp.getArgs();
System.out.println("前置通知:beforePrintLog,参数是:" + Arrays.toString(args));
}
@AfterReturning(value = "pointcut()", returning = "rtValue")
public void afterReturningPrintLog(Object rtValue) {
System.out.println("后置通知:afterReturningPrintLog,返回值 是:" + rtValue);
}
@AfterThrowing(value = "pointcut()", throwing = "e")
public void afterThrowingPrintLog(Throwable e) {
System.out.println("异常通知:afterThrowingPrintLog,异常是:" + e);
}
@After("pointcut()")
public void afterPrintLog() {
System.out.println("最终通知:afterPrintLog");
}
/**
* 环绕通知
*
* @param pjp * @return
*/
@Around("pointcut()")
public Object aroundPrintLog(ProceedingJoinPoint pjp) {
//定义返回值
Object rtValue = null;
try {
//前置通知 System.out.println("前置通知");
//1.获取参数
Object[] args = pjp.getArgs();
//2.执行切入点方法
rtValue = pjp.proceed(args);
//后置通知
System.out.println("后置通知");
} catch (Throwable t) {
//异常通知 System.out.println("异常通知");
t.printStackTrace();
} finally { //最终通知
System.out.println("最终通知");
}
return rtValue;
}
}
(3)注解模式
在使用注解驱动开发aop时,我们要明确的就是,是注解替换掉配置文件中的下面这行配置:
<!--开启spring对注解aop的支持-->
<aop:aspectj-autoproxy/>
在配置类中使用如下注解进行替换上述配置
@Configuration
@ComponentScan("com.lagou")
@EnableAspectJAutoProxy
//开启spring对注解AOP的支持
public class SpringConfiguration {
}
5、声明式事务
(1)xml模式
坐标
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.12.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.12.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.1.12.RELEASE</version>
</dependency>
配置
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--定制事务细节,传播行为、隔离级别等-->
<tx:attributes>
<!--一般性配置-->
<tx:method name="*" read-only="false"propagation="REQUIRED"
isolation="DEFAULT" timeout="-1"/>
<!--针对查询的覆盖性配置-->
<tx:method name="query*" read-only="true" propagation="SUPPORTS"/>
</tx:attributes>
</tx:advice>
<aop:config>
<!--advice-ref指向增强=横切逻辑+方位-->
<aop:advisor advice-ref="txAdvice" pointcut="execution(*com.lagou.edu.service.impl.TransferServiceImpl.*(..))"/>
</aop:config>
(2)xml+注解
配置
<!--配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManage r">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--开启spring对注解事务的支持-->
<tx:annotation-driven transaction-manager="transactionManager"/>
在接口、类或者方法上添加@Transactional注解
@Transactional(readOnly = true,propagation = Propagation.SUPPORTS)
(3)注解模式
Spring基于注解驱动开发的事务控制配置,只需要把 xml 配置部分改为注解实现。
只是需要一个注解替换掉xml配置文件中的
<tx:annotation-driven transaction- manager="transactionManager"/> 配置。
在 Spring 的配置类上添加 @EnableTransactionManagement 注解即可。
@EnableTransactionManagement
//开启spring注解事务的支持
public class SpringConfiguration {
}
六、Spring IOC源码剖析
1、Spring IOC初始化的主体流程
1.1、IOC的容器体系
IoC容器是Spring的核心模块,是抽象了对象管理、依赖关系管理的框架解决方案。Spring提供了很多的容器,其中BeanFactory是顶层容器(根容器),不能被实例化,它定义了所有 IoC容器必须遵从的一套原则,具体的容器实现可以增加额外的功能,比如我们常用到的ApplicationContext,其下更具体的实现如ClassPathXmlApplicationContext包含了解析xml等一系列的内容,AnnotationConfigApplicationContext则是包含了注解解析等一系列的内容。Spring IoC容器继承体系非常聪明,需要使用哪个层次用哪个层次即可,不必使用功能大而全的。
BeanFactory顶级方法栈如下:
1.2、Bean生命周期的关键时间点
模拟思路:创建一个TestBean,让其实现几个特殊的接口,并分别在构造器、接口方法中打断点,观察调用栈,分析出Bean对象创建和管理关键点的时机。
TestBean
public class TestBean implements InitializingBean {
public TestBean(){
System.out.println("构造TestBean");
}
public void afterPropertiesSet() throws Exception {
System.out.println("TestBean afterPropertiesSet ...");
}
}
BeanPostProcessor接口实现类
public class MyBeanPostProcessor implements BeanPostProcessor {
public MyBeanPostProcessor(){
System.out.println("构造MyBeanPostProcessor");
}
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if("testBean".equals(beanName)) {
System.out.println("BeanPostProcessor 实现类 postProcessBeforeInitialization 方法被调用中......");
}
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if("testBean".equals(beanName)) {
System.out.println("BeanPostProcessor 实现类 postProcessAfterInitialization 方法被调用中......");
}
return bean;
}
}
BeanFactoryPostProcessor接口实现类
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
public MyBeanFactoryPostProcessor(){
System.out.println("构造 MyBeanFactoryPostProcessor ");
}
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
System.out.println("BeanFactoryPostProcessor的实现方法 postProcessBeanFactory 调用中......");
}
}
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="testBean" class="com.spring.test.bean.TestBean"/>
<bean id="myBeanPostProcessor" class="com.spring.test.bean.MyBeanPostProcessor"/>
<bean id="myBeanFactoryPostProcessor" class="com.spring.test.bean.MyBeanFactoryPostProcessor"/>
</beans>
IoC容器源码分析用例
public class TestIoc {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
TestBean testBean = applicationContext.getBean(TestBean.class);
System.out.println(testBean);
}
}
(1)分析Bean的创建是在容器初始化时还是在getBean时
通过断点可以看出,在未设置延迟加载的情况下,Bean的创建是在容器初始化过程中完成的。
(2)分析构造函数调用情况
在TestBean的构造函数处打断点,
可以看到构造函数的调用时机是在AbstractApplicationContext类的refresh()方法的
finishBeanFactoryInitialization(beanFactory)处。
(3)分析InitializingBean之afterPropertiesSet初始化方法的调用
在TestBean的afterPropertiesSet()方法打断点,可以看到afterPropertiesSet()方法的调用时机也是在AbstractApplicationContext类的refresh()方法的finishBeanFactoryInitialization(beanFactory)处。
(4)分析BeanFactoryPostProcessor初始化和调用情况
在构造函数和postProcessBeanFactory()方法处打断点,发现初始化和方法调用均是在AbstractApplicationContext类refresh方法的invokeBeanFactoryPostProcessors(beanFactory)。
(5)分析BeanPostProcessor初始化和调用情况
分别在构造函数、postProcessBeforeInitialization()、postProcessAfterInitialization()处打断点,观察调用栈,发现
BeanPostProcessor:初始化在AbstractApplicationContext类refresh方法的registerBeanPostProcessors(beanFactory);
postProcessBeforeInitialization:调用在AbstractApplicationContext类refresh方法的finishBeanFactoryInitialization(beanFactory);
postProcessAfterInitialization:调用在AbstractApplicationContext类refresh方法的finishBeanFactoryInitialization(beanFactory);
(6)总结
经调试我们发现Bean对象创建的几个关键点时机都在调用AbstractApplicationContext类的refresh()方法中,可见这个方法对spring ioc容器的初始化来说非常重要。
关键点 | 触发代码 |
bean对象构造器 | AbstractApplicationContext refresh finishBeanFactoryInitialization(beanFactory) |
BeanFactoryPostProcessor初始化 | AbstractApplicationContext refresh invokeBeanFactoryPostProcessors(beanFactory) |
BeanFactoryPostProcessor方法调用 | AbstractApplicationContext refresh invokeBeanFactoryPostProcessors(beanFactory) |
BeanPostProcessor初始化 | AbstractApplicationContext refresh registerBeanPostProcessors(beanFactory) |
BeanPostProcessor方法调用 | AbstractApplicationContext refresh finishBeanFactoryInitialization(beanFactory) |
1.3、Spring IOC初始化主流程
我们可以看出spring ioc初始化的关键环节就在AbstractApplicationContext的refresh()方法中
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
// 第一步:刷新前的预处理
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
// 第二步:获取BeanFactory,默认实现是DefaultListableBeanFactory加载
// BeanDefition并注册到BeanDefitionRegistry
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
// 第三步:BeanFactory的预准备工作(BeanFactory进行一些设置,比如context的类加载器等)
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
// 第四步:BeanFactory准备工作完成后进行的后置处理工作
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
// 第五步:实例化并调用实现了BeanFactoryPostProcessor接口的bean
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
// 第六步:注册BeanPostProcessor(bean的后置处理器),在创建bean的前后等执行
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
// 第七步:初始化MessageSource组建(做国际化、消息绑定、消息解析)
initMessageSource();
// Initialize event multicaster for this context.
// 第八步:初始化事件派发器
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
// 第九步:子类重写这个方法,在容器刷新的时候可以自定义逻辑
onRefresh();
// Check for listener beans and register them.
// 第十步:注册应用的监听器,就是注册实现了ApplicationListener接口的监听器bean
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
// 第十一步:初始化所有剩下的非懒加载的单例bean,初始化创建非懒加载方式的单例bean实例(未设置属性)
// 填充属性,初始化方法调用(比如调用afterPropertiesSet方法、init-method方法)
// 调用BeanPostProcessor(后置处理器)对实例bean进行后置处理
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
// 第十二步:完成context的刷新。主要是调用LifecycleProcessor的onRefresh()方法,
// 并且发布事件(ContextRefreshedEvent)
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
2、BeanFactory创建流程
2.1、BeanFactory子流程
org.springframework.context.support.AbstractApplicationContext
|--> refresh()
|--> obtainFreshBeanFactory()
|--> refreshBeanFactory()
org.springframework.context.support.AbstractRefreshableApplicationContext
|--> refreshBeanFactory()
|--> loadBeanDefinitions() 多次进入该方法的调用
org.springframework.beans.factory.groovy.GroovyBeanDefinitionReader
|--> loadBeanDefinitions()
org.springframework.beans.factory.xml.XmlBeanDefinitionReader
|--> loadBeanDefinitions()
|--> doLoadBeanDefinitions() 真正“干活”的类(spring通常do开头为正真开始处理的方法)
// 将资源文件的流对象封装成Document对象
Document doc = doLoadDocument(inputSource, resource);
// 注册bean对象
int count = registerBeanDefinitions(doc, resource);
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + count + " bean definitions from " + resource);
}
return count;
2.2、BeanDefinition加载解析及注册子流程
(1)涉及到如下几个关系步骤
Resource定位:指对BeanDefinition对资源定位过程。通俗讲就是找到定义javabean信息的xml文件,并将其封装成Resource对象。
BeanDefinition载入:把用户定义好的javabean表示为IoC容器内部的数据结构,这个容器内部的数据结构就是BeanDefinition。
注册BeanDefinition到IoC容器。
(2)过程分析
step1
子流程入口在org.springframework.context.support.AbstractRefreshableApplicationContext#
refreshBeanFactory()
protected final void refreshBeanFactory() throws BeansException {
// 判断是否已有 bean factory
if (hasBeanFactory()) {
// 销毁 beans
destroyBeans();
// 关闭 bean factory
closeBeanFactory();
}
try {
// 实例化 DefaultListableBeanFactory
DefaultListableBeanFactory beanFactory = createBeanFactory();
// 设置序列化id
beanFactory.setSerializationId(getId());
// 自定义bean工厂的一些属性(是否覆盖、是否允许循环依赖)
customizeBeanFactory(beanFactory);
/**
* 加载应用中的BeanDefinitions
*/
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
// 赋值当前的 bean factory
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
step2
依次调用多个类的loadBeanDefinitions方法
org.springframework.context.support.AbstractXmlApplicationContext
org.springframework.beans.factory.support.AbstractBeanDefinitionReader
org.springframework.beans.factory.groovy.GroovyBeanDefinitionReader
org.springframework.beans.factory.xml.XmlBeanDefinitionReader#
doLoadBeanDefinitions()
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
// 读取xml信息,将xml中信息保存为Document对象
Document doc = doLoadDocument(inputSource, resource);
// 解析document对象,封装BeanDifinition对象并进行注册
int count = registerBeanDefinitions(doc, resource);
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + count + " bean definitions from " + resource);
}
return count;
}
}
step3
重点关注
org.springframework.beans.factory.xml.XmlBeanDefinitionReader
|-> doLoadBeanDefinitions()
|-> registerBeanDefinitions()
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();
/**
* 关注 registerBeanDefinitions 和 createReaderContext 方法
*/
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
先看:
org.springframework.beans.factory.xml.XmlBeanDefinitionReader
|-> createReaderContext()
/**
* Create the {@link XmlReaderContext} to pass over to the document reader.
*/
public XmlReaderContext createReaderContext(Resource resource) {
return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
this.sourceExtractor, this, getNamespaceHandlerResolver());
}
/**
* Lazily create a default NamespaceHandlerResolver, if not set before.
* @see #createDefaultNamespaceHandlerResolver()
*/
public NamespaceHandlerResolver getNamespaceHandlerResolver() {
if (this.namespaceHandlerResolver == null) {
this.namespaceHandlerResolver = createDefaultNamespaceHandlerResolver();
}
return this.namespaceHandlerResolver;
}
/**
* Create the default implementation of {@link NamespaceHandlerResolver} used if none is specified.
* <p>The default implementation returns an instance of {@link DefaultNamespaceHandlerResolver}.
* @see DefaultNamespaceHandlerResolver#DefaultNamespaceHandlerResolver(ClassLoader)
*/
protected NamespaceHandlerResolver createDefaultNamespaceHandlerResolver() {
ClassLoader cl = (getResourceLoader() != null ? getResourceLoader().getClassLoader() : getBeanClassLoader());
return new DefaultNamespaceHandlerResolver(cl);
}
可以看到此处,spring首先完成了NamespaceHandlerResolver的初始化。
再看:
org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader
|-> registerBeanDefinitions()
/**
* This implementation parses bean definitions according to the "spring-beans" XSD
* (or DTD, historically).
* <p>Opens a DOM Document; then initializes the default settings
* specified at the {@code <beans/>} level; then parses the contained bean definitions.
*/
@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
doRegisterBeanDefinitions(doc.getDocumentElement());
}
进入:
org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader
|-> doRegisterBeanDefinitions()
/**
* Register each bean definition within the given root {@code <beans/>} element.
*/
@SuppressWarnings("deprecation") // for Environment.acceptsProfiles(String...)
protected void doRegisterBeanDefinitions(Element root) {
// Any nested <beans> elements will cause recursion in this method. In
// order to propagate and preserve <beans> default-* attributes correctly,
// keep track of the current (parent) delegate, which may be null. Create
// the new (child) delegate with a reference to the parent for fallback purposes,
// then ultimately reset this.delegate back to its original (parent) reference.
// this behavior emulates a stack of delegates without actually necessitating one.
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = createDelegate(getReaderContext(), root, parent);
if (this.delegate.isDefaultNamespace(root)) {
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
if (StringUtils.hasText(profileSpec)) {
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
// We cannot use Profiles.of(...) since profile expressions are not supported
// in XML config. See SPR-12458 for details.
if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
if (logger.isDebugEnabled()) {
logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +
"] not matching: " + getReaderContext().getResource());
}
return;
}
}
}
/**
* 重点是:parseBeanDefinitions方法,preProcessXml、postProcessXml都是钩子方法
*/
preProcessXml(root);
parseBeanDefinitions(root, this.delegate);
postProcessXml(root);
this.delegate = parent;
}
进入:
org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader
|-> parseBeanDefinitions
/**
* Parse the elements at the root level in the document:
* "import", "alias", "bean".
* @param root the DOM root element of the document
*/
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
if (delegate.isDefaultNamespace(ele)) {
// 解析 bean 元素
parseDefaultElement(ele, delegate);
}
else {
// 解析自定义标签元素
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
进入:
org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader
|-> parseDefaultElement()
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
// import元素处理
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
// alias元素处理
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
// bean元素处理
// 解析bean元素
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
// 嵌套beans处理
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
doRegisterBeanDefinitions(ele);
}
}
进入:
org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader
|-> processBeanDefinition()
/**
* Process the given bean element, parsing the bean definition
* and registering it with the registry.
*/
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// 解析bean元素为BeanDefinition,但是此时使用BeanDefinitionHolder有包装成了BeanDefinitionHolder对象
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// Register the final decorated instance.
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// Send registration event.
// 完成BeanDefinition的注册
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
至此,注册流程结束,我们发现,所谓的注册就是把封装xml中定义的bean信息封装为BeanDefinition对象之后放入一个Map中,BeanFactory是以Map的结构组织这些BeanDefinition的。
// Still in startup registration phase
this.beanDefinitionMap.put(beanName, beanDefinition);
this.beanDefinitionNames.add(beanName);
在org.springframework.beans.factory.support.DefaultListableBeanFactory中可以看到该Map的定义:
/** Map of bean definition objects, keyed by bean name. */
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
/** List of bean definition names, in registration order. */
private volatile List<String> beanDefinitionNames = new ArrayList<>(256);
(3)时序图
3、Bean创建流程
(1)通过关键时机点分析
我们知道Bean创建子流程入口在
org.springframework.context.support.AbstractApplicationContext
|-> refresh()
|-> finishBeanFactoryInitialization()
/*
Instantiate all remaining (non-lazy-init) singletons.
初始化所有剩下的非懒加载的单例bean
初始化创建非懒加载方式的单例bean实例(未设置属性)
填充属性
初始化方法调用(比如调用afterPropertiesSet方法、init-method方法)
调用BeanPostProcessor(后置处理器)对实例bean进行后置处理
*/
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
(2)进入finishBeanFactoryInitialization
/**
* Finish the initialization of this context's bean factory,
* initializing all remaining singleton beans.
*/
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
// Initialize conversion service for this context.
if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
beanFactory.setConversionService(
beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
}
// Register a default embedded value resolver if no bean post-processor
// (such as a PropertyPlaceholderConfigurer bean) registered any before:
// at this point, primarily for resolution in annotation attribute values.
if (!beanFactory.hasEmbeddedValueResolver()) {
beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
}
// Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
for (String weaverAwareName : weaverAwareNames) {
getBean(weaverAwareName);
}
// Stop using the temporary ClassLoader for type matching.
beanFactory.setTempClassLoader(null);
// Allow for caching all bean definition metadata, not expecting further changes.
beanFactory.freezeConfiguration();
// Instantiate all remaining (non-lazy-init) singletons.
// 实例化所有立即加载的单例bean
beanFactory.preInstantiateSingletons();
}
(3)进入preInstantiateSingletons
public void preInstantiateSingletons() throws BeansException {
if (logger.isTraceEnabled()) {
logger.trace("Pre-instantiating singletons in " + this);
}
// Iterate over a copy to allow for init methods which in turn register new bean definitions.
// While this may not be part of the regular factory bootstrap, it does otherwise work fine.
List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
// Trigger initialization of all non-lazy singleton beans...
for (String beanName : beanNames) {
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
if (isFactoryBean(beanName)) {
Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
if (bean instanceof FactoryBean) {
final FactoryBean<?> factory = (FactoryBean<?>) bean;
boolean isEagerInit;
if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>)
((SmartFactoryBean<?>) factory)::isEagerInit,
getAccessControlContext());
}
else {
isEagerInit = (factory instanceof SmartFactoryBean &&
((SmartFactoryBean<?>) factory).isEagerInit());
}
if (isEagerInit) {
// 实例化bean
getBean(beanName);
}
}
}
else {
// 实例化bean
getBean(beanName);
}
}
}
}
可以看到工厂bean或者普通的bean,最终都是通过getBean()方法获取实例的。
(4)进入getBean的doGetBean
// Create bean instance.
// 创建单例bean
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
// 创建bean
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
throw ex;
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
(5)进入createBean
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory
|-> createBean
try {
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
if (logger.isTraceEnabled()) {
logger.trace("Finished creating instance of bean '" + beanName + "'");
}
return beanInstance;
}
(6)进入doCreateBean
创建bean此时并未设置属性
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
初始化bean实例
// Initialize the bean instance.
Object exposedObject = bean;
try {
populateBean(beanName, mbd, instanceWrapper);
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
4、lazy-init延迟加载机制原理
(1)lazy-init延迟加载机制分析
普通bean的初始化是在容器启动初始化阶段执行的,而被lazy-init=true修饰的bean则是在容器里第一次进行context.getBean()时进行触发。Spring启动的时候会把所有bean信息(包括xml和注解)解析转化成Spring能够识别的BeanDefinition并存到HashMap里提供下面的初始化时使用,然后对每个BeanDefinition进行处理,如果是懒加载的则在容器初始化阶段不处理,其它的则在容器初始化阶段进行初始化并依赖注入。
public void preInstantiateSingletons() throws BeansException {
// Iterate over a copy to allow for init methods which in turn register new bean definitions.
// While this may not be part of the regular factory bootstrap, it does otherwise work fine.
// 所有BeanDefinition集合
List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
// Trigger initialization of all non-lazy singleton beans...
// 触发所有非懒加载单例bean的初始化
for (String beanName : beanNames) {
// 获取bean定义
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
// 判断是否是懒加载单例bean,如果是单例的并且不是懒加载的则在容器创建时初始化
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
if (isFactoryBean(beanName)) {
Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
// 判断是否是 factoryBean
if (bean instanceof FactoryBean) {
final FactoryBean<?> factory = (FactoryBean<?>) bean;
boolean isEagerInit;
if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>)
((SmartFactoryBean<?>) factory)::isEagerInit,
getAccessControlContext());
}
else {
isEagerInit = (factory instanceof SmartFactoryBean &&
((SmartFactoryBean<?>) factory).isEagerInit());
}
if (isEagerInit) {
getBean(beanName);
}
}
}
else {
/**
* 如果是普通bean则进行初始化并依赖注入,此getBean(beanName)接下来触发的逻辑
* 和懒加载时context.getBean("beanName")所触发的逻辑是一样的
*/
getBean(beanName);
}
}
}
}
(2)总结
- 对于被修饰为lazy-init的bean,spring容器初始化阶段不会进行init并依赖注入,当第一次进行getBean时候才进行初始化并依赖注入;
- 对于非懒加载的bean,getBean的时候会从缓存里获取,因为容器初始化阶段Bean已经初始化完成并缓存了起来。
5、Spring IoC循环依赖问题
循环依赖就是两个或两个以上的Bean互相持有对方,最终形成闭环。
注意,这里不是函数的调用循环,是对象的相互依赖关系。
spring中循环依赖场景有:
- 构造器的循环依赖(构造器注入)
- Field属性的循环依赖(set注入)
其中,构造器的循环依赖问题无法解决,只能抛出BeanCurrentlyInCreationException异常,在解决属性循环依赖时,spring采用的方法是提前暴露对象。
(1)单例bean构造器参数循环依赖(无法解决)
(2)prototype原型bean循环依赖(无法解决)
对于原型bean的初始化过程中不论是通过构造器参数循环依赖还是通过set方法依赖,spring都会直接报错。
org.springframework.beans.factory.support.AbstractBeanFactory
|-> doGetBean()
// Fail if we're already creating this bean instance:
// We're assumably within a circular reference.
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
进入:isPrototypeCurrentlyInCreation
/**
* Return whether the specified prototype bean is currently in creation
* (within the current thread).
* @param beanName the name of the bean
*/
protected boolean isPrototypeCurrentlyInCreation(String beanName) {
Object curVal = this.prototypesCurrentlyInCreation.get();
return (curVal != null &&
(curVal.equals(beanName) || (curVal instanceof Set && ((Set<?>) curVal).contains(beanName))));
}
在获取bean之前如果这个原型bean正在被创建则直接抛出异常。原型bean在创建之前会进行标记这个beanName正在被创建,等创建结束之后会删除标记
else if (mbd.isPrototype()) {
// It's a prototype -> create a new instance.
Object prototypeInstance = null;
try {
// 创建原型bean之前添加标记
beforePrototypeCreation(beanName);
// 创建原型bean
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
// 创建原型bean之后删除标记
afterPrototypeCreation(beanName);
}
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
总结:spring不支持原型bean的循环依赖。
(3)单例bean通过set方法或者@Autowired进行循环依赖
spring的循环依赖的理论依据是基于java的引用传递,当获取对象的引用时,对象的属性是可以延后设置的,但是构造器必须是在获取引用之前。
spring通过set方法或者@Autowired方法解决循环依赖其实就是通过提前暴露一个ObjectFactory对象来完成,简单来说ClassA在调用构造器完成对象初始化之后,在调用ClassA的setClassB方法之前就把ClassA实例化的对象通过ObjectFactory提前暴露到spring容器中。
step1
spring容器初始化ClassA通过构造器初始化对象后提前暴露到spring容器;
// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
// 将初始化后的对象提前ObjectFactory对象注入到容器中
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
step2
ClassA调用setClassB方法,spring首先尝试从容器中获取ClassB,此时ClassB还不存在spring容器中;
step3
spring容器初始化ClassB,同时也将ClassB提前暴露到spring容器中;
step4
ClassB调用setClassA方法,spring从容器中获取ClassA,因为第一步已经提前暴露了ClassA,因此可以获取到ClassA实例。(ClassA通过spring容器获取ClassB,完成了对象初始化操作);
step5
这样ClassA和ClassB都完成了对象初始化操作,解决了循环依赖问题。
(4)结合生命周期理解spring如何解决循环依赖
在 Spring 框架中 Bean 的生命周期是指一个 Bean 从被创建、初始化、使用到销毁的整个过程。
- 加载并注册Bean定义:容器启动时首先加载所有的 BeanDefinition 将他们注册到 BeanaFactory 中,这些Bean定义包含了bean的元数据信息(类名、作用域、依赖关系等)此时还没有实际创建bean实例
- 调用 BeanFactoryPostProcessor:在所有的bean定义都注册完毕后spring容器会调用所有实现了 BeanFactoryPostProcessor 接口的bean,允许它们在实际创建bean实例之前对bean定义进行修改或处理。此时还没有进行bean的实例化,也没有进行属性的赋值和注入
- 实例化(Instantiation):容器通过反射机制创建Bean实例
- 在 BeanFactoryPostProcessor 完成对bean定义的处理后,spring容器才会开始创建bean实例,BeanPostProcessor开始介入
- 属性赋值(Populate Properties):容器为 Bean 的属性注入依赖
- 使用 @Autowired、@Value 等进行属性注入
- 初始化前处理(Post-Process Before Initialization):实现了 BeanPostProcessor 接口的bean可以在初始化之前对bean进行处理或修改
- 实现 BeanPostProcessor 接口的bean在容器执行到 BeanPostProcessor.postProcessBeforeInitialization(Object bean, String beanName) 时获取bean并修改
- 初始化(Initialization):bean进行自定义初始化(如资源加载、配置初始化)
- InitializingBean.afterPropertiesSet():如果bean实现了InitializingBean接口,spring将调用这个方法
- init-method:在 XML 配置或注解中指定的初始化方法
- @PostConstruct:方法上使用该注解的bean会在初始化时调用该方法
- 初始化后处理(Post-Process After Initialization):在bean初始化完成后,容器会调用 BeanPostProcessor 接口的 postProcessAfterInitialization 方法,允许对 bean 进行后置处理,常用于代理的创建等
- 实现 BeanPostProcessor 接口的bean在容器执行到BeanPostProcessor.postProcessAfterInitialization(Object bean, String beanName)时获取bean进行操作
- 使用(Use):Bean处于使用状态,由应用程序调用其方法来执行业务逻辑
- 销毁(Destruction):当容器关闭或作用域结束时,Spring容器会销毁bean,并进行一些清理工作
- DisposableBean.destroy():如果 bean 实现了 DisposableBean 接口,spring将调用这个方法
- destory-method:在 XML配置中指定的销毁方法
- @PreDestroy:bean在销毁时调用该方法
重点理解 实例化 --> 属性赋值 --> 初始化 这个过程,这里是解决循环依赖的关键:
循环依赖就是两个或两个以上的bean相互持有对方,最终形成闭环,是对象相互依赖的循环。
-
Prototype(原型模式)下循环依赖无法解决
-
原型模式下每次spring都会创建新的bean,而不是从缓存中获取现有的实例,原型bean的生命周期与单例bean不同,spring容器不管理原型bean的生命周期,因此也无法使用三级缓存机制来解决循环依赖
-
由于原型bean每次请求都会创建新实例,且不进行缓存管理,spring无法通过三级缓存机制解决循环依赖问题,每次创建原型bean时,必须完整初始化依赖的所有bean,导致循环依赖无法解决
-
-
Singleton(单例模式)
-
构造器注入循环依赖无法解决
-
是在bean实例化阶段通过类的构造方法将依赖注入到bean中
-
构造器注入依赖可以通过懒加载某些bean的初始化来避免在构造器注入时立即解析依赖从而打破循环依赖
-
构造器注入的特点是对象在被创建时,所有依赖必须通过构造器参数传递并注入,这要求构造对象时所有的依赖对象必须已经准备好并且初始化完成。三级缓存机制是在对象已经实例化(即调用了构造器)之后,通过缓存暴漏对象的早期引用来解决循环依赖的问题。但构造器注入要求依赖在对象创建时就已经完全提供,导致无法利用三级缓存解决这个问题。
-
-
setter方法注入的循环依赖可以解决
-
是在对象实例化(调用构造方法)后调用对象的setter方法来注入依赖的,这意味着对象可以先被实例化然后再注入它的依赖
-
在 setter 方法注入的情况下,spring 容器首先可以实例化 beanA 和 beanB ,然后再通过调用setter 方法注入依赖。这使得即使两个 bean 互相依赖也可以通过分步完成实例化和依赖注入解决循环依赖问题。由于 beanA 和 beanB 都已经被实例化了(尽管此时它们的依赖还未注入),spring 可以将这些早期的引用放入缓存中,然后完成依赖注入的过程
-
说一下三级缓存
-
一级缓存:用于存储完全初始化的单例bean,它是一个map存着bean的名称和创建好的实例
-
二级缓存:这个缓存用于存储早期暴露的单例bean,通常是已经实例化但尚未初始化完成的bean(这是为了防止在bean初始化过程中再次创建该bean)。它包含了可能尚未完成依赖注入的bean,用于在循环依赖的过程中提供未初始化完全的bean引用。
-
三级缓存:这个缓存存储的是对象工厂,它负责创建早期的单例bean引用,如果二级缓存中没有bean实例,而三级缓存中有一个用于创建该实例的工厂,spring就会使用这个工厂来创建bean的早期引用,并放入二级缓存。三级缓存可以延迟的创建bean的代理对象或早期引用,这样就可以在bean的生命周期早期阶段将bean暴露给其它依赖它的bean
-
-
-
字段注入(在属性字段上使用 @Autowired 注解)
-
spring通过反射直接将依赖注入到字段中
-
它与setter方法解决循环依赖的方式一样,都是依赖容器的三级缓存机制
-
-
七、Spring AOP源码剖析
1、代理对象的创建
1.1、时机点分析
我们发现在getBean之前,LagouBean对象已经产生(即在第一行初始化时完成对象创建),而且该对象是一个代理对象(cglib代理对象),所以说在容器初始化过程中目标Bean已经完成代理,并返回了代理对象。
1.2、代理对象的创建流程
org.springframework.context.support.AbstractApplicationContext
|-> refresh()
|-> finishBeanFactoryInitialization(beanFactory)
持续进入到:
org.springframework.beans.factory.support.AbstractBeanFactory
|-> doGetBean()
|-> createBean()
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory
|-> doCreateBean()
|-> createBeanInstance()
|-> instantiateBean()
/**
* 初始化bean,包括bean后置处理器初始化,
* bean的一些初始化方法的执行init-method,
* bean的实现等生命周期相关接口的属性注入
*/
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
// 执行所有的AwareMethods
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
invokeAwareMethods(beanName, bean);
return null;
}, getAccessControlContext());
}
else {
invokeAwareMethods(beanName, bean);
}
Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
// 执行所有的 BeanPostProcessor#postPrrocessorBeforeInitialization 初始化之前的处理器方法
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
try {
// 这里就开始执行afterPropertiesSet(实现了InitializingBean接口)方法和initMethod
invokeInitMethods(beanName, wrappedBean, mbd);
}
catch (Throwable ex) {
throw new BeanCreationException(
(mbd != null ? mbd.getResourceDescription() : null),
beanName, "Invocation of init method failed", ex);
}
if (mbd == null || !mbd.isSynthetic()) {
// 整个Bean初始化完成,执行后置处理器方法
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory
|-> applyBeanPostProcessorsAfterInitialization()
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
throws BeansException {
Object result = existingBean;
// 循环执行后置处理器
for (BeanPostProcessor processor : getBeanPostProcessors()) {
Object current = processor.postProcessAfterInitialization(result, beanName);
if (current == null) {
return result;
}
result = current;
}
return result;
}
org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator
|-> postProcessAfterInitialization()
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
// 检查下该类是否已经暴露过了(可能已经创建了,比如A依赖B时,创建A时候,就会先去创建B
// 当真正需要创建B时,就没有必要再代理一次已经代理过的对象),避免重复创建
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator
|-> wrapIfNecessary()
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
// targetSoureBeans包含,说明前面创建过
if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
// Create proxy if we have advice.
// 得到所有候选Advisor,对Advisors和bean的方法双层遍历匹配,最终得到一个List<Advisor>,即specificInterceptors
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
// 重点,创建代理对象
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator
|-> createProxy()
// 为指定bean创建代理对象
protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
@Nullable Object[] specificInterceptors, TargetSource targetSource) {
if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
}
// 创建代理的工作交给ProxyFactory
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.copyFrom(this);
// 根据一些情况判断是否要设置proxyTargetClass=true
if (!proxyFactory.isProxyTargetClass()) {
if (shouldProxyTargetClass(beanClass, beanName)) {
proxyFactory.setProxyTargetClass(true);
}
else {
evaluateProxyInterfaces(beanClass, proxyFactory);
}
}
// 把指定和通用拦截对象合并,并都适配成Advisor
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
// 设置参数
proxyFactory.addAdvisors(advisors);
proxyFactory.setTargetSource(targetSource);
customizeProxyFactory(proxyFactory);
proxyFactory.setFrozen(this.freezeProxy);
if (advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
}
// 上面准备做完就开始创建代理
return proxyFactory.getProxy(getProxyClassLoader());
}
org.springframework.aop.framework.ProxyFactory
|-> getProxy()
public class ProxyFactory extends ProxyCreatorSupport {
public Object getProxy(@Nullable ClassLoader classLoader) {
// 用ProxyFactory创建AopProxy, 然后用AopProxy创建Proxy, 所以这里重要的是看获取的AopProxy
// 对象是什么,然后进去看怎么创建动态代理, 提供了两种:jdk proxy, cglib
return createAopProxy().getProxy(classLoader);
}
}
org.springframework.aop.framework.ProxyCreatorSupport
public class ProxyCreatorSupport extends AdvisedSupport {
private AopProxyFactory aopProxyFactory;
public ProxyCreatorSupport() {
this.aopProxyFactory = new DefaultAopProxyFactory();
}
protected final synchronized AopProxy createAopProxy() {
if (!this.active) {
activate();
}
//先获取创建AopProxy的工厂, 再由此创建AopProxy
return getAopProxyFactory().createAopProxy(this);
}
public AopProxyFactory getAopProxyFactory() {
return this.aopProxyFactory;
}
}
流程就是用AopProxyFactory创建AopProxy, 再用AopProxy创建代理对象,这里的AopProxyFactory默认是DefaultAopProxyFactory,看他的createAopProxy方法
public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: Either an interface or a target is required for proxy creation.");
}
if (targetClass.isInterface()) {
return new JdkDynamicAopProxy(config);
}
return new ObjenesisCglibAopProxy(config);
} else {
return new JdkDynamicAopProxy(config);
}
}
private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {
Class<?>[] interfaces = config.getProxiedInterfaces();
return (interfaces.length == 0 || (interfaces.length == 1 && SpringProxy.class.equals(interfaces[0])));
}
}
这里决定创建代理对象是用JDK Proxy,还是用Cglib了,最简单的从使用方面使用来说:设置proxyTargetClass=true强制使用Cglib代理,什么参数都不设并且对象类实现了接口则默认用JDK代理,如果没有实现接口则也必须用Cglib
ProxyFactory#getProxy(java.lang.ClassLoader)
|-> CglibAopProxy#getProxy(java.lang.ClassLoader)
public Object getProxy(@Nullable ClassLoader classLoader) {
if (logger.isTraceEnabled()) {
logger.trace("Creating CGLIB proxy: " + this.advised.getTargetSource());
}
try {
Class<?> rootClass = this.advised.getTargetClass();
Assert.state("信Tar:getmecelatsjsamvuastbeavailablefor creating a CGLIB proxy");
Class<?> proxySuperClass = rootClass;
if (ClassUtils.isCglibProxyClass(rootClass)) {
proxySuperClass = rootClass.getSuperclass();
Class<?>[] additionalInterfaces = rootClass.getInterfaces();
for (Class<?> additionalInterface : additionalInterfaces) {
this.advised.addInterface(additionalInterface);
}
}
// Validate the class, writing log messages as necessary.
validateClassIfNecessary(proxySuperClass, classLoader);
// 配置 Cglib 增强
Enhancer enhancer = createEnhancer();
if (classLoader != null) {
enhancer.setClassLoader(classLoader);
if (classLoader instanceof SmartClassLoader && ((SmartClassLoader)classLoader).isClassReloadable(proxySuperClass)) {
enhancer.setUseCache(false);
}
}
enhancer.setSuperclass(proxySuperClass);
enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
enhancer.setStrategy(new ClassLoaderAwareUndeclaredThrowableStrategy(classLoader));
Callback[] callbacks = getCallbacks(rootClass);
Class<?>[] types = new Class<?>[callbacks.length];
for (int x = 0; x < types.length; x++) {
types[x] = callbacks[x].getClass();
}
// fixedInterceptorMap only populated at this point, after getCallbacks call above
enhancer.setCallbackFilter(new ProxyCallbackFilter(
this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap,
this.fixedInterceptorOffset));
enhancer.setCallbackTypes(types);
// 生成代理类,并且创建一个代理类的实例
return createProxyClassAndInstance(enhancer, callbacks);
} catch (CodeGenerationException | IllegalArgumentException ex) {
throw new AopConfigException("Could not generate CGLIB subclass of "
+ this.advised.getTargetClass()
+ ": Common causes of this problem include using a final class or a non-visible class",
ex);
}
catch (Throwable ex) {
// TargetSource.getTarget() failed
throw new AopConfigException("Unexpected AOP exception", ex);
}
}
1.3、AOP源码分析类方法调用关系课堂讲解过程中记录
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean
调用
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsAfterInitialization
调用
org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessAfterInitialization(后置处理器AbstractAutoProxyCreator完成bean代理对象创建)
调用
org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#wrapIfNecessary
调用
org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#createProxy (在这一步把委托对象的aop增强和通用拦截进行合并,最终给代理对象)
调用
org.springframework.aop.framework.DefaultAopProxyFactory#createAopProxy 调用
org.springframework.aop.framework.CglibAopProxy#getProxy(java.lang.ClassLoader)
2、Spring声明式事务控制
2.1、@EnableTransactionManagement
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {
@EnableTransactionManagement注解使用@Import标签引入了TransactionManagementConfigurationSelector类,这个类又向容器中导入了两个重要的组件
protected String[] selectImports(AdviceMode adviceMode) {
switch (adviceMode) {
case PROXY:
return new String[] {AutoProxyRegistrar.class.getName(),
ProxyTransactionManagementConfiguration.class.getName()};
case ASPECTJ:
return new String[] {determineTransactionAspectClass()};
default:
return null;
}
}
2.2、加载事务控制组建
(1)AutoProxyRegistrar
AutoProxyRegistrar类的registerBeanDefinitions方法中又注册了一个组件
candidateFound = true;
if (mode == AdviceMode.PROXY) {
AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
if ((Boolean) proxyTargetClass) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
return;
}
}
一直进入,最后进入
org.springframework.aop.config.AopConfigUtils
|-> registerAutoProxyCreatorIfNecessary()
发现最终,注册了一个叫做 InfrastructureAdvisorAutoProxyCreator 的 Bean,而这个类是 AbstractAutoProxyCreator 的子类,实现了 SmartInstantiationAwareBeanPostProcessor 接口
public class InfrastructureAdvisorAutoProxyCreator extends AbstractAdvisorAutoProxyCreator
public abstract class AbstractAdvisorAutoProxyCreator extends AbstractAutoProxyCreator
public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware
(2)ProxyTransactionManagementConfiguration组件
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {
@Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor() {
// 事务增强
BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
// 向事务增强器中注入 属性解析器 transactionAttributeSource
advisor.setTransactionAttributeSource(transactionAttributeSource());
// 向事务增强器中注入 事务拦截器 transactionInterceptor
advisor.setAdvice(transactionInterceptor());
if (this.enableTx != null) {
advisor.setOrder(this.enableTx.<Integer>getNumber("order"));
}
return advisor;
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
// 属性解析器 transactionAttributeSource
public TransactionAttributeSource transactionAttributeSource() {
return new AnnotationTransactionAttributeSource();
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
// 事务拦截器 transactionInterceptor
public TransactionInterceptor transactionInterceptor() {
TransactionInterceptor interceptor = new TransactionInterceptor();
interceptor.setTransactionAttributeSource(transactionAttributeSource());
if (this.txManager != null) {
interceptor.setTransactionManager(this.txManager);
}
return interceptor;
}
}
ProxyTransactionManagementConfiguration是一个容器配置类,注册了一个组件 transactionAdvisor,称为事务增强器,然后在这个事务增强器中又注入了两个属性: transactionAttributeSource,即属性解析器transactionAttributeSource 和 事务拦截器 transactionInterceptor。
(3)AnnotationTransactionAttributeSource
public class AnnotationTransactionAttributeSource extends AbstractFallbackTransactionAttributeSource
implements Serializable {
private static final boolean jta12Present;
private static final boolean ejb3Present;
static {
ClassLoader classLoader = AnnotationTransactionAttributeSource.class.getClassLoader();
jta12Present = ClassUtils.isPresent("javax.transaction.Transactional", classLoader);
ejb3Present = ClassUtils.isPresent("javax.ejb.TransactionAttribute", classLoader);
}
private final boolean publicMethodsOnly;
private final Set<TransactionAnnotationParser> annotationParsers;
}
属性解析器有一个成员变量是annotationParsers,是一个集合,可以添加多种注解解析器 (TransactionAnnotationParser),我们关注 Spring 的注解解析器,部分源码如下
protected TransactionAttribute parseTransactionAnnotation(AnnotationAttributes attributes) {
RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute();
// 以下对应@Transaction的属性
Propagation propagation = attributes.getEnum("propagation");
rbta.setPropagationBehavior(propagation.value());
Isolation isolation = attributes.getEnum("isolation");
rbta.setIsolationLevel(isolation.value());
rbta.setTimeout(attributes.getNumber("timeout").intValue());
rbta.setReadOnly(attributes.getBoolean("readOnly"));
rbta.setQualifier(attributes.getString("value"));
List<RollbackRuleAttribute> rollbackRules = new ArrayList<>();
for (Class<?> rbRule : attributes.getClassArray("rollbackFor")) {
rollbackRules.add(new RollbackRuleAttribute(rbRule));
}
for (String rbRule : attributes.getStringArray("rollbackForClassName")) {
rollbackRules.add(new RollbackRuleAttribute(rbRule));
}
for (Class<?> rbRule : attributes.getClassArray("noRollbackFor")) {
rollbackRules.add(new NoRollbackRuleAttribute(rbRule));
}
for (String rbRule : attributes.getStringArray("noRollbackForClassName")) {
rollbackRules.add(new NoRollbackRuleAttribute(rbRule));
}
rbta.setRollbackRules(rollbackRules);
return rbta;
}
属性解析器的作用之一就是用来解析@Transaction注解。
(4)TransactionInterceptor 事务拦截器,部分源码如下
// 构造方法传入
public TransactionInterceptor(PlatformTransactionManager ptm, Properties attributes) {
setTransactionManager(ptm);
setTransactionAttributes(attributes);
}
public Object invoke(MethodInvocation invocation) throws Throwable {
// Work out the target class: may be {@code null}.
// The TransactionAttributeSource should be passed the target class
// as well as the method, which may be from an interface.
Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
// Adapt to TransactionAspectSupport's invokeWithinTransaction...
// 添加事务
return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
}
(5)上述组建如何管理起来?
1、事务拦截器实现了MethodInterceptor接口,追溯一下上面提到的InfrastructureAdvisorAutoProxyCreator后置处理器,它会在代理对象执行目标方法的时候获取其拦截器链,而拦截器链就是这个TransactionInterceptor,这就把这两个组件联系起来;
2、构造方法传入PlatformTransactionManager(事务管理器)、TransactionAttributeSource(属性解析器),但是追溯一下上面贴的ProxyTransactionManagementConfiguration的源码,在注册事务拦截器的时候并没有调用这个带参构造方法,而是调用的无参构造方法,然后再调用set方法注入这两个属性,效果一样。
(6)invokeWithinTransaction方法
// If the transaction attribute is null, the method is non-transactional.
// 获取属性解析器,即在ProxyTransactionManagemrntConfiguration容器配置类中注册事务拦截器时注入的
TransactionAttributeSource tas = getTransactionAttributeSource();
final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
// 获取事务管理器
final PlatformTransactionManager tm = determineTransactionManager(txAttr);
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
try {
// This is an around advice: Invoke the next interceptor in the chain.
// This will normally result in a target object being invoked.
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
// target invocation exception
// 如果目标方法抛异常,会执行completeTransactionAfterThrowing(获取事务管理器,执行回滚操作)
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
cleanupTransactionInfo(txInfo);
}
// 如果目标方法正常运行,则会执行commitTransactionAfterReturning(获取事务管理器,执行事务提交操作)
commitTransactionAfterReturning(txInfo);
return retVal;
总结:
@EnableTransactionManagement 注解
(1)通过@import引入了TransactionManagementConfigurationSelector类,它的selectImports方法导入了另外两个类:
AutoProxyRegistrar和ProxyTransactionManagementConfiguration;
(2)AutoProxyRegistrar类分析方法registerBeanDefinitions中,引入了其他类,通过 AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry)引入InfrastructureAdvisorAutoProxyCreator,
它继承了AbstractAutoProxyCreator,是一个后置处理器类;
(3)ProxyTransactionManagementConfiguration 是一个添加了@Configuration注解的配置类 (注册bean)
注册事务增强器(注入属性解析器、事务拦截器)属性解析器:AnnotationTransactionAttributeSource,内部持有了一个解析器集合Set<TransactionAnnotationParser> annotationParsers;具体使用的是SpringTransactionAnnotationParser解析器,用来解析@Transactional的事务属性事务拦截器:TransactionInterceptor实现了MethodInterceptor接口,该通用拦截会在产生代理对象之前和aop增强合并,最终一起影响到代理对象TransactionInterceptor的invoke方法中invokeWithinTransaction会触发原有业务逻辑调用(增强事务)。
八、Spring启动时初始化数据方法
项目启动时,在spring进行初始化完成后,但是服务此时还不能访问,对数据进行初始化。有三种方法:
1、PostConstruct注解
/**
* spring容器启动后,初始化数据
*/
@Component
public class InitData {
@Autowired
private IUserService userService;
@PostConstruct
public void init() {
userService.getName();
}
}
2、InitializingBean接口
/**
* spring容器启动后,初始化数据
*/
@Component
public class InitServlet implements InitializingBean {
@Autowired
private IUserService userService;
@Override
public void afterPropertiesSet() throws Exception {
userService.getName();
}
}
3、监听ApplicationListener
//交给Spring管理,如果不是自动扫描加载bean的方式,则在xml里配一个即可
@Component
public class InitData implements ApplicationListener<ContextRefreshedEvent> {
@Autowired
private IProductService productService;
@Autowired
private IUserService userService;
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
userService.getName();
}
}
4、总结
执行顺序:初始化时会先调用@PostConstruct注解标注的方法,而后调用实现InitializingBean接口的afterPropertiesSet方法,最后执行ContextRefreshedEvent事件的bean中的onApplicationEvent。建议使用@PostConstruct注解,减少Spring的侵入性以及耦合性。