Spring生命周期回调与容器扩展

本篇主要总结下Spring容器在初始化实例前后,提供的一些回调方法和可扩展点。利用这些方法和扩展点,可以实现在Spring初始化实例前后做一些特殊逻辑处理。

下面主要介绍:

  • 类级别的生命周期初始化回调方法init-method配置、InitializingBean接口和PostConstruct注解
  • 容器级别的扩展BeanPostProcessor接口和BeanFactoryPostProcessor接口

1. 类级别生命周期回调

1.1 init-method

参照:Spring bean xsd init-method

init-method是在Spring配置文件中声明bean的时候的一个配置项。init-method配置项的值为类中的一个无参方法,但可抛出异常。该方法会在Spring容器实例化对象并设置完属性值之后被调用。

init-method能实现的功能与InitializingBean接口、PostConstruct注解一致

Spring配置文件及测试类如下:

<bean id = "initMethodBeanService" class="name.liuxi.spring.ext.InitMethodBeanService" init-method="init">
      <property name="f2" value="2"/>
</bean>

测试类如下:

public class InitMethodBeanService {
    private static Integer f1;
    private Integer f2;

    static {
        f1 = 1;
        System.out.println("InitMethodBeanService static block execute...");
    }

    public InitMethodBeanService(){
        System.out.println("InitMethodBeanService construct method execute...");
    }

    public void init(){
        System.out.println("InitMethodBeanService init method execute...");
    }

    public Integer getF2() {
        return f2;
    }

    public void setF2(Integer f2) {
        this.f2 = f2;
        System.out.println("InitMethodBeanService setF2 method execute...");
    }
}

执行结果打印如下:

InitMethodBeanService static block execute...
InitMethodBeanService construct method execute...
InitMethodBeanService setF2 method execute...
InitMethodBeanService init method execute...
test method execute...

1.2 InitializingBean接口

参照:Spring官方文档beans-factory-lifecycle-initializingbean

InitializingBean接口中声明了一个方法afterPropertiesSet,该方法会在Spring容器实例化对象并设置完属性值之后被调用。和上面的init-method实现的功能一致,因此Spring不推荐使用InitializingBean接口。

例子比较简单,不列出来了

1.3 PostConstruct注解

翻译:Spring官方文档beans-postconstruct-and-predestroy-annotations

@PostConstruct注解是和init-methodInitializingBean接口实现效果一致的生命周期回调方法

@PostConstruct
public void postConstruct(){
    System.out.println("PostConstructService postConstruct method execute...");
}

总结下上面三个生命周期回调方法init-methodInitializingBean接口@PostConstruct注解
1. 都是针对单个类的实例化后处理
2. 执行时间都是在类实例化完成,且成员变量完成注入之后调用的
3. 对于init-method,还可以在Spring配置文件的beans元素下配置默认初始化方法,配置项为default-init-method
4. 若以上三种方式配置的初始化方法都不一样,则执行顺序为:@PostConstruct注解方法 –> InitializingBean的afterPropertiesSet –> init-method方法 ;若三种方式配置的方法一样,则方法只执行一次 (参照:Spring官方文档beans-factory-lifecycle-combined-effect
5. 有初始化回调方法,对应的也有销毁的回调方法。@PostConstruct注解方法 –> InitializingBean的afterPropertiesSet –> init-method方法 分别对应 @PreDestroy注解方法 –> DisposableBean的destroy –> destroy-method方法

2. 容器级别扩展

翻译:Spring官方文档3.8 Container Extension Points

通常情况下,开发人员无需自定义实现一个ApplicationContext的子类去扩展Spring IOC容器,Spring IOC容器通过对外暴露的一些接口,可实现对Spring IOC容器的扩展。

2.1 BeanPostProcessor接口

2.1.1 bean实例初始化后处理器及后处理器链

BeanPostProcessor接口定义了两个容器级别的回调方法postProcessBeforeInitializationpostProcessAfterInitialization,用于在初始化实例后的一些逻辑处理,会针对容器中的所有实例进行处理。实现了BeanPostProcessor接口的类,称之为bean实例初始化后处理器

若在Spring IOC容器中集成了多个实例初始化后处理器,这些后处理器构成的集合称之为bean实例初始化后处理器链

  1. postProcessBeforeInitialization方法在类实例化且成员变量注入完成之后执行,初始化方法(例如InitializingBeanafterPropertiesSet方法)之前执行
  2. postProcessAfterInitialization方法在类实例化且成员变量注入完成之后执行,初始化方法(例如InitializingBeanafterPropertiesSet方法)之后执行

总结:
1. 实例初始化后处理器多用于对实例的一些代理操作。Spring中一些使用到AOP的特性也是通过后处理器的方式实现的。
2. 实例初始化后处理器链 是多个后处理器,就会有执行顺序的问题,可以通过实现Ordered接口,指定后处理的执行顺序,Ordered接口声明了getOrder方法,方法返回值越小,后处理的优先级越高,越早执行。
3. 在通过实现BeanPostProcessor接口自定义实例初始化后处理器的时候,建议也实现Ordered接口,指定优先级。
4. 这些后处理器的作用域是当前的Spring IOC容器,即后处理器被声明的Spring IOC容器。对于有层次结构的Spring IOC容器,实例初始化后处理器链不会作用于其他容器所初始化的实例上,即使两个容器在同一层次结构上。
5. 实例初始化后处理器的实现类只需要和普通的被Spring管理的bean一样声明,Spring IOC容器就会自动检测到,并添加到实例初始化后处理器链中。
6. 相对于自动检测,我们也可以调用ConfigurableBeanFactoryaddBeanPostProcessor方法,以编程的方式将一个实例初始化后处理器添加到实例初始化后处理器链中。这在需要判定添加条件的场景下比较实用。这种编程式的方式会忽略到实现的Ordered接口所指定的顺序,而会作用于所有的被自动检测的实例初始化后处理器之前。

2.1.2 bean实例初始化后处理器与AOP

BeanPostProcessor是一个特殊的接口,实现这个接口的类会被作为Spring管理的bean的实例的后处理器。因此,在Spring应用上下文启动的一个特殊阶段,会直接初始化所有实现了BeanPostProcessor接口的实例,以及该实例所引用的类也会被实例化。然后作为后处理器应用于其他普通实例。

由于AOP的自动代理是以实例化后处理器的方式实现的,所以无论是bean实例初始化后处理器链实例还是其引用的实例,都不能被自动代理。因而,不要在这些实例上进行切面织入。(对于这些实例,会产生这样的日志消息:“类foo不能被所有的实例化后处理器链处理,即不能被自动代理”)。

注意:当实例化后处理器以autowiring或@Resource的方式引用其他bean,Spring容器在以类型匹配依赖注入的时候,可能会注入非指定的bean(例如:实例化后处理器实现类以Resource方式依赖bean,若set方法和被依赖的bean的名称一致或者被依赖bean未声明名称,则依赖注入会以类型匹配的方式注入,此时可能会注入非指定的bean)。这也会导致自动代理或其他方式的实例化后处理器处理失败。

2.1.3 bean实例初始化后处理器示例

public class BeanPostProcessorService implements BeanPostProcessor {
    @Override
    public Object postProcessAfterInitialization(Object o, String s) throws BeansException {
        System.out.println("BeanPostProcessorService postProcessAfterInitialization method execute... ");
        return o;
    }

    @Override
    public Object postProcessBeforeInitialization(Object o, String s) throws BeansException {
        System.out.println("BeanPostProcessorService postProcessBeforeInitialization method execute... ");
        return o;
    }
}

2.2 BeanFactoryPostProcessor接口

2.2.1 bean factory后处理器

通过实现BeanFactoryPostProcessor接口,可以读取容器所管理的bean的配置元数据,在bean完成实例化之前进行更改,这些bean称之为bean factory后处理器

BeanFactoryPostProcessors与BeanPostProcessor接口的异同点:
相同点:

  1. 都是容器级别的后处理器
  2. 都可配置多个后处理器,并通过实现Ordered接口,指定执行顺序
  3. 都是针对接口声明的容器中所管理的bean进行处理,在有层级结构的容器中,不能处理其他容器中的bean,即使两个容器是同一层次
  4. 都是只需要在容器中和普通bean一样声明,容器会自动检测到,并注册为后处理器
  5. 会忽略延迟初始化属性配置

不同点:

  1. BeanFactoryPostProcessors接口在bean**实例化前处理bean的配置元数据BeanPostProcessor接口在bean实例化后处理bean的实例**
  2. BeanFactoryPostProcessors接口也能通过BeanFactory.getBean()方法获取bean的实例,这样会引起bean的实例化。由于BeanFactoryPostProcessors后处理器是在所有bean实例化之前执行,通过BeanFactory.getBean()方法会导致提前实例化bean,从而打破容器标准的生命周期,这样可能会引起一些负面的影响(例如:提前实例化的bean会忽略bean实例化后处理器的处理)。

2.2.2 Spring内置及自定义bean factory后处理器

Spring内置了一些bean factory后处理器(例如:PropertyPlaceholderConfigurerPropertyOverrideConfigurer)。同时也支持实现BeanFactoryPostProcessor接口,自定义bean factory后处理器。下面说说Spring内置的两个后处理器和自定义后处理器。

PropertyPlaceholderConfigurer

Spring为了避免主要的XML定义文件的修改而引起的风险,提供了配置分离,可以将一些可能变更的变量配置到属性配置文件中,并在XML定义文件中以占位符的方式引用。这样,修改配置只需要修改属性配置文件即可。PropertyPlaceholderConfigurer用于检测占位符,并替换占位符为配置属性值。示例如下:

PropertyPlaceholderConfigurer通过jdbc.properties属性配置文件,在运行时,将dataSource这个bean中数据库相关信息的属性占位符替换成对应的配置值。
XML配置如下:

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="locations" value="classpath:com/foo/jdbc.properties"/>
</bean>

<bean id="dataSource" destroy-method="close"
        class="org.apache.commons.dbcp.BasicDataSource">
    <property name="driverClassName" value="${jdbc.driverClassName}"/>
    <property name="url" value="${jdbc.url}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
</bean>

属性配置文件jdbc.properties如下:

jdbc.driverClassName=org.hsqldb.jdbcDriver
jdbc.url=jdbc:hsqldb:hsql://production:9002
jdbc.username=sa
jdbc.password=root

PropertyPlaceholderConfigurer不仅支持属性配置文件的读取,也支持读取系统属性。通过systemPropertiesMode属性值可配置读取优先级。各种取值说明如下:

  • 0 : 不读取系统属性
  • 1 : 若引用的属性配置文件中未检索到对应占位符的配置,则读取系统属性。默认为1
  • 2 : 先读取系统属性,再读取引用的属性配置文件。这种配置可能导致系统属性覆盖配置文件。

PropertyOverrideConfigurer

PropertyOverrideConfigurer类可以通过引用属性配置文件,直接给容器中的bean赋值。当一个bean的属性被多个PropertyOverrideConfigurer类实例赋值时,最后一个的值会覆盖前面的。

还是以上面给上面的dataSource的bean赋值为例:

PropertyOverrideConfigurer类对属性配置文件的引用使用一个新的方式,如下:

<context:property-override location="classpath:override.properties"/>

override.properties属性配置文件的属性的命名规则和上面不同(上面例子中需要保证属性名和占位符一致),命名规则是beanName.property

dataSource.driverClassName=com.mysql.jdbc.Driver
dataSource.url=jdbc:mysql:mydb
dataSource.username=sa
dataSource.password=root

支持复合属性的赋值,但是要保证引用被赋值属性的对象非空
例如:foo.fred.bob.sammy=123

自定义bean factory后处理器

自定义bean factory后处理器就是实现BeanFactoryPostProcessor接口,完成对Spring容器管理的bean的配置元数据进行修改。例如:修改类属性注入的值,示例如下:

定义一个用户类UserBean

public class UserBean {
    private String userName;

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }
}

Spring XML配置文件配置用户类,并给用户名属性userName注入值haha

<bean class="name.liuxi.spring.ext.BeanFactoryPostProcessorService"/>
<bean id="user" class="name.liuxi.spring.ext.UserBean">
      <property name="userName" value="haha"/>
</bean>

下面是自定义的bean factory后处理器,修改属性userName的值为heihei

public class BeanFactoryPostProcessorService implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        System.out.println("BeanFactoryPostProcessorService postProcessBeanFactory method execut...");
        BeanDefinition bd = beanFactory.getBeanDefinition("user");
        MutablePropertyValues pv =  bd.getPropertyValues();
        if(pv.contains("userName"))
        {
            pv.addPropertyValue("userName", "heihei");
        }
    }
}

传送门======>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Spring容器生命周期包括以下几个阶段: 1. 实例化:创建BeanFactory容器实例。 2. 配置:设置容器的配置信息,如注册Bean后置处理器、设置属性编辑器等。 3. 实例化Bean:根据配置信息,实例化所有需要的Bean。 4. 注入依赖:对所有需要注入的Bean进行依赖注入。 5. 初始化:调用所有Bean的初始化方法(如InitializingBean接口的afterPropertiesSet方法、自定义的init方法)。 6. 使用:容器Bean可以被应用程序使用。 7. 销毁:容器关闭时,调用所有Bean的销毁方法(如DisposableBean接口的destroy方法、自定义的destroy方法)。 总体来说,Spring容器生命周期是在实例化、配置、实例化Bean、注入依赖、初始化、使用和销毁这七个阶段中完成的。每个阶段都可以通过配置文件或代码进行自定义,以满足不同的业务需求。 ### 回答2: Spring容器生命周期可以分为以下阶段: 1. 初始化阶段:Spring容器会加载配置文件,并根据配置文件中的信息创建相应的Bean对象。此阶段包括了Bean的定义和解析、AOP代理的创建等操作。 2. 实例化阶段:在初始化阶段,Spring容器会根据配置文件中的信息实例化Bean对象,并将其放入容器中。在这个阶段,Spring会调用Bean的构造函数或工厂方法来创建对象。 3. 属性赋值阶段:在实例化阶段之后,Spring容器会为Bean对象设置相应的属性值。这些属性值可以通过Setter方法注入,也可以通过构造函数或工厂方法参数注入。 4. 初始化阶段:在所有的Bean实例化和属性赋值完成后,Spring将调用Bean的初始化方法进行一些额外的操作。这些操作包括了Bean的自定义初始化逻辑,以及与其他Bean之间的依赖关系的处理。 5. 使用阶段:在初始化阶段之后,Bean就可以被其他Bean或系统代码使用了。 6. 销毁阶段:当Spring容器关闭时,会调用所有Bean的销毁方法进行一些清理工作。这些销毁方法可以是自定义的,用于释放资源、关闭数据库连接等操作。 总结来说,Spring容器生命周期包括了初始化阶段、实例化阶段、属性赋值阶段、初始化阶段、使用阶段和销毁阶段。在每个阶段,Spring容器会执行不同的操作来管理和控制Bean的生命周期。 ### 回答3: Spring容器生命周期可以分为三个阶段:实例化阶段、初始化阶段和销毁阶段。 在实例化阶段,Spring容器会根据配置文件或注解等方式创建Bean的实例。这个过程包括Bean的实例化和依赖关系的建立。容器会读取配置文件,通过反射机制实例化Bean对象,并将其存储在容器中。同时,容器会根据Bean之间的依赖关系,将相关的Bean进行关联。 在初始化阶段,Spring容器会对实例化后的Bean进行初始化操作。这个过程包括Bean的属性注入、生命周期回调和自定义初始化方法。容器会将配置文件中的属性值注入到Bean的属性中,调用Bean的生命周期回调方法(如BeanPostProcessor的beforeInitialization和afterInitialization方法),并执行开发者自定义的初始化方法(如@PostConstruct注解标注的方法)。 在销毁阶段,Spring容器会对Bean进行销毁操作。这个过程包括Bean的生命周期回调和自定义销毁方法。容器会调用Bean的生命周期回调方法(如BeanPostProcessor的postProcessBeforeDestruction方法),并执行开发者自定义的销毁方法(如@PreDestroy注解标注的方法)。同时,容器会释放Bean占用的资源,如关闭数据库连接、释放文件句柄等。 总之,Spring容器生命周期经历了实例化、初始化和销毁三个阶段。通过这个阶段的处理,容器可以正确地创建、初始化和销毁Bean对象,并为开发者提供了丰富的扩展点,使得开发者可以在Bean的生命周期中进行必要的操作和自定义处理。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值