Spring 中Bean的生命周期

Bean的生命周期

Bean 的生命周期是指在 IOC 容器中, 一个 Bean 从初始化到销毁的整个过程.

生命周期

一、Aware 接口

Aware 接口是 Spring 提供的可用于自定义 Bean 特性的接口。

ApplicationContextAware

为实现类添加 applicationContext 对象引用.

可以将引用转换(向下转型)为此接口的已知子类(例如: ConfigurableApplicationContext)以编程方式操纵它们的方法。另一种用途是手动获取其他Bean对象。

ApplicationContextAware 接口的定义:

public interface ApplicationContextAware {
    void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
}

例子:

public class DemoApplicationContextAware implements ApplicationContextAware {
    private ConfigurableApplicationContext applicationContext;
    
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException{
        // 强制转换(向下转型)
        this.applicationContext = (ConfigurableApplicationContext) applicationContext;
       
        // 程序内获取配置信息. 可以动态改变程序的功能和运行状态.
        configContext.getEnvironment().getProperty("spring.profiles.active");
    }
    
}

BeanNameAware

为实现类提供其关联对象中定义的名称的引用.

ps: 个人理解, 提供该对象在 IOC 容器中的名称.

BeanNameAware 接口的定义:

public interface BeanNameAware {

    void setBeanName(String name) throws BeansException;
}

例子:

public class Person implements BeanNameAware {

    public void setBeanName(String name) throws BeansException{
        System.out.println(name);
        // out: person
    }
}

其他 Aware 及其功能

名称 注入依赖 DOCS
ApplicationContextAware 声明ApplicationContext。 ApplicationContextAware and BeanNameAware
ApplicationEventPublisherAware 封闭的事件发布者ApplicationContext。 附加功能 ApplicationContext
BeanClassLoaderAware 用于加载bean的类加载器。 实例化Bean
BeanFactoryAware 声明BeanFactory ApplicationContextAware and BeanNameAware
BeanNameAware 声明bean的名称。 ApplicationContextAware and BeanNameAware
BootstrapContextAware 容器运行的资源适配器BootstrapContext。通常仅在支持JCA的ApplicationContext实例中可用。 JCA CCI
LoadTimeWeaverAware 定义编织器用于在加载时处理类定义。 在Spring框架中使用AspectJ进行加载时编织
MessageSourceAware 用于解析消息的已配置策略(支持参数化和国际化)。 附加功能 ApplicationContext
NotificationPublisherAware Spring JMX通知发布者。 通知
ResourceLoaderAware 配置资源的加载程序,用于对资源进行低级访问。 资源
ServletConfigAware 容器当前运行的 ServletConfig。仅在Web感知的Spring ApplicationContext中有效。 Spring MVC
ServletContextAware 容器当前运行的 ServletContext。仅在Web感知的Spring ApplicationContext中有效。 Spring MVC

二、Bean的初始化与销毁

对象的初始化发生在对象属性设置之后(意味着已经完成实例化, 并且已对属性赋值).
销毁发生在容器关闭前(应用程序关闭前).

Spring 提供了三种初始化/销毁 Bean 的方式.

若定义了多种初始化方法, 但方法名称相同, 将只调用一次. 同理销毁方法也一样.

基于 @PostConstruct、@PreDestory 注解

@PostConstruct、@PreDestory 是 JSR-250 定义的生命周期注解.
可用于标记任意 ‘无参数’ 的方法.

例子:

public class Person {
    /**
    * 生命周期注解 @PostConstruct,在实例化之后执行
    */
    @PostConstruct
    public void instantiationAfter() {
    
    }
    
    /**
    * 生命周期注解 @PreDestory,在销毁之前执行
    */
    @PreDestory
    public void destoryBefore() {
    
    }
}

基于XML或Java配置

init-method、destory-method 属性可以指定任意 ‘无参数’ 的方法名称.

XML 配置:

<bean id="person" class="lifecycle.Person" init-method="init" destory-method="destory"/>

或 Java 配置类:

@Bean(initMethod = "init", destoryMethod = "destory")
public Person person(){
    return new Person();
}

例子:

public class Person {
    /**
    * 由 init-method 指定的自定义方法
    */
    private void init() {
    
    }
    
    /**
    * 由 destory-method 指定的自定义方法
    */
    private void destory() {
    
    }
}

实现 InitializingBean、DisposableBean 接口

InitializingBean 定义了一个方法, Spring 会在对象属性设置后, 回调此方法:

void afterPropertiesSet() throws Exception;

DisposableBean 也定义了一个方法, Spring 会在容器销毁前, 回调此方法:

void destory() throws Exception;

注: Spring官方出于解耦的原因, 不建议通过这两个接口进行初始化、销毁操作.

例子:

public class Person implements InitializingBean, DisposableBean {
    /**
     * 实现 InitializingBean 的初始化方法
     *
     * @throws Exception
     */
    @Override
    public void afterPropertiesSet() throws Exception{
        
    }
    
    /**
     * 实现 DisposableBean 的初始化方法
     *
     * @throws Exception
     */
    @Override
    public void destory() throws Exception{
        
    }
}

三、BeanPostProcessor Bean后置处理器

BeanPostProcessor 接口定义了可以自定义实现的回调方法,以提供自定义的(或覆盖容器的)实例化逻辑和依赖关系解析逻辑等。
如果要在 Spring 容器完成实例化,配置和初始化 bean 之后实现某些自定义逻辑,则可以插入一个或多个自定义 BeanPostProcessor 实现。

您可以配置多个 BeanPostProcessor 实例,并且可以通过 Ordered 接口设置 order 来控制这些实例的执行顺序。

实现 BeanPostProcessor 接口的类是特殊的,容器会对它们进行不同的处理。BeanPostProcessor 直接引用的所有实例和 bean 都会在启动时实例化,作为 ApplicationContext 特殊启动阶段的一部分。
接下来,所有 BeanPostProcessor 实例都以排序方式注册,并应用于容器中的所有其他 bean (以编程方式注册的 BeanPostProcessor 实例始终在自动检测注册的 BeanPostProcessor 实例之前处理,不管任何显式排序)。
另外, AOP 也是通过 BeanPostProcessor 实现的, 所以 BeanPostProcessor 实例和它们直接引用的 bean 都不符合自动代理的条件.

BeanPostProcessor 接口定义:

public interface BeanPostProcessor {

    /**
    * bean 初始化之前执行.
    **/
    @Nullable
    default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
    
    /**
    * bean 初始化之后执行.
    **/
    @Nullable
    default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
}

ApplicationContextAwareProcessor 源码实例

ApplicationContextAware 接口的检查也是通过 BeanPostProcessor 实现的. 这也能解释为什么 Aware 在初始化之前执行.

class ApplicationContextAwareProcessor implements BeanPostProcessor {

    private final ConfigurableApplicationContext applicationContext;

    private final StringValueResolver embeddedValueResolver;


    /**
     * 为不同的 applicationContext 创建 ApplicationContextAwareProcessor.
     */
    public ApplicationContextAwareProcessor(ConfigurableApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
        this.embeddedValueResolver = new EmbeddedValueResolver(applicationContext.getBeanFactory());
    }


    @Override
    @Nullable
    public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {
        AccessControlContext acc = null;
        
    ---- 偏离标题 略过...
        // 检查 Java安全管理器 和 匹配给定的 Aware 接口
        if (System.getSecurityManager() != null &&
                (bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware ||
                        bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware ||
                        bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)) {
            // 若开启 Java安全管理器, 获取访问控制上下文
            acc = this.applicationContext.getBeanFactory().getAccessControlContext();
        }

        if (acc != null) {
            // 开启 Java安全管理器的情况下需要为不可信的外部代码进行提权操作.
            AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
                invokeAwareInterfaces(bean);
                return null;
            }, acc);
        }
    ----
        else {
            // 根据 Aware 接口类型调用对应的接口方法.
            invokeAwareInterfaces(bean);
        }

        return bean;
    }
    
    private void invokeAwareInterfaces(Object bean) {
        if (bean instanceof Aware) {
            if (bean instanceof EnvironmentAware) {
                ((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
            }
            if (bean instanceof EmbeddedValueResolverAware) {
                ((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
            }
            if (bean instanceof ResourceLoaderAware) {
                ((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
            }
            if (bean instanceof ApplicationEventPublisherAware) {
                ((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
            }
            if (bean instanceof MessageSourceAware) {
                ((MessageSourceAware) bean).setMessageSource(this.applicationContext);
            }
            if (bean instanceof ApplicationContextAware) {
                ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
            }
        }
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        return bean;
    }

}

附 生命周期demo

spring lifecycle demo - github

Demo 输出结果:

Person 类通过 'Person()' 实例化  
Aware方法:'BeanNameAware' 的 'setBeanName()' 方法。  
Aware方法:'ApplicationContextAware' 的 'setApplicationContext()' 方法。  
初始化之前执行。Class: Person. Bean Name: person  
初始化方法:'@PostConstruct' 的 'instantiationAfter()' 方法。  
初始化方法:'InitializingBean' 的 'afterPropertiesSet()' 方法。  
初始化方法:'init-method' 的 'customInitMethod()' 方法。  
初始化之后执行。Class: Person. Bean Name: person  
销毁方法:'@PreDestroy' 的 'destroyBefore()' 方法。  
销毁方法:'DisposableBean' 的 'destroy()' 方法。  
销毁方法:'destroy-method' 的 'customDestroyMethod()' 方法。  

参考

Spring core文档 1.6节、1.8节

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