当前位置:   article > 正文

Springboot扩展点之@PostConstruct_springboot postconstruct

springboot postconstruct

Springboot扩展点系列实现方式、工作原理集合:

Springboot扩展点之ApplicationContextInitializer

Springboot扩展点之BeanFactoryPostProcessor

Springboot扩展点之BeanDefinitionRegistryPostProcessor

Springboot扩展点之BeanPostProcessor

Springboot扩展点之InstantiationAwareBeanPostProcessor

Springboot扩展点之SmartInstantiationAwareBeanPostProcessor

Springboot扩展点之ApplicationContextAwareProcessor

Springboot扩展点之@PostConstruct

Springboot扩展点之InitializingBean

Springboot扩展点之SmartInitializingSingleton

Springboot扩展点之CommandLineRunner和ApplicationRunner

Springboot扩展点之FactoryBean

Springboot扩展点之DisposableBean

Springboot扩展点系列之终结篇:Bean的生命周期

前言

@postContruct全限定类名是javax.annotation.PostConstruct,可以看出来其本身不是Spring定义的注解,但是Spring提供了具体的实现,所以这篇文章主要分析的是@PostConstruct在Spring项目开发中的功能特性、实现方式和基本工作原理。

功能特性

从@PostConstruct注解的注释上看,可以了解到以下内容:

1、要在依赖加载后,对象佤用前执行,并且只执行一次;

2、所有支持依赖注入的类都需要支持此方法。即使类没有请求注入任何的资源,也必须调用被@PostConstruct注解标记的方法;

3、一个类中在一个方法上使用@PostConstruct注解;

4、使用@PostConstruct注解标记的方法不能有参数,除非是拦截器,可以采用拦截器规范定义的InvocationContext对象。

5、使用@PostConstruct注解标记的方法不能有返回值,实际上如果有返回值,也不会报错,但是会忽略掉;

6、使用@PostConstruct注解标记的方法的权限,public、private、protected都可以;

7、使用@PostConstruct注解标记的方法不能被static修饰,但是final是可以的;

  1. package javax.annotation;
  2. import java.lang.annotation.*;
  3. import static java.lang.annotation.ElementType.*;
  4. import static java.lang.annotation.RetentionPolicy.*;
  5. @Documented
  6. @Retention (RUNTIME)
  7. @Target(METHOD)
  8. public @interface PostConstruct {
  9. }

在在实际的Spring项目中Bean的生命周期里,其执行的时机是:

1、Bean的实例化;

2、Bean内依赖属性的注入 ;

3、Bean里被@PostConstruct标记的方法;

下面在实现方式里,用一个小例子来验证一下这个过程。

实现方式

1、定义一个ExampleController类,采用setter的依赖注入的方式,注入exampleService属性,另外在定义一个myPostConstruct方法用@PostConstruct注解标记;

  1. @RestController
  2. @Slf4j
  3. public class ExampleController {
  4. private ExampleService exampleService;
  5. public ExampleController() {
  6. log.info("----ExampleController无参数构造方法被执行");
  7. }
  8. @Autowired
  9. public void setExampleService(ExampleService exampleService) {
  10. this.exampleService = exampleService;
  11. log.info("----ExampleController类的setExampleService方法被调用");
  12. }
  13. @PostConstruct
  14. public void myPostConstruct(){
  15. log.info("----ExampleController类的myPostConstruct方法被调用");
  16. }
  17. }

2、定义ExampleService类

  1. @Service
  2. @Slf4j
  3. public class ExampleService {
  4. public ExampleService() {
  5. log.info("----ExampleService的无参数构造方法被调用");
  6. }
  7. }

3、定义一个单元测试,在单元测试中启动Spring容器;

  1. @Test
  2. public void test4(){
  3. log.info("----单元测试执行开始");
  4. AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("com.fanfu");
  5. log.info("----单元测试执行完毕");
  6. }

单元测试验证结果:

从单元测试的执行结果来看,首先,ExampleConstroller被实例化,接着是ExampleService被实例化,然后通过setter依赖注入的方式把ExampleService对象注入到了ExampleConstroller对象中,之后才开始了被@PostConstruct注解标记的myPostConstruct方法的执行。下面就单元测试的结果分析一个@PostConstruct注解的工作原理。

工作原理

@PostConstruct的工作原理的关键问题就是:在Spring容器启动的过程,被@PostConstruct标记的方法是怎么被执行的?

在被@PostConstruct标记的方法上打上断点,待程序执行的断点的时候观察一下方法调用栈信息,这时会发现:

1、Spring容器启动过程的最后一步,即把需要提前注册的一些非懒加载的单例Bean时,如ExampleController,注意这时exampleController对象实例化完成,需要注入的exampleService的属性已经被实例化,且已经注入到exampleController对象中,在BeanPostProcessor接口的扩展方法中,被@PostConstruct标记的方法开始触发执行,入口位置在AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInitialization。

  1. public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
  2. throws BeansException {
  3. Object result = existingBean;
  4. for (BeanPostProcessor processor : getBeanPostProcessors()) {
  5. Object current = processor.postProcessBeforeInitialization(result, beanName);
  6. if (current == null) {
  7. return result;
  8. }
  9. result = current;
  10. }
  11. return result;
  12. }

那么触发被@PostConstruct注解标记的方法执行的BeanPostProcessor接口的具体是实现是哪个类呢?通过debug分析,是CommonAnnotationBeanPostProcessor类。

2、CommonAnnotationBeanPostProcessor类继承于InitDestroyAnnotationBeanPostProcessor,实际的触发@PostConstruct标记方法执行的入口是在InitDestroyAnnotationBeanPostProcessor的postProcessBeforeInitialization()

3、InitDestroyAnnotationBeanPostProcessor的postProcessBeforeInitialization()内,逻辑相对比较简洁,先查询bean中被@PostConstruct标记的方法,然后再使用java反射来执行这个方法;

  1. public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
  2. //查询bean中被@PostConstruct标记的方法,相关的信息封在LifecycleMetadata对象的
  3. LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());
  4. try {
  5. //使用java反射执行被@PostConstruct标记的方法
  6. metadata.invokeInitMethods(bean, beanName);
  7. }
  8. catch (InvocationTargetException ex) {
  9. throw new BeanCreationException(beanName, "Invocation of init method failed", ex.getTargetException());
  10. }
  11. catch (Throwable ex) {
  12. throw new BeanCreationException(beanName, "Failed to invoke init method", ex);
  13. }
  14. return bean;
  15. }

总结

从以上几步的分析来看,被@PostConstruct标记的方法是怎么被执行的,这个问题回答清楚了。那么如果面试官问你,你了解@PostContruct注解是怎么工作的吗?你就可以这么回答他:在Bean实例化、属性注入后,被@PostConstruct标记的方法是在BeanPostProcessor的扩展方法postProcessBeforeInitialization()触发执行的,具体实现类是InitDestroyAnnotationBeanPostProcessor,具体的逻辑是:先查询被@PostConstruct标记的方法,然后使用java反射去执行这个方法。回答完后,如果他不换一个问题的话,把Springboot的扩展点都给他盘一遍。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/article/detail/57726
推荐阅读
相关标签
  

闽ICP备14008679号