spring源码阅读-单例bean的创建(IOC相关)精简主干版,不包括BeanPostProcessor等扩展

主要文件内容

版本:5.2.1.RELEASE

pom文件:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>org.example</groupId>
    <artifactId>spring</artifactId>
    <version>1.0-SNAPSHOT</version>
    <name>test</name>
    <description />
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <!--spring版本号,5.0.0RELEASE是2017年9月发布的新版本 -->
        <spring.version>5.2.1.RELEASE</spring.version>
        <!--mybatis版本号,3.4.5是2017年8月发布的新版本 -->
        <mybatis.version>3.5.0</mybatis.version>
        <!--<slf4j.version>1.7.25</slf4j.version> -->
        <slf4j.version>1.7.25</slf4j.version>

    </properties>
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>

        <!--spring核心包——Start -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <!--spring核心包——End -->
        <!--AOP start -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.8.8</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.3</version>
        </dependency>
        <!--AOP end -->
        <!--MyBatis核心包 -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>${mybatis.version}</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus</artifactId>
            <version>3.1.0</version>
        </dependency>
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper</artifactId>
            <version>4.1.6</version>
        </dependency>

        <!--mybatis/spring包 -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>2.0.1</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-typehandlers-jsr310</artifactId>
            <version>1.0.2</version>
        </dependency>



        <!--javaEE版本8.0 -->
        <dependency>
            <groupId>javax</groupId>
            <artifactId>javaee-api</artifactId>
            <version>8.0</version>
            <scope>provided</scope>
        </dependency>
        <!--mysql数据库的jdbc连接包 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.15</version>
        </dependency>
        <!-- 导入dbcp的jar包,用来在applicationContext.xml中配置数据库  -->
        <dependency>
            <groupId>commons-dbcp</groupId>
            <artifactId>commons-dbcp</artifactId>
            <version>1.4</version>
        </dependency>
        <!--jstl标签包 -->
        <dependency>
            <groupId>jstl</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>
        <!--邮件发送 -->
        <dependency>
            <groupId>com.sun.mail</groupId>
            <artifactId>javax.mail</artifactId>
            <version>1.6.1</version>
        </dependency>
        <!--日志文件管理包——logStart -->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.16.0</version>
        </dependency>
        <!--日志文件管理包——logEnd -->
        <!--格式化对象,方便输出日志 -->
        <!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.42</version>
        </dependency>

        <dependency>
            <groupId>org.codehaus.jackson</groupId>
            <artifactId>jackson-mapper-asl</artifactId>
            <version>1.9.13</version>
        </dependency>
        <!--文件上传组件包 -->
        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.3.3</version>
            <exclusions>
                <exclusion>
                    <artifactId>commons-io</artifactId>
                    <groupId>commons-io</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.6</version>
        </dependency>
        <dependency>
            <groupId>commons-codec</groupId>
            <artifactId>commons-codec</artifactId>
            <version>1.11</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.0.11</version>
        </dependency>
        <!--Redis -->
        <!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>2.9.0</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework.data/spring-data-redis -->
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-redis</artifactId>
            <version>1.8.1.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>commons-beanutils</groupId>
            <artifactId>commons-beanutils</artifactId>
            <version>1.9.3</version>
        </dependency>

    </dependencies>
    <build>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.3.2</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

application.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="demo" name="demo" class="test.bean.Demo">
        <property name="arg1" value="arg1str"/>
        <property name="arg2" value="2222"/>
    </bean>
</beans>

项目结构

一个单纯的maven项目
项目结构

main方法:

package test;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import test.bean.Demo;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;

public class Test {

    public static void main(String[] args) throws InvocationTargetException, IllegalAccessException, NoSuchFieldException, IOException {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("application.xml");
        Demo demo = (Demo) ctx.getBean("demo");
        System.out.println("demo::" + demo);
    }
}

一、主要流程模块先放前面,理清思路

1、读取配置文件,初始化beanDefinitionMap

2、实例化-填充值-初始化对象,放入singletonObjects,作为容器中已初始化对象,用于后续获取bean


二、再debug一下,粗看具体涉及到哪些主要的节点方法

模块一、加载并解析beanDefinition

org.springframework.beans.factory.support.DefaultListableBeanFactory#registerBeanDefinition

registerBeanDefinition

调用堆栈:

调用堆栈

解释:

DefaultListableBeanFactory类中有beanDefinitionMap对象,用于存放从application.xml中标签解析出来的,以name属性为key、BeanDefinition列表为value的对象信息。

模块二、实例化-》填充参数-》初始化bean对象

调用堆栈:

调用堆栈

org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean)

在这里插入图片描述
1、方法中,首先会根据传入的bean名称去三级缓存中的singletonObjects(一个存放初始化操作完成了的bean的ConcurrentHashMap)取这个object,而此时未初始化,必然是null。
2、进入179行的第一个if判断。 isSingletonCurrentlyInCreation(beanName)方法中会去singletonsCurrentlyInCreation中找是否有这个beanName,有的话证明是在创建流程中,此时明显并不是,所以判断返回false,整个方法直接return null。


org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean
解释:

图1
图2
1、首先刚进入方法,根据上面的方法解析,去getSingleton方法拿示例对象,肯定是拿不到instance的。
2、图2中297行,获得根据application.xml解析出来的BeanDefinition对象,其中包括了:
在这里插入图片描述
类名、作用域、是否懒加载、成员属性等等。。。
注意,如果类未加载完成,走创建bean逻辑,在此方法后会调用getSingleton方法中的addSingleton方法,将bean放入singletonObjects。
3、进入createBean-》doCreateBean方法

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean

doCreateBean
这个方法中有三步创建bean的关键方法:
createBeanInstance、populateBean、initializeBean
里面的代码比较长,不好贴,用文字描述一下作用:

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBeanInstance

根据作者解释:
Create a new instance for the specified bean, using an appropriate instantiation strategy: factory method, constructor autowiring, or simple instantiation.
即根据合适的实例化策略(制定的工厂方法or默认构造方法or autowire注明的构造方法),根据你指明的beanName,创建一个新的实例
很明显,此处调用的是简单的无参构造方法。

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean

Apply the given property values, resolving any runtime references to other beans in this bean factory. Must use deep copy, so we don’t permanently modify this property.
应用提供的属性值,如果是引用类型的属性,把引用对象用深拷贝的方式应用进来。
这个方法就是用反射填充实例化对象中的成员变量。

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean(java.lang.String, java.lang.Object, org.springframework.beans.factory.support.RootBeanDefinition)

Initialize the given bean instance, applying factory callbacks as well as init methods and bean post processors.
Called from createBean for traditionally defined beans, and from initializeBean for existing bean instances.
初始化给到的实例化对象,应用工厂的回调来当做初始化方法和bean post processors的扩展内容
对于传统定义的bean,从createBean调用;对于现有bean实例,从initializeBean调用
在这里插入图片描述

在这个方法中会调用invokeInitMethods方法,该方法会检查bean是否实现了InitializingBean或者自定义的init方法(application.xml文件中有这个属性),如果有就调用对应的初始化方法,没有就直接返回出去。
1、在其前后有invokeAwareMethods方法,如果对象实现了Aware接口,会进行额外处理
2、applyBeanPostProcessorsBeforeInitialization、applyBeanPostProcessorsAfterInitialization方法对设置了beanPostProcessor的对象进行初始化前后的扩展处理。
这里我们会直接返回填充了值的bean对象,没有任何多余花里胡哨的操作。

上文中有写,由于doGetBean方法中在创建完对象后会将其放入singletonObjects,之后再次从singletonObjects对象中取,就能得到初始化后的bean对象了。


三、主要类相关UML图

BeanFactory

BeanFactory
其中:DefaultListableBeanFactory实现了BeanDefinitionRegistry接口,实现了org.springframework.beans.factory.support.DefaultListableBeanFactory#registerBeanDefinition方法,对BeanDefinition对象进行了处理。


四、注册流程图

整理了一些主要的bean注册方法
注册流程图

五、debug中所有打的断点+断言条件

有些步骤如果没打断点会略过中间的重要节点,所以这边把主要的节点都贴出来,可以参照一下,避免踩坑。断言条件可以根据自身创建的类名称对应修改。

总览

总览

断言条件

断言图1
断言图2
断言图3
断言图4

六、ps

本文中只是走了关于spring中注册application.xml配置文件中各个单例bean对象的主要流程,其中和aware、BeanPostProcessor和循环依赖的内容提到了一嘴,在这里并没有具体体现。主要是debug一遍,对大体流程做个了解,希望大家都能对bean的注册流程有了体感,以后有时间加上这些扩展相关代码的阅读和解析。
如果有不同意见,欢迎相互讨论学习。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值