SpringBean的生命周期

生命周期

对于普通的java对象,当new的时候创建对象,当它没有任何引用的时候被垃圾回收机制回收。而Spring Ioc容器托管的对象,它们的生命周期完全由容器控制。Spring Bean的声明周期如下:

整个生命周期流程如下:

  1. Spring对Bean进行初始化 (此时执行构造器)

  2. Spring将值和Bean的引用注入Bean对应的属性中

  3. 如果Bean实现了BeanNameAware接口,Spring将Bean的ID传递给setBeanName(String name)方法 (实现BeanNameAware清主要是为了通过Bean的引用来获得Bean的ID,一般业务中是很少有用到Bean的ID的

  4. 如果Bean实现了BeanFactoryAware接口,Spring将调用setBeanFactory方法并把BeanFactory容器实例作为参数传入。 (实现BeanFactoryAware 主要目的是为了获取Spring容器,如Bean通过Spring容器发布事件等)

  5. 如果Bean实现了ApplicationContextAware接口,Spring容器将调用setApplicationContext(ApplicationContext ctx)方法,把应用上下文作为参数传入。 (作用与BeanFactory类似都是为了获取Spring容器,不同的是ApplicationContextAware继承自BeanFactory,所以除了BeanFactory的所有功能,还提供了很多其他强大功能 )

  6. 当经过上述几个步骤后,bean对象已经被正确构造,但如果你想要对象被使用前再进行一些自定义的处理,就可以通过BeanPostProcessor接口实现。 注意:这个接口作用于所有的Bean 该接口提供了两个函数:

    • postProcessBeforeInitialzation( Object bean, String beanName ) 当前正在初始化的bean对象会被传递进来,我们就可以对这个bean作任何处理。 这个函数会先于InitialzationBean执行,因此称为前置处理。 所有Aware接口的注入就是在这一步完成的。

    • postProcessAfterInitialzation( Object bean, String beanName ) 当前正在初始化的bean对象会被传递进来,我们就可以对这个bean作任何处理。 这个函数会在InitialzationBean完成后执行,因此称为后置处理。

  7. 当BeanPostProcessor的前置处理完成后就会进入InitializingBean接口。

    这一阶段也可以在bean正式构造完成前增加我们自定义的逻辑,但它与前置处理不同,由于该函数并不会把当前bean对象传进来,因此在这一步没办法处理对象本身,只能增加一些额外的逻辑。 若要使用它,我们需要让bean实现该接口,并把要增加的逻辑写在该函数中。然后Spring会在前置处理完成后检测当前bean是否实现了该接口,并执行afterPropertiesSet函数。

    当然,Spring为了降低对客户代码的侵入性,给bean的配置提供了init-method属性,该属性指定了在这一阶段需要执行的函数名。Spring便会在初始化阶段执行我们设置的函数。init-method本质上仍然使用了InitializingBean接口。

  8. 经过以上的工作后,Bean将一直驻留在应用上下文中给应用使用,直到应用上下文被销毁

  9. 如果Bean实现了DisposableBean接口,Spring将调用它的destory方法,作用与在配置文件中对Bean使用destory-method属性的作用一样,都是在Bean实例销毁前执行的方法。

BeanFactoryPostProcessor

BeanFactoryPostProcessor与BeanPostProcessor不同的是:

  1. BeanFactoryPostProcessor在所有Bean初始化之前执行,也就是最开始的地方;

  2. BeanFactoryPostProcessor只执行一次,而BeanPostProcessor在每个Bean生成周期期间都会执行;

BeanFactoryPostProcessor可以对Bean的配置元数据(XML中配置的bean的属性)进行处理。如果你需要在所有Bean初始化之前执行一段逻辑并且只执行一次,选择BeanFactoryPostProcessor在合适不过了。

使用示例:

public class Test implements BeanPostProcessor, BeanFactoryPostProcessor {

  	// 所有Bean初始化的时候都会执行
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("enter Test.postProcessBeforeInitialization, beanName:" + beanName);
        return bean;
    }

  	// 所有Bean初始化之前执行一次
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
        System.out.println("enter Test.postProcessBeanFactory, beanName:" + configurableListableBeanFactory.getBeanDefinition("shareInterface").getBeanClassName());
    }
}

SmartLifecycle

SmartLifecycle 在容器所有bean加载和初始化完毕执行。

有时候我们需要在Spring加载和初始化所有bean后,接着执行一些任务或者启动需要的异步服务,这样我们可以使用 SmartLifecycle 来做到。SmartLifecycle 是一个接口。当Spring容器加载所有bean并完成初始化之后,会接着回调实现该接口的类中对应的方法(start()方法)。除了start方法,其他方法的作用如下:

// 所有bean初始化完成后执行,只有isAutoStartup返回true才会被执行
public void start() {
  
}

// 该方法只对直接实现接口Lifecycle的类才起作用,对实现SmartLifecycle接口的类无效
// 方法stop()和方法stop(Runnable callback)的区别只在于,后者是SmartLifecycle子类的专属。
public void stop() {

}

// 只有该方法返回false时,start方法才会被执行。
// 只有该方法返回true时,stop(Runnable callback)或stop()方法才会被执行。
public boolean isRunning() {
  return false;
}

// true:执行start方法,false:不执行start方法(默认)
public boolean isAutoStartup() {
  return false;
}

// 当isRunning方法返回true时,该方法才会被调用。
public void stop(Runnable callback) {
				// 如果你让isRunning返回true,需要执行stop这个方法,那么就不要忘记调用callback.run()。
        // 否则在你程序退出时,Spring的DefaultLifecycleProcessor会认为你这个TestSmartLifecycle没有stop完成,程序会一直卡着结束不了,等待一定时间(默认超时时间30秒)后才会自动结束。
        // PS:如果你想修改这个默认超时时间,可以按下面思路做,当然下面代码是springmvc配置文件形式的参考,在SpringBoot中自然不是配置xml来完成,这里只是提供一种思路。
        // <bean id="lifecycleProcessor" class="org.springframework.context.support.DefaultLifecycleProcessor">
        //      <!-- timeout value in milliseconds -->
        //      <property name="timeoutPerShutdownPhase" value="10000"/>
        // </bean>
}

// 如果工程中有多个实现接口SmartLifecycle的类,则这些类的start的执行顺序按getPhase方法返回值从小到大执行。
// 例如:1比2先执行,-1比0先执行。 stop方法的执行顺序则相反,getPhase返回值较大类的stop方法先被调用,小的后被调用。
public int getPhase() {
  return 0;
}

小技巧

// 1. 获取某个类的所有Bean实例的name
String[] beanNames = context.getBeanNamesForType(TestBean.class);

参考:https://www.zhihu.com/question/38597960

Last updated