从Spring源码看创建对象的过程
Spring对于程序员set注入的属性叫做属性的填充、对于set注入之后的处理(包括BeanPostProcessor的处理、初始化方法的处理)叫做初始化。
研读AbstractBeanFactory类中的doGetBean()方法
doGetBean()方法首先完成的工作是获取对象(针对于 scope=singleton 这种形式的对象,Spring把曾经创建获得对象进行存储。后续先获取对象) ,如果获取不到,才会创建对象。
源码图如下:
粗略看创建对象的大体流程
从代码中,我们可以看到一行注释:
java
Create bean instance.
这是通过scope进行讨论,以单例情况为例:
-
调用AbstractAutowireCapableBeanFactory类中的
javaprotected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)方法
-
createBean方法又会调用本类中的doCreateBean方法;
-
doCreateBean方法中createBeanInstance就是使用反射进行对象的创建;
-
populateBean来完成属性填充
markdown包括set注入 和自动注入 <bean autowired="byName|byType" <beans default-autowried @Autowired
-
initializeBean方法进行初始化操作。
详细阅读doGetBean方法
首先来看一下两个参数
java
protected <T> T doGetBean(
String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
throws BeansException
name就是bean的id值;
requiredType指创建bean的类型;
代码详解:
markdown
final String beanName = transformedBeanName(name);
大多数情况下name和beanName是一样的,就是bean的id值;
但是还有两种情况,需要转换;
1.实现FactoryBean接口类bean的id;
比如 beanFactory.getBean("&s") 会把&去掉,剩下s;此时name为&s,beanName被赋值为了s
2.如果配置文件bean为<bean id="user" name="u" class="xxxx" />
beanFactory.getBean("u"),通过别名u来获取bean,会转换成id。
markdown
Object bean;
代表最终创建好的对象
markdown
Object sharedInstance = getSingleton(beanName);
会从DefaultSingletonBeanRegistry 类中的 singletonObjects、earlySingletonObjects 、singletonFactories获取(从这三个中获取,是为了解决循环引用的问题);
1.第一次获取,会获取为null;
2.后续从Spring容器获取,可以获取到:
注意:Spring创建对象对象会有2种状态
1 完全状态: 对象创建完成 属性填充完成 初始化完成 (如果需要代理 AOP,也已经完成)
2 正在创建中: 仅仅有一个简单对象
doGetBean中调用的DefaultSingletonBeanRegistry中getSingleton方法定义如下:
markdown
if(sharedInstance != null && args == null) {
if(logger.isTraceEnabled()) {
if(isSingletonCurrentlyInCreation(beanName)) {------------------------------判断获取到的bean是否在创建中
logger.trace("Returning eagerly cached instance of singleton bean '" + beanName + "' that is not fully initialized yet - a consequence of a circular reference");
} else {
logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
}
}------------------------这些代码是帮Spring开发人员作代码跟踪的
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);分析
markdown
sharedInstance是从getSingleton()方法中获取到的对象,它与返回的bean有什么区别,就是这行代码的主要功能。
applicationContext.xml配置文件中
如果bean的配置为:
<bean id="u" class="xxxx.User"/>
那么获取到的sharedInstance 就等同于 bean;
如果bean的配置为:
<bean id="fb" class="xxxxx.XXXFactoryBean"/>
此时获取到的sharedInstance是XXXFactoryBean类的对象,我们想获取的是XXXFactoryBean的getObject()里的对象,此时getObjectForBeanInstance(sharedInstance, name, beanName, null)返回返回 FactoryBean#getObject();
所以:
getObjectForBeanInstance(sharedInstance, name, beanName, null);
如果是简单对象 bean就是sharedInstance
如果是FactoryBean对象 bean是 FactoryBean#getObject()返回的对象
markdown
if(isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
用来校验:如果已经在创建这个bean了,抛出异常
markdown
BeanFactory parentBeanFactory = getParentBeanFactory();
if(parentBeanFactory != null && !containsBeanDefinition(beanName)) {
// Not found -> check parent.
String nameToLookup = originalBeanName(name);
if(parentBeanFactory instanceof AbstractBeanFactory) {
return((AbstractBeanFactory) parentBeanFactory).doGetBean(nameToLookup, requiredType, args, typeCheckOnly);
} else if(args != null) {
// Delegation to parent with explicit args.
return(T) parentBeanFactory.getBean(nameToLookup, args);
} else if(requiredType != null) {
// No args -> delegate to standard getBean method.
return parentBeanFactory.getBean(nameToLookup, requiredType);
} else {
return(T) parentBeanFactory.getBean(nameToLookup);
}
}
======doGetBean源码结束
用来解决Spring的父子容器问题:
父子容器代码演示
//applicationContext-parents.xml中有配置bean,
<bean id="p" class="com.sjdwz.Product"/>
applicationContext.xml中有配置bean
<bean id="u" class="com.sjdwz.User"/>
@Test
public void test3() {
DefaultListableBeanFactory parent = new DefaultListableBeanFactory();
XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(parent);
xmlBeanDefinitionReader.loadBeanDefinitions(new ClassPathResource("applicationContext-parents.xml"));
DefaultListableBeanFactory child = new DefaultListableBeanFactory(parent);//把父容器传进来,作联系
XmlBeanDefinitionReader xmlBeanDefinitionReader1 = new XmlBeanDefinitionReader(child);
xmlBeanDefinitionReader1.loadBeanDefinitions(new ClassPathResource("applicationContext.xml"));
//一旦使用父子容器 最终父子容器的配置信息 融合
// 如果遇到同名的配置内容 使用子容器中的内容
User u = (User) child.getBean("u");
System.out.println("u = " + u);
Product p = child.getBean("p", Product.class);
System.out.println("p = " + p);
}
注意:如果子容器和父容器有相同类的注入,并且id也相同,会用子容器里的配置。
如果有父容器有bean的配置信息,在子容器中没有,那么实例化父容器对应的bean(递归进行)
markdown
if(!typeCheckOnly) {
markBeanAsCreated(beanName);
}
typeCheckOnly是doGetBean()方法的参数,在调用getBean时,设置的值为false;
typeCheckOnly 标志 false 【默认】
typeCheckOnly=true代表 spring只是对获取类型进行判断而并不是创建对象
beanFactory.getBean("u",User.class)
typeCheckOnly = true:spring是不会创建User对象 判断当前工厂获得或者创建的对象类型是不是User类型
typeCheckOnly=false:spring会创建对象 或者 获得这个对象 返回给调用者
markBeanAsCreated(beanName);标志这个bean时需要常见对象,而不是进行类型检查;
他需要做两件事:
1. 标记这个bean被创建
2. clearMergedBeanDefinition(beanName);
markBeanAsCreated(beanName)源码:
markdown
要想理解markBeanAsCreated中的clearMergedBeanDefinition()概念,要继续往后看doGetBean()源码。
markdown
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
Spring中bean是有继承关系的。
<bean id="p" abstract=true"/>------------如果一个bean abstract被设置为true,那它就是专门被用来继承的抽象bean,作为父bean
<bean id="u" class="xxx.xxx" parent="p"/>------------parent="p" 指定其继承于哪个bean。
进行汇总 汇总成了 RootBeanDefinition
Spring这样设计,是为了将共有的东西进行抽取到父bean中。
mergedBeanDefinition是指父子bean的定义整合到一起------------------汇总成RootBeanDefinition。
这样就可以回答上面的问题了:markBeanAsCreated()中要标记它被创建完成,就意味着它合并过了,所以要进行clearMergedBeanDefinition()操作。
markdown
checkMergedBeanDefinition(mbd, beanName, args);
防止工厂中bean都是抽象bean。
markdown
String[] dependsOn = mbd.getDependsOn();
if(dependsOn != null) {
for(String dep: dependsOn) {
if(isDependent(beanName, dep)) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
}
registerDependentBean(dep, beanName);
try {
getBean(dep);
} catch(NoSuchBeanDefinitionException ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName, "'" + beanName + "' depends on missing bean '" + dep + "'", ex);
}
}
}
这是为了处理depends-on的(现在已经很少在开发中使用了)
<bean depends-on=" "/>
下面就是根据scope进行区分,来创建了:
我们开发中经常使用的是Singleton的scope,所以重点分析它。
markdown
if(mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () - > {
try {
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);
}
这是要给lambda表达式:
首先调用getSingleton()方法
getSingleton()方法内部会调用createBean(beanName, mbd, args)方法(这是核心)
getSingleton()方法具体分析
beforeSingletonCreation(beanName)方法分析
markdown
用来做校验,需要满足如下两个条件:
1.这个bean没有被排除
备注:排除的概念,比如@ComponentScan(excludeFilters=)
比如<context:component-scan base-package="com.sjdwz">
<context:exclude-filter type="" expression="">
</context:component-scan>
用来排除某些bean
2.这个bean此时正在创建中
如果在bean是被创建中的状态,就没必要再创建了,直接在缓存中获取就可以了,这是为了解决循环依赖问题的。
createBean()方法分析
研读doCreateBean()中创建对象的方法
为什么对返回的是instanceWrapper(bean的包装)呢?
为什么要封装属性呢?便于进行类型转换;
markdown
id是Integer类型
比如标签中设置 <property name="id" value="1">
"1"会通过类型转换器转为Integer类型
或者用@PropertySource()中@Value()注解读取propery文件中,也要类型转换器来解决。
类型转换器可以分为以下两种:
1.内置类型转换器,比如:
String转Integer ,String转List,String转数组类型;
2.自定义类型转换器
比如可以自己开发一个String转Date。
类型转换器开发:
1. 使用PropertyEditorSupport,然后注册到 CustomeEditorConfigure 中;这是很早之前流行的做法,现在很少使用了;
2. 实现Converter接口,然后这个bean的名字,必须叫convertionService
markdown
createBeanInstance()就是用反射来创建对象了。
doGetBean()方法中对属性进行填充
markdown
doGetBean()中populateBean(beanName, mbd, instanceWrapper);是对属性进行填充的。
注入分为,set注入,自动注入(bean或beans标签中autowire属性),构造注入
构造注入是使用构造方法来对属性进行填充的,这应该是由createBeanInstance()方法来调用的,所以不会在中populateBean()体现。
populateBean会进行set注入和自动注入。
set注入分为:
<bean id="" class="">
JDK类型 <property name="" value=""/>
自建类型 <property name="" ref=""/>
@Value------JDK类型注入
@Autowired------自建类型注入
@Inject
@Resources
populateBean()方法中hasInstAwareBpps变量来判断是否是注解类型注入;------------------AutowiredAnnotationBeanPostProcessor来处理注解的注入
非注解的属性填充是通过applyPropertyValues(beanName, mbd, bw, pvs);完成的
在注入时还会进行类型转换
如果配置了自定义类型转换器的时候会调用自定义的,否则调用内置类型转换器
注意:不管是<bean >标签进行属性填充、还是注解进行属性填充,当这个类型是自定义类型时,都会继续走BeanFactory#getBean()方法
applyPropertyValues()方法说明:
doGetBean()方法中初始化的操作
markdown
doGetBean()中exposedObject = initializeBean(beanName, exposedObject, mbd);是进行初始化操作的。
问题:属性填充中的BeanPostProcessor和初始化中的BeanPostProcessor有什么区别呢?
属性填充中的BeanPostProcessor,一般情况下指的是Spring自己提供的BeanPostProcessor;
初始化中的BeanPostProcessor,一般情况下指的是程序员提供的BeanPostProcessor。
下面我们再来讨论下创建对象时,为其做AOP代理是如何进行的。
是在初始化中的BeanPostProcessor的after中进行的。