Spring容器之ClassPathXmlApplicationContext

本文参考了
https://javadoop.com/post/spring-ioc

该文是基于Spring 4.3.9.release版本展开的

对于Java开发者来说,Spring绝对不会陌生,那么其中Spring容器的初始化过程是怎样的呢?该文简要跟踪了ClassPathXmlApplicationContext初始化的过程,现与大家分享。

首先给个ClassPathXmlApplicationContext使用的小例子,直观感受一下。

定义如下测试Bean:

public class TestBean implements InitializingBean, DisposableBean, BeanNameAware, BeanFactoryAware{
    /**
     * @Value和@Autoware是由AutowiredAnnotationBeanPostProcessor处理的;
     * 如果程序中不引入AutowiredAnnotationBeanPostProcessor,则无法解析
     * */
    //在properties文件中配置了username=test
    @Value("${username}")
    private String userName;

    private Integer age;

    public void setUserName(String userName){
        System.out.println("set 方法被调用.....");
        this.userName = userName;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getUserName(){
        return this.userName;
    }

    public TestBean(){
        System.out.println("Constructor : 构造器被调用..... | username: "+userName+" | age: "+age);
    }

    public void init(){
        System.out.println("XML : xml文件中init-method指定的方法init被调用..... | username: "+userName+" | age: "+age);
    }

    public void destroyInXml(){
        System.out.println("XML : xml文件中destory-method指定的方法destory被调用..... | username: "+userName+" | age: "+age);

    }
    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        System.out.println("Aware : BeanFactoryAware.setBeanFactory被调用..... | username: "+userName+" | age: "+age);
    }

    @Override
    public void setBeanName(String name) {
        System.out.println("Aware : BeanNameAware.setBeanName被调用..... | username: "+userName+" | age: "+age);
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("DisposableBean : destroy()被调用..... | username: "+userName+" | age: "+age);

    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("InitializingBean : afterPropertiesSet()被调用..... | username: "+userName+" | age: "+age);
    }

    /**
     * 默认情况下,Spring不认识@PostConstruct和@PreDestroy注解。
     * 要启用这两个注解,要么注册CommonAnnotationBeanPostProcessor,要么添加配置<context:annotation-config />
     * */
    @PostConstruct
    public void postConstruct(){
        System.out.println("@PostConstruct : 被调用..... | username: "+userName+" | age: "+age);
    }

    @PreDestroy
    public void preDestroy(){
        System.out.println("@PreDestroy : 被调用..... | username: "+userName+" | age: "+age);

    }

    public String toString(){
        System.out.println(this);
        System.out.println("toString()..... | userName : "+userName+" | age: "+age);
        return "";
    }
}

bean的配置如下:

<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"
       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"
       default-lazy-init="false">
    <!--context标签由ContextNamespaceHandler处理,同理,别的标签由相应的XXXNamespaceHandler处理-->
    <context:property-placeholder location="classpath*:config/*.properties"/>
    <context:annotation-config />
    <bean id="myBeanPostProcessor" class="init.MyBeanPostProcessor" />
    <bean id="myInstantiationPostProcessor" class="init.MyInstantiationPostProcessor" />
    <bean id="myBeanFactoryPostProcessor" class="init.MyBeanFactoryPostProcessor" />

    <bean id="testBean" class="init.TestBean" init-method="init" destroy-method="destroyInXml" >
        <property name="age" value="20"/>
    </bean>
</beans>

自定义的三个后处理器

public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        BeanDefinition testBean = beanFactory.getBeanDefinition("testBean");
        System.out.println("BeanFactoryPostProcessor : postProcessBeanFactory被调用.....");
    }
}
public class MyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof TestBean)
            System.out.println("BeanPostProcessor : postProcessBeforeInitialization被调用.....| username : "+((TestBean) bean).getUserName()+" | age: "+((TestBean) bean).getAge());
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof TestBean)
            System.out.println("BeanPostProcessor : postProcessAfterInitialization被调用..... | username : "+((TestBean) bean).getUserName()+" | age: "+((TestBean) bean).getAge());
        return bean;
    }
}
public class MyInstantiationPostProcessor extends AutowiredAnnotationBeanPostProcessor implements InstantiationAwareBeanPostProcessor {
    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        if (beanName.equals("testBean"))
            System.out.println("InstantiationAwareBeanPostProcessor : postProcessBeforeInstantiation.....");

        return null;
    }

    @Override
    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        if (beanName.equals("testBean"))
            System.out.println("InstantiationAwareBeanPostProcessor : postProcessAfterInstantiation.....| username : "+((TestBean) bean).getUserName() +" | age: "+((TestBean) bean).getAge());

        return true;
    }

    @Override
    public PropertyValues postProcessPropertyValues(
            PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName)
            throws BeansException {
        if (bean instanceof TestBean)
            System.out.println("InstantiationAwareBeanPostProcessor : postProcessPropertyValues.....| username : "+((TestBean) bean).getUserName() +" | age: "+((TestBean) bean).getAge());

        return pvs;
    }
}

启动Spring容器:

public class Main {
    public static void main(String[] args){
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath*:bean.xml");
        TestBean testBean = (TestBean) context.getBean("testBean");
        //context.getBeanFactory().destroySingletons();
    }
}

运行结果如下:
BeanFactoryPostProcessor : postProcessBeanFactory被调用.....
InstantiationAwareBeanPostProcessor : postProcessBeforeInstantiation.....
Constructor : 构造器被调用..... | username: null | age: null
InstantiationAwareBeanPostProcessor : postProcessAfterInstantiation.....| username : null | age: null
InstantiationAwareBeanPostProcessor : postProcessPropertyValues.....| username : test | age: null
Aware : BeanNameAware.setBeanName被调用..... | username: test | age: 20
Aware : BeanFactoryAware.setBeanFactory被调用..... | username: test | age: 20
BeanPostProcessor : postProcessBeforeInitialization被调用.....| username : test | age: 20
@PostConstruct : 被调用..... | username: test | age: 20
InitializingBean : afterPropertiesSet()被调用..... | username: test | age: 20
XML : xml文件中init-method指定的方法init被调用..... | username: test | age: 20
BeanPostProcessor : postProcessAfterInitialization被调用..... | username : test | age: 20

Bean的初始化过程会调用很多PostProcessor,那么顺序是怎样的呢?我们从ClassPathXmlApplicationContext的启动过程分析一下。


ClassPathXmlApplicationContext-StartUp.png

从上图可看出,ClassPathXmlApplicationContext的启动过程中,有两个步骤是比较重要的,obtainFreshBeanFactory()和finishBeanFactoryInitialization();前一个方法负责读取xml文件,并将这些信息转化为BeanDefinition保存在BeanFactory的map中,后一个方法负责创建(Instantiation)并初始化(Initialization)bean。

在此,简要分析一下后一个方法的主要部分。该方法最后会进入到AbstractAutowireCapableBeanFactorydoCreateBean()中。

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
            throws BeanCreationException {

        // 省略不重要的代码
        if (instanceWrapper == null) {
            //运用反射创建bean,此时bean中的属性都没有赋值
            instanceWrapper = createBeanInstance(beanName, mbd, args);
        }

        ......
              
        try {
            //调用一些PostProcessor方法
            populateBean(beanName, mbd, instanceWrapper);
            if (exposedObject != null) {
                 //属性赋值
                exposedObject = initializeBean(beanName, exposedObject, mbd);
            }
        }

        ......

        // Register bean as disposable.
        try {
            registerDisposableBeanIfNecessary(beanName, bean, mbd);
        }
        ......

        return exposedObject;
    }
    protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) {
        PropertyValues pvs = mbd.getPropertyValues();
        ......

        // 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.
        boolean continueWithPropertyPopulation = true;

        if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
            for (BeanPostProcessor bp : getBeanPostProcessors()) {
                if (bp instanceof InstantiationAwareBeanPostProcessor) {
                    InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
                    if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
                        //子类覆写该方法时,不要返回false,否则代码就会跳出
                        continueWithPropertyPopulation = false;
                        break;
                    }
                }
            }
        }

        if (!continueWithPropertyPopulation) {
            return;
        }
        ......

        boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
        boolean needsDepCheck = (mbd.getDependencyCheck() != RootBeanDefinition.DEPENDENCY_CHECK_NONE);

        if (hasInstAwareBpps || needsDepCheck) {
            PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
            if (hasInstAwareBpps) {
                for (BeanPostProcessor bp : getBeanPostProcessors()) {
                    if (bp instanceof InstantiationAwareBeanPostProcessor) {
                        InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
                        //这里会使用AutowiredAnnotationBeanPostProcessor对@Autowire和@Value修饰的属性赋值
                        pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
                        if (pvs == null) {
                            return;
                        }
                    }
                }
            }
            if (needsDepCheck) {
                checkDependencies(beanName, mbd, filteredPds, pvs);
            }
        }
        //最后对一般的属性赋值,赋值时又涉及到字段类型的转换问题,后续再分析
        applyPropertyValues(beanName, mbd, bw, pvs);
    }

protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {
        if (System.getSecurityManager() != null) {
            AccessController.doPrivileged(new PrivilegedAction<Object>() {
                @Override
                public Object run() {
                    //调用Aware类的方法,比如BeanNameAware
                    invokeAwareMethods(beanName, bean);
                    return null;
                }
            }, getAccessControlContext());
        }
        else {
            //调用Aware类的方法,比如BeanNameAware
            invokeAwareMethods(beanName, bean);
        }

        Object wrappedBean = bean;
        if (mbd == null || !mbd.isSynthetic()) {
            //调用BeanPostProcessor的postProcessBeforeInitialization()
            wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
        }

        try {
            //调用afterPropertiesSet()和init-method方法
            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()) {
            //调用BeanPostProcessor的postProcessAfterInitialization()
            wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
        }
        return wrappedBean;
    }

从上述代码的分析过程,就可以看出前面例子的结果的顺序为何是这样。

补充几点说明:

  • BeanPostProcessorpostProcessBeforeInitialization()的调用时机是在bean的所有属性赋值之后,而不是之前。这里的before Initialization强调的是init-method这些方法之前;
  • @Value和@Autowire修饰的属性的赋值要在普通属性之前;
  • Spring中的注释都是由BeanPostProcessor处理的,比如@Autowire由AutowiredAnnotationBeanPostProcessor处理,@Required由RequiredAnnotationBeanPostProcessor处理,Java自带的@PostConstruct等由CommonAnnotationBeanPostProcessor处理,这些Processor可以由<context:annotation-config />一键引入,<context:xxx />又由ContextNamespaceHandler解析。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,470评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,393评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,577评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,176评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,189评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,155评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,041评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,903评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,319评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,539评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,703评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,417评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,013评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,664评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,818评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,711评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,601评论 2 353

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,651评论 18 139
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 46,803评论 6 342
  • 什么是Spring Spring是一个开源的Java EE开发框架。Spring框架的核心功能可以应用在任何Jav...
    jemmm阅读 16,461评论 1 133
  • 一、Spring框架 1.1 Spring框架是什么 Spring是一种容器框架,用于配置各个组件(bean)并且...
    Jane_Static阅读 390评论 0 0
  • Spring简介 spring框架由Rod Johnson开发,2004年发布了Spring框架的第一版。Spri...
    qiuqiu_hz阅读 1,077评论 0 15