在前面的文章中,我们把 refresh() 中 obtainFreshBeanFactory、invokeBeanFactoryPostProcessors、方法都梳理了一遍,其中还有一个 registerBeanPostProcessors,但个人觉得实际上和 invokeBeanFactoryPostProcessors类似,所以没有单独写一篇文章去描述的,尽管它非常重要,但是你可以自己去分析一下或者看看这篇文章:registerBeanPostProcessors 详解。
refresh() 中另外一个非常重要的方法就是 finishBeanFactoryInitialization,其中涉及到 Bean 实例化过程(即已经注册的 BeanDefinition)以及 BeanPostProcessor(实现了 BeanPostProcessor 接口的 Bean)的执行。
在 Bean 的实例化之前,再来讲讲 BeanDefinition,前面说过 BeanDefinition 描述一个 Bean 的实例信息,但实际上它是一个接口,给这些信息定义了一些 getter 和 setter 方法,作为接口层它不负责定义具体的参数,AbstractBeanDefinition
对这个接口进行了实现,如下:
这个时候应该会存在一个疑问,就是为什么 Spring 实例化要通过 BeanDefinition,Claas不也是类对象吗?因为Class 是无法完成 bean 的抽象,比如 bean 的作用域,bean 的注入模型,bean 是否是懒加载等等信息,Class是无法抽象出来的,故而需要一个 BeanDefinition 类来抽象这些信息。
AbstractBeanDefinition 是 BeanDefinition 的直接实现类,从类名也知道,它是一个抽象的 BeanDefinition,还不够具体。
AbstractBeanDefinition 已经对 BeanDefinition 方法有了基本实现逻辑,增加了许多新的属性(默认属性)。
从 Spring2.5 开始,RootBeanDefinition仅作为运行时的BeanDefinition视图。如果需要编程定义BeanDefinition,那么推荐使用GenericBeanDefinition。
Spring的解释是:GenericBeanDefinition的优势在于,它允许动态定义父依赖项,而不是一个以"硬编码"定义BeanDefinition的角色。也就是说,bean的一般形式是以GenericBeanDefinition的标准形式存在的,在特定的时机,会将GenericBeanDefinition转成RootBeanDefinition。
这段官方的解释第一次看着实让人摸不着头脑,但我们只需要记住这个 RootBeanDefinition 在实例化一个 Bean 的时候,需要与其他的 BeanDefinition 进行合并,也就是说 BeanDefinition 存在父子关系,对应的属性为parentName
。
而合并的目的就是不同的 BeanDefinition 存在共性,比如这个 Bean 继承了某个类或继承了某个 Bean,亦或是实现了某个接口,而合并过程就是把存在父子关系的 BeanDefinition 的属性合并起来,如果存在相同的属性,则以子Beanfinition的属性为准。
一般情况下,在 bean 的初始化时,会将 BeanDefinition 转换成 RootBeanDefinition。
至于为什么要合并,我觉得可能是为了解析成一个完整的 beandefinition 吧。
栗子
假如有这样一个动物抽象类:
@Data
public abstract class Animal {
private String name;
}
然后定义一个子类 Cat:
@Data
public class Cat extends Animal{
private String name;
}
然后我们的 xml 配置如下:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd"
default-lazy-init="false">
<bean id="animal" class="net.javatv.bean.Animal" abstract="true">
<property name="name" value="animal">property>
bean>
<bean id="cat" class="net.javatv.bean.Cat" parent="animal"/>
beans>
写个测试类:
@Test
public void test3() {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
Cat cat = applicationContext.getBean(Cat.class);
System.out.println(cat.getName());
}
可以看到 cat 的 name 属性也被赋值成了animal:
这种方式常用在一些公共的配置上,如连接池的配置,多数据源的配置等等。而这个合并的过程就在实例化bean 的过程中。
需要注意的是,RootBeanDefinition 可以单独作为一个BeanDefinition,也可以作为其他 BeanDefinition 的父类。但是他不能作为其他 BeanDefinition 的子类,这点在源码中可以很好的体现,在 setParentName 的时候,会抛出一个异常:
ChildBeanDefinition相当于一个子类,不可以单独存在,必须要依赖一个父 BeanDetintion。通过源码发现其最大的区别他的 parentName 属性是通过构造方法设置的,而且并没有提供一个无参构造方法给我们。
从 spring 2.5 开始,提供了一个更好的注册bean definition类
GenericBeanDefinition
,所以以后推荐使用它(这里就不做过多解释了)。
GenericBeanDefinition 同样继承了 AbstractBeanDefinition,其实也就是一个普通的 BeanDefinition ,和另外 2 个不同的是,它有两个比较重要的子类:
@Configuration
注解注释的类;@Component
、@Service
、@Controller
等注解注释的类。
元素的 autowire-candidate属性设置为 false,这样容器在查找自动装配对象时,将不考虑该 bean,即它不会被考虑作为其它 bean 自动装配的候选者,但是该 bean 本身还是可以使用自动装配来注入其它 bean 的。
标签的信息,其实类里面就是有一个 list,list里面是 PropertyValue 对象,PropertyValue 就是一个 name 和 value 属性,用于封装
标签的名称和值信息。
标签的信息,其实类里面就是有一个 map,map 中用构造函数的参数顺序作为 key,值作为 value 存储到 map 中。AbstractApplicationContext#finishBeanFactoryInitialization()
该方法从名字上就可以知道是用来完成 Bean 工厂初始化的,即 Bean 的实例化,我们进入该方法,主要是看AbstractApplicationContext#preInstantiateSingletons()
进入 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.
//xml解析时,讲过,把所有beanName都缓存到beanDefinitionNames了
List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
// Trigger initialization of all non-lazy singleton beans...
for (String beanName : beanNames) {
//把父BeanDefinition里面的属性拿到子BeanDefinition中,也就是上面讲的BeanDefinition合并
//该方法可自行具体研究,这里暂不讨论
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
//如果不是抽象的,单例的,非懒加载的就实例化
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
//判断bean是否实现了 FactoryBean 接口,这里可以先不看
if (isFactoryBean(beanName)) {
Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
if (bean instanceof FactoryBean) {
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 {
//主要看实例化过程,如果不是FactoryBean,则及时普通的Bean
getBean(beanName);
}
}
}
// Trigger post-initialization callback for all applicable beans...
for (String beanName : beanNames) {
Object singletonInstance = getSingleton(beanName);
if (singletonInstance instanceof SmartInitializingSingleton) {
SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
smartSingleton.afterSingletonsInstantiated();
return null;
}, getAccessControlContext());
} else {
smartSingleton.afterSingletonsInstantiated();
}
}
}
}
按照上面的分析过程,进入AbstractBeanFactory#getBean()
:
public Object getBean(String name) throws BeansException {
return doGetBean(name, null, null, false);
}
可以看到调用了doGetBean
方法,在 Spring 中一般涉及到 doXxx
就是真正干实事的方法,而该方法也不是说从头看到底,我们找到最关键的代码出,如下:
为什么先看这里,在我们学习 Spring 的时候,尽管没看过源码我们也知道 Spring 创建实例默认是单例的,也就是说大多数情况下都是单例 Bean。
DefaultSingletonBeanRegistry#getSingleton()
初次实例化 bean 通过 getSingleton 方法:
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "Bean name must not be null");
synchronized (this.singletonObjects) {
// 从 一级缓存 中拿取实例,如果存在直接返回,或者创建 bean
// 常见的就是容器创建的时候创建实例,然后如果通过@Autowired注入,则会在次调用getBean,从缓存中拿取实例
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
if (this.singletonsCurrentlyInDestruction) {
throw new BeanCreationNotAllowedException(beanName,
"Singleton bean creation not allowed while singletons of this factory are in destruction " +
"(Do not request a bean from a BeanFactory in a destroy method implementation!)");
}
if (logger.isDebugEnabled()) {
logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
}
/**
* 把 beanName 添加到 singletonsCurrentlyInCreation Set 容器中,在这个集合里面的 bean 都是正在实例化的
* 实际就是实例化还没完成的 BeanName
*/
beforeSingletonCreation(beanName);
boolean newSingleton = false;
boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
if (recordSuppressedExceptions) {
this.suppressedExceptions = new LinkedHashSet<>();
}
try {
// 调到 getObject 方法,完成 bean 的实例化,即调用createBean()方法
singletonObject = singletonFactory.getObject();
newSingleton = true;
} catch (IllegalStateException ex) {
// Has the singleton object implicitly appeared in the meantime ->
// if yes, proceed with it since the exception indicates that state.
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
throw ex;
}
} catch (BeanCreationException ex) {
if (recordSuppressedExceptions) {
for (Exception suppressedException : this.suppressedExceptions) {
ex.addRelatedCause(suppressedException);
}
}
throw ex;
} finally {
if (recordSuppressedExceptions) {
this.suppressedExceptions = null;
}
afterSingletonCreation(beanName);
}
if (newSingleton) {
/**
* 到这里,Bean已经完成实例化,然后要做 2 件事
* 1、singletonsCurrentlyInCreation 把 beanName 从这个集合中删除
* 2、addSingleton,把 bean 缓存到一级缓存中
*/
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}
这里存在 2 种情况,第一是可以直接从缓存中拿取实例,第二种就是需要创建实例,调用getObject()
,我们先看第二种情况,调用链路往下为AbstractAutowireCapableBeanFactory#createBean()
。
在该方法中,主要看doCreateBean()
:
可以看到,这里出现了一个新的接口BeanWrapper
,其实就是一个 Bean 的包装器,包括对 Bean 的属性、方法,数据等,同时它还具备属性转换的能力,因为它还得是一个类型转换器,它有唯一的一个实现类是BeanWrapperImpl
,具体的可以看看这篇文章:Spring中的BeanWrapper及类型转换。
那么他和 BeanDefinition有什么关系呢?
BeanDefinition(原料)->BeanFactory(工厂)->BeanWrapper(产品)
然后,进入 Bean 实例化核心方法AbstractAutowireCapableBeanFactory#createBeanInstance()
,并且包装成 BeanWrapper 对象。
在进入createBeanInstance()
方法之前,先来讲讲在实例化过程中的几种情况:
在 BeanDefinition 有 factory-method 属性的会实例化。在源码中体现如下:
if (mbd.getFactoryMethodName() != null) {
return instantiateUsingFactoryMethod(beanName, mbd, args);
}
举个栗子
新建 2 个类,并且不添加 @Conponent 注解:
FactoryMethodBean
public class FactoryMethodBean {
public Object method() {
return new Handsome();
}
}
Handsome
import lombok.Data;
@Data
public class Handsome {
private String name = "ayue";
}
xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd"
default-lazy-init="false">
<bean id="factoryMethodBean" class="net.javatv.bean.FactoryMethodBean"/>
<bean id="handsome" factory-bean="factoryMethodBean" factory-method="method"/>
beans>
测试类
@Test
public void test4() {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
Handsome man = applicationContext.getBean(Handsome.class);
System.out.println(man.getName());
}
根据断点定位可以看到:
也就是说 Handsome 类也被实例化了,底层源码就是通过反射来创建实例,这里就不贴源码了,可自行分析。
其中需要注意的是,factoryMethodName 除了上面的方式,还有一种就是类的静态方法也是可以实例化的,如下:
import lombok.Data;
@Data
public class Handsome {
private String name = "ayue";
public static Object method() {
return new Handsome();
}
}
另外,Spring 会扫描有@Bean
注解的方法,然后把方法名称设置到 BeanDefinition 的 factoryMethod 属性中,然后就会调到上面的方法实现 @Bean
方法的调用,@Bean
的具体流程后续在分析。
如果不是 factoryMethodName ,则进入下一个方法determineConstructorsFromBeanPostProcessors
,这 个 方法是 BeanPostProcessor 接口类的应用,最终会调到 AutowiredAnnotationBeanPostProcessor
类的方法, 在方法中会扫描有注解的构造函数然后完成装配过程,然后把有有@Autowired
注解的构造函数返回。
举个栗子
实例 A
import lombok.Data;
import org.springframework.stereotype.Component;
@Data
@Component
public class A {
private String name = "a";
}
实例 B
import lombok.Data;
import org.springframework.stereotype.Component;
@Data
@Component
public class B {
private String name = "b";
}
@Autowired 有参构造函数
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class AutowiredConstructBean {
//只有一个构造函数的情况
@Autowired
public AutowiredConstructBean(A a, B b) {
System.out.println(a.getName());
System.out.println(b.getName());
}
}
测试结果:
可以看到 A 和 B 都被实例化。
此时有另外一个问题,如果有两个构造函数呢?如下:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class AutowiredConstructBean {
@Autowired
public AutowiredConstructBean(A a, B b) {
System.out.println(a.getName());
System.out.println(b.getName());
}
@Autowired
public AutowiredConstructBean(A a) {
System.out.println(a.getName());
}
}
结果如下:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'autowiredConstructBean': Invalid autowire-marked constructor: public net.javatv.autowired.AutowiredConstructBean(net.javatv.autowired.A). Found constructor with 'required' Autowired annotation already: public net.javatv.autowired.AutowiredConstructBean(net.javatv.autowired.A,net.javatv.autowired.B)
意思是已经存在了构造器,所以直接抛出异常了。
我们可进入源码分析AbstractAutowireCapableBeanFactory#determineConstructorsFromBeanPostProcessors()
protected Constructor<?>[] determineConstructorsFromBeanPostProcessors(@Nullable Class<?> beanClass, String beanName)
throws BeansException {
if (beanClass != null && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
Constructor<?>[] ctors = ibp.determineCandidateConstructors(beanClass, beanName);
if (ctors != null) {
return ctors;
}
}
}
}
return null;
}
然后进入AutowiredAnnotationBeanPostProcessor#determineCandidateConstructors()
方法:
1、其中第一个 if 代码块我们可以忽略,即 @Lookup
方法注入,因为不怎么使用了,如果你想了解,可以看这篇文章:Spring中的Lookup
2、获取有参构造函数
从代码 debug 也能看到:
3、判断构造函数是否有 @Autowired 注解
此时,涉及到多个构造函数的情况,如果只有一个且@Autowired 的 required 属性为 true,则正常执行。如果存在多个构造函数且required=true
,就会进入requiredConstructor != null
为 true,抛出异常,即上面代码演示的情况。
因此,如果要解决多个构造函数不抛异常则需要设置required=false
,此时该段代码并不会执行:
代码演示如下:
@Component
public class AutowiredConstructBean {
@Autowired(required = false)
public AutowiredConstructBean(A a, B b) {
System.out.println(a.getName());
System.out.println(b.getName());
}
@Autowired(required = false)
public AutowiredConstructBean(A a) {
System.out.println("第 2 个构造函数" + a.getName());
}
}
再去看看结果:
从结果可以看出,有 2 个参数的构造函数打印出了结果,而 1 个参数的并没有打印出来。这是为什么呢?
这是因为 Spring 做了一个排序操作并且取了参数最多的构造函数来实例化,具体方法在ConstructorResolver#autowireConstructor
中的这段代码:
AutowireUtils.sortConstructors(candidates);
同时,在拿到构造函数以后,通过 autowireConstructor()
进行参数的解析和实例化,这个过程相对来说比较复杂,我们先知道大致流程即可,但最后实例化的时候仍然调用的是 getBean()
方法来实例化的。
实际上,不管是 Field、Method、还是构造函数中有@Autowired 注解引入的类,都是通过 getBean()
方法进行实例化的。
对于没有 @Autowired 注解的有参构造函数,当只有一个有参构造函数的时候正常执行,但有 2 个的时候同样会报错,但不同于上述错误。
代码演示:
@Component
public class AutowiredConstructBean {
// 去掉@Autowired注解
public AutowiredConstructBean(A a, B b) {
System.out.println(a.getName());
System.out.println(b.getName());
}
public AutowiredConstructBean(A a) {
System.out.println("第 2 个构造函数" + a.getName());
}
}
错误显示找不到默认的构造函数:
为什么会出现这种情况,通过源码分析,经过一系列的判断之后走向了如下方法:
即最后返回了空的构造器,所以抛出异常,那么怎么解决呢?
其实只要添加一个无参构造器就行了。
@Component
public class AutowiredConstructBean {
public AutowiredConstructBean() {
System.out.println("添加无参构造函数");
}
public AutowiredConstructBean(A a, B b) {
System.out.println(a.getName());
System.out.println(b.getName());
}
public AutowiredConstructBean(A a) {
System.out.println("第 2 个构造函数" + a.getName());
}
}
这也说明了一个问题,就是在 Spring 中,如果你的实体类是有参的构造函数,那么必须添加一个无参的构造函数。如果没有那就是默认的无参构造函数,也就是最常见的第 4 种情况。
无参构造函数实例化,就是AbstractAutowireCapableBeanFactory#instantiateBean()
,其实现就是简单的反射实现,并且大部分情况都是通过该种方式实现。
在实例化完成之后,需要对类中的属性进行依赖注入操作,对于什么是IOC,什么是DI,似乎不应该在这里讲,不管是面试还是学习 IOC 和 DI 都是耳熟能详的,但可能还是有人对两个概念是模糊不清,所以这里总结一下。
首先说说 IOC,中文翻译为控制反转,它并不是一种技术,而是一种思想,那么什么叫反转?有反转那就有正转。
举个栗子
好比我们吃饭,常规情况下我们需要先去买菜,然后还需要准备调料,厨具等,然后还需要自己来炒菜,这一整个过程相当复杂,并且一旦其中缺了一个环节都不能达到目标,类比到对象中就是如果对象 A 需要对象 B,那么在A中我们就需要通过 new A()
的方式来获取实例 B,然后使用完之后还需要将对象销毁,比如数据库连接等,耦合度非常高,这种方式可以理解为正转。
而 IOC 的做法则不同,照样是吃饭,但是我们可以在外卖APP上点外卖,APP上有很多可以供我们选择,而我要做的就是我需要吃什么,外卖员送过来即可,如果送错了,那就抛出异常即可,整个做饭的过程都不需要我们来控制,这里的外卖APP就类似一个容器。
Spring 所倡导的开发方式就是如此,所有的类都会在 Spring 容器中登记,告诉 Spring 你是个什么东西,你需要什么东西,然后 Spring 会在系统运行到适当的时候,把你要的东西主动给你,同时也把你交给其他需要你的东西。所有的类的创建、销毁都由 Spring 来控制,也就是说控制对象生存周期的不再是引用它的对象,而是 Spring。对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被 Spring 控制,所以这叫控制反转。
DI,Dependency Injection,即依赖注入,我把他理解为是对 IOC 的实现,按照依赖和注入我们把它拆分一下:
谁依赖谁
应用程序依赖于 IOC 容器,这里的应用程序可以是一个类,也可以是一整个项目;
为什么需要依赖
应用程序需要依靠 IOC 容器来提供外部所需要的资源(对象等);
谁注入谁
IOC 容器注入了某个需要被依赖的资源(对象等);
注入了什么
容器外部所需要的资源(对象等)。
举个栗子
对象 A 需要操作数据库,以前我们总是要在 A 中自己编写代码来获得一个 Connection 连接对象,有了 Spring 我们就只需要告诉 Spring,A 中需要一个 Connection,至于这个 Connection 怎么构造,何时构造,A 不需要知道。在系统运行时,Spring 会在适当的时候制造一个 Connection,然后像打针一样,注射到 A 当中,这样就完成了对各个对象之间关系的控制。A需要依赖 Connection才能正常运行,而这个 Connection是由 Spring注入到A中的,依赖注入的名字就这么来的。那么 DI 是如何实现的呢? Java 1.3之后一个重要特征是反射(reflection),它允许程序在运行的时候动态的生成对象、执行对象的方法、改变对象的属性,Spring 就是通过反射来实现注入的。
而在 Spring 中常见的对类的属性或方法的注入方式就是**@Autowired或者@Resource**注解,因此,在反射之前,需要先进行对类中的注解的收集。
关于对 @Autowired 或者 @Resource注解的收集,我们回到 3.3 的doCreateBean()
方法中,在实例化之后,通过调用applyMergedBeanDefinitionPostProcessors()
方法来完成注入:
进入该方法可以发现,实际上是通过对 BeanPostProcessor 接口来调用的如下:
其中可以看到有 2 个重要的实现类CommonAnnotationBeanPostProcessor
和AutowiredAnnotationBeanPostProcessor
。
CommonAnnotationBeanPostProcessor
这个类完成了对@Resource
注解的属性或者方法的收集,此外,这个类还对@PostConstruct
和@PreDestory
支持。
其构造方法如下:
这也就是为什么我们在项目中可能会看到被@PostConstruct
注解标记的方法,因为会在实例化的时候加载注入。
进入postProcessMergedBeanDefinition()
,具体的收集过程如下:
1、查看缓存里面有没有 InjectionMetadata 对象;
2、循环遍历类中的所有的方法,判断方法上是否有@PostConstruct注解和@PreDestroy 注解,然后通过反射调用,如下图所示:
1、看缓存里面有没有 InjectionMetadata 对象 。
2、从类中获取所有 Field 对象,循环 field 对象,判断 field 有没有@Resource 注解,如果有注解封装成 ResourceElement 对象。
3、最终把两个 field 和 Method 封装的对象集合封装到 InjectionMetadata 对象中。
AutowiredAnnotationBeanPostProcessor
类是对 @Autowired 和 @Value 注解的属性和方法的收集,构造方法如下:
其方法 findAutowiringMetadata()
是对注解进行解析:
收集过程同@Resource,也是收集 Field 和 Method 上的注解,然后放到 InjectionMetadata 对象中。
Bean 实例化完成、并且收集完@Resource 和@Autowired 注解以后就开始依赖注入,还是在doCreateBean()
方法中:
进入AbstractAutowireCapableBeanFactory#populateBean()
方法。
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
if (bw == null) {
if (mbd.hasPropertyValues()) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");
}
else {
// Skip property population phase for null instance.
return;
}
}
// Give any InstantiationAwareBeanPostProcessors the opportunity to modify the
// state of the bean before properties are set. This can be used, for example,
// to support styles of field injection.
/**
* 属性填充判断:这里调用了 InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation方法
* 给InstantiationAwareBeanPostProcessor最后一次机会在属性设置前来改变bean
* 注意:如果实现这个类并且在postProcessAfterInstantiation()返回 false 可以导致其他实例无法注入
*/
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
return;
}
}
}
}
PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);
int resolvedAutowireMode = mbd.getResolvedAutowireMode();
// 自动装配:根据名称或类型自动注入,从Spring2.5开始,开始支持使用注解(@Autowired)来自动装配Bean的属性,所以这里不分析
if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
// Add property values based on autowire by name if applicable.
if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
// 名称注入
autowireByName(beanName, mbd, bw, newPvs);
}
// Add property values based on autowire by type if applicable.
if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
// 类型注入
autowireByType(beanName, mbd, bw, newPvs);
}
pvs = newPvs;
}
boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
// 依赖检查
boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);
PropertyDescriptor[] filteredPds = null;
if (hasInstAwareBpps) {
if (pvs == null) {
pvs = mbd.getPropertyValues();
}
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
// 依赖注入过程
PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {
if (filteredPds == null) {
filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
}
// 老版本用这个方式去注入
pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {
return;
}
}
pvs = pvsToUse;
}
}
}
if (needsDepCheck) {
if (filteredPds == null) {
filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
}
// 依赖检查,对应 depends-on 属性,3.0 已弃用,这里不在分析
checkDependencies(beanName, mbd, filteredPds, pvs);
}
if (pvs != null) {
// 将属性应用到bean中,这种一般是 XMl properties的方式,现在基本没有使用,所以不在分析
applyPropertyValues(beanName, mbd, bw, pvs);
}
}
以@Autowired
为例,当注入了某个类时,如下:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class AutowiredConstructBean {
@Autowired
private A a;
public String init(){
return a.getName();
}
}
测试类:
@Test
public void test4() {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
AutowiredConstructBean bean = applicationContext.getBean(AutowiredConstructBean.class);
System.out.println(bean.init());
}
输出:
进入 populateBean()
源码,先看这段代码:
它会遍历 InstantiationAwareBeanPostProcessor 类型的接口进行注入,而在上面注解的收集讲到了它的实现类之一就是AutowiredAnnotationBeanPostProcessor
,所以当存在@Autowired
注入时,则进入AutowiredAnnotationBeanPostProcessor#postProcessProperties()
方法。
进入该方法,首先会收集 Bean 中是否存在@Autowired
注解,并放到 InjectedElement 中:
如上面例子中的 AutowiredConstructBean 中注入了 A,通过debug可以看到:
收集到注入对象之后开始注入,进入inject()
方法:
debug 上面的例子可以很明显的看到:
然后在进入element.inject
循环注入,我们再看看该方法:
需要注意的是,上面方法中的 target 是为 null 的,是因为在该方法中可以看到注入的方式有两种,字段和方法,所以它有 2 个具体的实现方法,如下:
所以我们这里看字段注入,即内部类 AutowiredFieldElement 中的 inject 方法:
该方法简单来说就是拿到需要注入的实例对象,完成注入即可,而对象的获取就是通过 getBean 方法来获取的,在单例模式下,一般来说容器初始化的时候会创建实例,然后通过 @Autowried 注入时,会直接从缓存中获取。
在执行完Bean的创建和IOC的依赖注入之后,然后就进入doCreateBean()
中的 initializeBean()
,初始化 Bean。
首先是对 Aware 接口的调用,Aware 是一个空扩展接口,具体的接口定义由子类实现,通常用于属性设置,如 BeanNameAware、BeanClassLoaderAware、BeanFactoryAware。功能的实现主要在invokeAwareMethods方法中。
当Bean实现了BeanNameAware,BeanClassLoaderAware,BeanFactoryAware三个接口时,就会在Bean初始化的时候去调用对应的set方法,设置对应的属性。具体的扩展暂时没有,所以可以先了解。
另外还有一个最常见的实现类 ApplicationContextAware,也是我们在项目中经常使用到的,也就是Spring上下文,我们可以用它来获取我们需要的 Bean,如下:
@Component
public class SpringContextHolder implements ApplicationContextAware {
private static ApplicationContext appContext = null;
/**
* 通过name获取 Bean.
*
* @param name
* @return
*/
public static Object getBean(String name) {
return appContext.getBean(name);
}
/**
* 通过class获取Bean.
*
* @param clazz
* @param
* @return
*/
public static <T> T getBean(Class<T> clazz) {
return appContext.getBean(clazz);
}
/**
* 通过name,以及Clazz返回指定的Bean
*
* @param name
* @param clazz
* @param
* @return
*/
public static <T> T getBean(String name, Class<T> clazz) {
return appContext.getBean(name, clazz);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if (appContext == null) {
appContext = applicationContext;
}
}
}
而对于 ApplicationContextAware 中的 ApplicationContext 的赋值就在 applyBeanPostProcessorsBeforeInitialization 中。
在 Bean 初始化前,调用所有 BeanPostProcessors 的 postProcessBeforeInitialization 方法,在 refresh() 中会进行BeanPostProcessors 的注册,即registerBeanPostProcessors,而 BeanPostProcessors 中有 2 个重要的方法。
1、ApplicationContextAwareProcessor,它是 BeanPostProcessors 的一个重要实现类,它在postProcessBeforeInitialization 中对 ApplicationContext 进行了赋值,代码如下:
进入 invokeAwareInterfaces:
很明显可以看到和 initializeBean 中的 invokeAwareMethods 一致。
2、InitDestroyAnnotationBeanPostProcessor,这个实现类主要就是对 @PostConstruct 注解方法的调用,上面讲的是收集,而这里就是具体的调用。
3、ImportAwareBeanPostProcessor,该方法是对 ImportAware 实例进行调用,在 Spring Boot 中,我们经常看到@import
就和它有关,这里就不做过多描述,感兴趣可自行研究。
该方法初始化 Bean 主要有两种情况:
1、Bean 实现了 InitializingBean 接口,则这个 Bean 在初始化的时候会调用 afterPropertiesSet()
方法,你也可以自己实现它,然后做自己想做的事,比如配置文件的解析,缓存预热,缓存数据加载到内存等等。
2、init-method 标签直接注入bean,如下面的例子:
public class InitMethodBean {
public void init(){
System.out.println("init-method初始化...");
}
}
xml 配置
<bean id="initMethodBean" class="net.javatv.autowired.InitMethodBean" init-method="init"/>
测试结果
需要注意的是,Spring 虽然可以通过 InitializingBean
完成 Bean 初始化后对这个 Bean 的回调,但是这种方式要求Bean实现InitializingBean接口。一但Bean实现了InitializingBean接口,那么这个Bean的代码就和Spring耦合到一起了。而 init-method
并不依赖于Spring的某个接口,但它是经过反射来执行的,效率上低于InitializingBean
。
对于init-method
,Spring 要求它是一个无参的方法,如果init-method
指定的方法中有参数,那么 Spring 将会抛出异常,如下:
另外,init-method
指定的方法可以是声明的抛出异常,如下:
public class InitMethodBean {
public void init() throws Exception {
System.out.println("init-method初始化...");
if (true) {
throw new Exception("init exception...");
}
}
}
afterPropertiesSet
和 init-method
和有@PostConstruct
注解的方法其实核心功能都是一样的,都是在该类实例化和 IOC 做完后调用的,只是调用时序不一样,从上面的代码分析可以知道他们的调用时序为:
@PostConstruc > afterPropertiesSet > init-method
本文主要讲的是 Bean 的实例化,到 Bean 的依赖注入,再到 Bean 的初始化三个过程,还有 BeanPostProcessor 以及 Aware 接口的扩展,当然,其中还有很多没分析到,如 getBean() 方法中的细节,FactoryBean的创建,循环依赖等等,但大致流程可以明确,后续在对具体的细节进行补充。