从Spring源码看创建对象的过程

从Spring源码看创建对象的过程

Spring对于程序员set注入的属性叫做属性的填充、对于set注入之后的处理(包括BeanPostProcessor的处理、初始化方法的处理)叫做初始化。

研读AbstractBeanFactory类中的doGetBean()方法

doGetBean()方法首先完成的工作是获取对象(针对于 scope=singleton 这种形式的对象,Spring把曾经创建获得对象进行存储。后续先获取对象) ,如果获取不到,才会创建对象。

源码图如下:

粗略看创建对象的大体流程

从代码中,我们可以看到一行注释:

java 复制代码
Create bean instance.

这是通过scope进行讨论,以单例情况为例:

  1. 调用AbstractAutowireCapableBeanFactory类中的

    java 复制代码
    protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)方法
  2. createBean方法又会调用本类中的doCreateBean方法;

  3. doCreateBean方法中createBeanInstance就是使用反射进行对象的创建;

  4. populateBean来完成属性填充

    markdown 复制代码
    包括set注入
    和自动注入
        <bean autowired="byName|byType"
        <beans default-autowried 
        @Autowired
  5. 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中进行的。

相关推荐
明月与玄武2 小时前
Spring Boot中的拦截器!
java·spring boot·后端
菲兹园长2 小时前
SpringBoot统一功能处理
java·spring boot·后端
muxue1782 小时前
go语言封装、继承与多态:
开发语言·后端·golang
开心码农1号2 小时前
Go语言中 源文件开头的 // +build 注释的用法
开发语言·后端·golang
北极象2 小时前
Go主要里程碑版本及其新增特性
开发语言·后端·golang
lyrhhhhhhhh3 小时前
Spring框架(1)
java·后端·spring
喝养乐多长不高4 小时前
Spring Web MVC基础理论和使用
java·前端·后端·spring·mvc·springmvc
莫轻言舞5 小时前
SpringBoot整合PDF导出功能
spring boot·后端·pdf
玄武后端技术栈5 小时前
什么是死信队列?死信队列是如何导致的?
后端·rabbitmq·死信队列
老兵发新帖7 小时前
NestJS 框架深度解析
后端·node.js