Spring框架中Bean的生命周期及加载顺序

本文内容纲要:Spring框架中Bean的生命周期及加载顺序

Spring 容器中的 Bean 是有生命周期的

Spring 允许在 Bean 在初始化完成后以及 Bean 销毁前执行特定的操作,常用的设定方式有以下三种:

  • 通过实现 InitializingBean/DisposableBean 接口来定制初始化之后/销毁之前的操作方法;
  • 通过 <bean> 元素的 init-method/destroy-method属性指定初始化之后 /销毁之前调用的操作方法;
  • 在指定方法上加上@PostConstruct 或@PreDestroy注解来制定该方法是在初始化之后还是销毁之前调用。

这是我们就有个疑问,这三种方式是完全等同的吗,孰先孰后?

一、简单介绍

1、init-method方法,初始化bean的时候执行,可以针对某个具体的bean进行配置。init-method需要在applicationContext.xml配置文档中bean的定义里头写明。例如:

这样,当TestBean在初始化的时候会执行TestBean中定义的init方法。

2、afterPropertiesSet方法,初始化bean的时候执行,可以针对某个具体的bean进行配置。afterPropertiesSet 必须实现 InitializingBean接口。实现 InitializingBean接口必须实现afterPropertiesSet方法。

3、BeanPostProcessor,针对所有Spring上下文中所有的bean,可以在配置文档applicationContext.xml中配置一个BeanPostProcessor,然后对所有的bean进行一个初始化之前和之后的代理。BeanPostProcessor接口中有两个方法: postProcessBeforeInitialization和postProcessAfterInitialization。 postProcessBeforeInitialization方法在bean初始化之前执行, postProcessAfterInitialization方法在bean初始化之后执行。

总之,afterPropertiesSet 和init-method之间的执行顺序是afterPropertiesSet 先执行,init-method 后执行。从BeanPostProcessor的作用,可以看出最先执行的是postProcessBeforeInitialization,然后是afterPropertiesSet,然后是init-method,然后是postProcessAfterInitialization。

下面我们将带着这个疑问,试图通过测试代码以及分析Spring源码找到答案。

首先,我们还是编写一个简单的测试代码:

Java代码  

public class InitSequenceBean implements InitializingBean {  

   

    public InitSequenceBean() {  

       System.out.println("InitSequenceBean: constructor");  

    }  

     

    @PostConstruct  

    public void postConstruct() {  

       System.out.println("InitSequenceBean: postConstruct");  

    }  

     

    public void initMethod() {  

       System.out.println("InitSequenceBean: init-method");  

    }  

     

    @Override  

    public void afterPropertiesSet() throws Exception {  

       System.out.println("InitSequenceBean: afterPropertiesSet");  

    }  

}  

  

并且在配置文件中添加如下Bean定义:

<beanclass="InitSequenceBean" init-method="initMethod">

好了,我们启动Spring容器,观察输出结果,就可知道三者的先后顺序了:

InitSequenceBean: constructor

InitSequenceBean: postConstruct

InitSequenceBean: afterPropertiesSet

InitSequenceBean: init-method

通过上述输出结果,三者的先后顺序也就一目了然了:

Constructor > @PostConstruct > InitializingBean > init-method

先大致分析下为什么会出现这些的结果:构造器(Constructor)被率先调用毋庸置疑,InitializingBean先于init-method我们也可以理解(在也谈Spring容器的生命周期中已经讨论过),但是PostConstruct为何率先于InitializingBean执行呢?

我们再次带着这个疑问去查看Spring源代码来一探究竟。

通过Debug并查看调用栈,我们发现了这个类org.springframework.context.annotation.CommonAnnotationBeanPostProcessor,从命名上,我们就可以得到某些信息——这是一个BeanPostProcessor。想到了什么?在也谈Spring容器的生命周期中,我们提到过BeanPostProcessor的postProcessBeforeInitialization是在Bean生命周期中afterPropertiesSet和init-method之前执被调用的。

再次观察CommonAnnotationBeanPostProcessor这个类,它继承自InitDestroyAnnotationBeanPostProcessor。InitDestroyAnnotationBeanPostProcessor顾名思义,就是在Bean初始化和销毁的时候所作的一个前置/后置处理器。

通过查看InitDestroyAnnotationBeanPostProcessor类下的postProcessBeforeInitialization方法:

Java代码  

public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {  

       LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());  

       try {  

           metadata.invokeInitMethods(bean, beanName);  

       }  

       catch (InvocationTargetException ex) {  

           throw new BeanCreationException(beanName, "Invocation of init method failed", ex.getTargetException());  

       }  

       catch (Throwable ex) {  

           throw new BeanCreationException(beanName, "Couldn't invoke init method", ex);  

       }  

        return bean;  

    }  

查看findLifecycleMetadata方法,继而我们跟踪到buildLifecycleMetadata这个方法体中,看下buildLifecycleMetadata这个方法体的内容:

Java代码  

private LifecycleMetadata buildLifecycleMetadata(final Class clazz) {  

       final LifecycleMetadata newMetadata = new LifecycleMetadata();  

       final boolean debug = logger.isDebugEnabled();  

       ReflectionUtils.doWithMethods(clazz, new ReflectionUtils.MethodCallback() {  

           public void doWith(Method method) {  

              if (initAnnotationType != null) {  

                  if (method.getAnnotation(initAnnotationType) != null) {  

                     newMetadata.addInitMethod(method);  

                     if (debug) {  

                         logger.debug("Found init method on class [" + clazz.getName() + "]: " + method);  

                     }  

                  }  

              }  

              if (destroyAnnotationType != null) {  

                  if (method.getAnnotation(destroyAnnotationType) != null) {  

                     newMetadata.addDestroyMethod(method);  

                     if (debug) {  

                         logger.debug("Found destroy method on class [" + clazz.getName() + "]: " + method);  

                     }  

                  }  

              }  

           }  

       });  

       return newMetadata;  

}  

  

分析这段代码发现,在这里会去判断某方法有没有被initAnnotationType/destroyAnnotationType注释,如果有,则添加到init/destroy队列中,后续一一执行。

initAnnotationType/destroyAnnotationType注释是什么呢,我们在CommonAnnotationBeanPostProcessor的构造函数中看到下面这段代码:

Java代码

public CommonAnnotationBeanPostProcessor() {  

       setOrder(Ordered.LOWEST_PRECEDENCE - 3);  

       setInitAnnotationType(PostConstruct.class);  

       setDestroyAnnotationType(PreDestroy.class);  

       ignoreResourceType("javax.xml.ws.WebServiceContext");  

}  

  

一切都清晰了吧。一言以蔽之,@PostConstruct注解后的方法在BeanPostProcessor前置处理器中就被执行了,所以当然要先于InitializingBean和init-method执行了。

最后,给出本文的结论,Bean在实例化的过程中:

Constructor > @PostConstruct > InitializingBean > init-method

本文源代码下载:https://lb-multi-demo.googlecode.com/svn/trunk/spring-lifecycle-test

加载顺序为:

先构造函数——>然后是b的set方法注入——>InitializingBean 的afterPropertiesSet方法——>init-method方法

一、Spring装配Bean的过程   

1. 实例化;  

2. 设置属性值;  

3. 如果实现了BeanNameAware接口,调用setBeanName设置Bean的ID或者Name;  

4. 如果实现BeanFactoryAware接口,调用setBeanFactory 设置BeanFactory;  

5. 如果实现ApplicationContextAware,调用setApplicationContext设置ApplicationContext  

6. 调用BeanPostProcessor的预先初始化方法;  

7. 调用InitializingBean的afterPropertiesSet()方法;  

8. 调用定制init-method方法;  

9. 调用BeanPostProcessor的后初始化方法;  

Spring容器关闭过程   

1. 调用DisposableBean的destroy();  

2. 调用定制的destroy-method方法;

总结为:

以下内容是从书中摘录来的,但是我发现即使摘录一遍,对其内容的理解也会更加深入!

① Spring IoC容器找到关于Bean的定义并实例化该Bean。

② Spring IoC容器对Bean进行依赖注入。

③ 如果Bean实现了BeanNameAware接口,则将该Bean的id传给setBeanName方法。

④ 如果Bean实现了BeanFactoryAware接口,则将BeanFactory对象传给setBeanFactory方法。

⑤ 如果Bean实现了BeanPostProcessor接口,则调用其postProcessBeforeInitialization方法。

⑥ 如果Bean实现了InitializingBean接口,则调用其afterPropertySet方法。

⑦ 如果有和Bean关联的BeanPostProcessors对象,则这些对象的postProcessAfterInitialization方法被调用。

⑧ 当销毁Bean实例时,如果Bean实现了DisposableBean接口,则调用其destroy方法。

BeanPostProcessor是创建每个类时都会去执行的一个接口,postProcessBeforeInitialization是在类初始化之前调用的一个方法,创建的对象的引用会指向改方法的返回值对象。

调用过程示例如下:

ClassA classA = new ClassA();

classA = postProcessBeforeInitialization(classA, "classA");

所以我们可以通过该方法就可以实现动态替换我们的bean。

@Component

public class LocalProcessor implements BeanPostProcessor {

@Autowired

private DefaultListableBeanFactory defaultListableBeanFactory;

private String targetBeanName = "test22";

@Override

public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {

if (StringUtils.endsWithIgnoreCase(beanName, targetBeanName)) {

boolean containsBean = defaultListableBeanFactory.containsBean(targetBeanName);

if (containsBean) {

//移除bean的定义和实例

defaultListableBeanFactory.removeBeanDefinition(targetBeanName);

}

//注册新的bean定义和实例

defaultListableBeanFactory.registerBeanDefinition(targetBeanName, BeanDefinitionBuilder.genericBeanDefinition(Test55.class).getBeanDefinition());

bean = null;

return new Test55();

}

return bean;

}

@Override

public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {

return bean;

}

}

在事件监听器中,分别注入和获取Bean对象。分别打印两个对象的类名,可以看到两个类名都是com.example.hellododo.data.Test55

@Slf4j

@Component

public class LocalEventListener implements ApplicationListener<ContextRefreshedEvent> {

@Autowired

private DefaultListableBeanFactory defaultListableBeanFactory;

private boolean doFist = true;

@Autowired

private Test22 test22;

@Override

public void onApplicationEvent(ContextRefreshedEvent event) {

Test22 bean = defaultListableBeanFactory.getBean(Test22.class);

if (doFist) {

if (bean != null) {

log.info("autowiredClassName={}, beanClassName={}", test22.getClass().getName(), bean.getClass().getName());

doFist = false;

}

}

}

}

本文内容总结:Spring框架中Bean的生命周期及加载顺序

原文链接:https://www.cnblogs.com/candlia/p/11919922.html

以上是 Spring框架中Bean的生命周期及加载顺序 的全部内容, 来源链接: utcz.com/z/362462.html

回到顶部