Spring实例化阶段过程介绍

Spring容器在处理实例化的过程一共经历了两个阶段。在第一个阶段,加载配置文件信息,装配BeanDefinition,并进行前置处理之后,当容器通过BeanFactory的getBean()方法,请求某个对象实例的时候,会触发第二阶段,实现Bean的实例化操作。getBean()方法可以在客户端对象中被显式调用也可以是被隐式调用的。
隐式调用的情况主要是有两种情况:

  • BeanFactory在调用getBean()创建的对象依赖于另一个对象的场合。
  • ApplicationContext的实例化过程与BeanFactory一致,不同于BeanFactory的是,在做完第一阶段的工作后,容器会调用全部的注册到容器中的getBean()方法。这点可以从AbstractApplicationContextrefresh()查看

对于显性调用与隐性调用可以参考文章:
Spring xml文件详解第16点

容器在第一次被getBean()调用的过程中。会通过createBean()的方式对对象进行实例化操作。过程如图所示:

在这里插入图片描述

Spring容器对管理的对象进行统一的生命周期管理。

对于getBean()的具体实现逻辑可以在AbstractBeanFactory类中查看到具体的实现
对于createBean()具体实现逻辑可以在其子类AbstractAutowireCapableBeanFactory中查看

Bean的实例化与BeanWrapper

容器在内部实现的时候采用了策略模式,决定以何种方式初始化Bean。一般是两者方式来初始化Bean或者动态生成其子类:

  • 反射方式(jdk动态代理)
  • CGLIB动态字节码方式(CGLIB动态代理)

InstantiationStrategy是实例化策略接口,在容器中提供了两种实现类:

  • SimpleInstantiaStrategy
  • CglibSubclassingInstantiationStrategy

SimpleInstantiationStrategy实现类可以实现简单的对象实例化过程。通过反射的方式实现实例化,但是不支持方法注入的方式将对象实例化。
CglibSubclassingInstantiationStrategy实现类继承了SimpleInstantiationStrategy类。不止可以以反射方式实例化对象还可以使用CGLIB动态字节码生成的功能,该策略可以动态生成类的子类。满足方法注入的实例化需求。可以满足方法注入的需求。

BeanWrapper代码:

        Object bean=Class.forName("com.example.demo.beanwrapper.MyBean").newInstance();
        Object a=Class.forName("com.example.demo.beanwrapper.MyBeanA").newInstance();
        Object b=Class.forName("com.example.demo.beanwrapper.MyBeanB").newInstance();
        
        BeanWrapper wrapper=new BeanWrapperImpl(bean);
        wrapper.setPropertyValue("a",a);
        wrapper.setPropertyValue("b",b);

使用JDK反射的方式操作对象Code对比操作BeanWrapper 而言,需要处理的异常多。

JDK反射代码:

    Class beanClass=bean.getClass();
        Field fa=beanClass.getField("a");
        Field fb=beanClass.getField("b");
        fa.set(bean,a);
        fb.set(bean, b);

spring Aware接口

Spring会检查以Aware为结尾的接口定义。如果是的场合,将Aware接口定义的BeanName注入到当前对象实例。
有以下的几个Aware结尾的接口:

BeanFactory共有的

  • BeanNameAware:将该实例对应的xml中的BeanName设置到当前对象实例
  • BeanClassLoaderAware:将该实例对应的ClassLoader注入给当前对象。默认加载的是ClassUtils类的ClassLoader
  • BeanFactoryAware:将容器自身注入到实例化的对象中

例子:
配置文件

<bean id="beanNameAware" class="com.example.demo.aware.BeanNameAwareDemo"></bean>

类代码:

package com.example.demo.aware;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanNameAware;

public class BeanNameAwareDemo implements BeanNameAware,BeanClassLoaderAware,BeanFactoryAware{
    
    private String beanName;
    
    private BeanFactory factory;
    
    private ClassLoader loader;
    
    @Override
    public void setBeanName(String name) {
        // TODO Auto-generated method stub
        this.beanName=name;
    }
    
    public void say() {
        System.out.println(beanName+"被实例化了,它的beanFactory是:"+factory+",它的ClassLoader是:"+loader);
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        // TODO Auto-generated method stub
        this.factory=beanFactory;
    }

    @Override
    public void setBeanClassLoader(ClassLoader classLoader) {
        // TODO Auto-generated method stub
        this.loader=classLoader;
    }

}

测试代码:

BeanNameAwareDemo demo=(BeanNameAwareDemo)factory.getBean("beanNameAware");
demo.say();

ApplicationContext独有的

  • ResourceLoaderAware:ApplicatiionContext本身实现了ResourceLoaderAware接口,检测到该接口,会将ApplicatiionContext本身注入到对象实例中
  • ApplicationEventPublicherAware:ApplicatiionContext本身实现了ApplicationEventPublisher接口,可以作为ApplicationEventPublisher来使用。检测到该接口,会将ApplicatiionContext本身注入到对象实例中
  • MessageSourceAware:ApplicatiionContext本身实现了MessageSourceAware接口,用于支持国际化。检测到该接口,会将ApplicatiionContext本身注入到对象实例中
  • ApplicationAware:检测到实现该接口的时候会将自身注入到bean对象中。

例子省略....

BeanPostProcessor处理实例化阶段对象

BeanPostProcessorBeanFactoryPostProcessor有点相似,但是两者处理的内容是完全不一样的。

  • 阶段不一样

     BeanPostProcessor处理的是对象实例化阶段。
     BeanFactoryPostProcessor处理的是容器启动阶段。
    
  • 处理的内容不一样

     BeanPostProcessor处理的是实例化的对象实例。
     BeanFactoryPostProcessor处理的是BeanDefinition。
    

BeanPostProcessor需要实现两个方法:

package org.springframework.beans.factory.config;

import org.springframework.beans.BeansException;
import org.springframework.lang.Nullable;


public interface BeanPostProcessor {
    @Nullable
    default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
    @Nullable
    default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

}

postProcessBeforeInitialization负责前置增强,在bean的实例化之前会执行,postProcessAfterInitialization负责后置增强,在实例化之后进行。

上文介绍的Aware结尾的接口的自动注入操作就是通过BeanPostProcessor进行处理的。检测到对应的Aware接口到时候会调用postProcessBeforeInitialization()检查设置相关的Aware依赖。

例子:
下列代码来自Spring源码中的ApplicationContextAwareProcessor:

    @Override
    @Nullable
    public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {
        AccessControlContext acc = null;

        if (System.getSecurityManager() != null &&
                (bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware ||
                        bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware ||
                        bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)) {
            acc = this.applicationContext.getBeanFactory().getAccessControlContext();
        }

        if (acc != null) {
            AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
                invokeAwareInterfaces(bean);
                return null;
            }, acc);
        }
        else {
            invokeAwareInterfaces(bean);
        }

        return bean;
    }

BeanPostProcessor还是Spring AOP实现的关键。AOP使用BeanPostProcessor生成代理对象。

创建一个自定义的BeanPostProcessor

配置文件

    <bean id="beanPostProcessor" class="com.example.demo.beanpostprocessor.MyBean">
        <property name="password">
            <value>123456</value>
        </property>
    </bean>

bean code:

public class MyBean implements DecodAble{
    
    private String password;
    
    @Override
    public String getPassword() {
        // TODO Auto-generated method stub
        return password;
    }

    @Override
    public void setPassword(String something) {
        // TODO Auto-generated method stub
        Base64.Encoder en=Base64.getEncoder();
        password=new String(en.encodeToString(something.getBytes()));
    }
    
    public void setValue(String someting) {
        password=someting;
    }
}

接口设置:

public interface DecodAble {
    String getPassword();
    void setPassword(String something);
}

BeanPostProcessor Code:

package com.example.demo.beanpostprocessor;

import java.util.Base64;
import java.util.Base64.Decoder;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

public class MyBeanPostProcessor implements BeanPostProcessor{

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        // TODO Auto-generated method stub
        if (bean instanceof DecodAble) {
            String password=((MyBean)bean).getPassword();
            System.out.println("原bean的password:"+password);
            ((MyBean)bean).setValue(decodeSomething(password));
        }
        return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        // TODO Auto-generated method stub
        return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
    }
    
    
    private String decodeSomething(String something) {
        Base64.Decoder decoder=Base64.getDecoder();
        String result=new String(decoder.decode(something.getBytes()));
        return result;
    }
}


测试代码:

MyBeanPostProcessor beanPostProcessor=new MyBeanPostProcessor();
        factory.addBeanPostProcessor(beanPostProcessor);
        MyBean bean=(MyBean) factory.getBean("beanPostProcessor");
        System.out.println("现在的password:"+bean.getPassword());

解析:
这是一个自定义的BeanPostProcessor,其功能为将注入的值Base64编码后传递给对象,在取出的时候通过BeanPostProcessor的前置处理将Base64解码为原字符串。添加了个接口,这个接口的作用只是作为一个判断的依据,不将未编码的bean也进行该解码操作。以至于后续取出的值与想要的值不一致。
其代码核心包括三个部分:

  • 实现BeanPostProcessor
  • 业务逻辑装配,其业务逻辑操作需要与BeanPostProcessor中的处理方法想对应。如编码方式要与解码方式相对应。
  • 创建BeanPostProcessor的对象,同时将这个对象通过方法:addBeanPostProcessor(beanPostProcessor)注册到容器之中,之后的操作与政策的beanFactory操作对象的方式一致。

InitializingBean与init-method:

InitializingBean是容器 内部广泛使用的一个对象生命周期标识接口。
源码:

public interface InitializingBean {

    void afterPropertiesSet() throws Exception;

}

使用场景是在容器实例化过程调用完BeanPostProcessor的前置处理之后。在前置处理结束后容器会检查bean是否实现了InitializingBean接口,是的场合会调用afterPropertiesSet()进一步调整实例状态。使用InitializingBean会导致Spring会有一种侵入式较高的感觉,Spring提供了init-method,在xml文件中使用init-method可以避开这种情景。

一般在使用第三方库的时候才会需要用到这种特性。比如第三方库提供了一个返回工作日的接口,该接口返回的工作日是不包括了公司的自定义的纪念日等日期的,在使用之前需要对其返回的日期进行过滤,去掉那些特殊的日期。这时候可以使用其init-method属性,或者是让bean实现InitializingBean接口,将过滤逻辑放在afterPropertiesSet()中进行处理。

代码:

public class MyBean implements DecodAble,InitializingBean{
    
    private String password;
    //...省略
        @Override
    public void afterPropertiesSet() throws Exception {
        // TODO Auto-generated method stub
        System.out.println("beanPostProcessor处理后的password:"+password);
    }
}

DisposableBean与destory-method

在一切值处理结束之后,容器会检查Singleton类型的Bean实例。看是否实现了DisposableBean接口:

public interface DisposableBean {
    void destroy() throws Exception;
}

是的场合为该实例注入一个销毁的回调。容器在销毁这些对象的时候会执行该回调。
但是并不是说注入了该销毁的回调就一定会执行,只有在对象没被使用的时候才会执行该销毁逻辑,之后才会执行这个销毁的方法,这个动作一般是在容器关闭的时候之后执行,要在容器关闭前执行的话需要我们手动告知在什么时候执行销毁的方法。
对于ApplicationContext道理也是一样的。

beanFactory 测试:

        DefaultListableBeanFactory factory=new DefaultListableBeanFactory();
        ...省略
//      bean.destroy();
        factory.destroySingletons();

bean:

public class MyBean implements DecodAble,InitializingBean,DisposableBean{
    ....省略
    
    @Override
    public void destroy() throws Exception {
        // TODO Auto-generated method stub
        System.out.println("bean执行了销毁的方法");
    }
}

ApplicationContext测试:

ClassPathXmlApplicationContext applicationContext=new ClassPathXmlApplicationContext("simple.xml");
applicationContext.registerShutdownHook();

完整实例代码:
gihub

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,686评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,668评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,160评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,736评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,847评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,043评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,129评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,872评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,318评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,645评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,777评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,861评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,589评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,687评论 2 351