本文转载自微信公众号《程序新视界》,作者是二哥。转载本文请联系程序新视界公众号。在前言中,我们在讲《Spring的Lifecycle》的时候,提到了在Spring的使用中,可以通过Lifecycle接口实现一些基于Spring容器的生命周期逻辑。与此相对的是通过@PostConstruct和@PreDestroy在Bean初始化或销毁时执行一些操作。显然Spring的Lifecycle是根据容器的生命周期来处理逻辑的,而@PostConstruct和@PreDestroy是根据Bean的生命周期来处理业务逻辑的。这里很多朋友有个误区,认为@PostConstruct注解也是Spring提供的。事实上,它不是。它是Java自带的注解。我们从头说起@PostConstruct注解。JSR-250规范在了解@PostConstruct注解之前,先普及一个概念:JSR-250规范。JSR-250主要是围绕“资源”的使用预定义了一些注解(Annotation)。这里的“资源”可以理解为Class类的实例,也可以理解为JavaBean,也可以理解为Spring中的Bean。JSR-250相关的注解都在javax.annotation和javax.annotation.security包中,包括:资源定义和权限控制。我们经常使用的@Resource、@PostConstruct、@PreDestroy、@Generated等都属于本规范定义的注解。规范没有提供具体的实现方法,只是提供了指导性文件和若干注解,由特定的框架来实现。也就是说,@PostConstruct注解并不是Spring提供的注解,而是Spring根据JSR-250规范实现了规范中的@PostConstruct约定。其他框架,或者你自己写一个框架,也可以按照约定来实现。@PostConstruct的约定@PostConstruct和@PreDestroy是JavaEE5引入的,位于javax.annotation包下,是java扩展包定义的注解。其中,javax中的x表示扩展。Java的最初设计者认为这些功能不是Java的核心API,所以将它们放在扩展包中,谁使用谁来实现谁就应该遵守约定。我们直接看类上的注解:“PostConstruct注解用在依赖注入完成后需要执行的方法上,进行任何初始化,必须在类放入服务前调用该方法.支持依赖注入所有的类都必须支持这个注解。用PostConstruct注解的方法必须被调用,即使这个类没有请求注入任何资源。只有一个方法可以被这个注解注解。”“应用了PostConstruct注释的方法必须遵守以下所有条件:该方法不得有任何参数,除非是EJB拦截器(interceptor),它将采用InvocationContext对象;返回类型方法必须是void;方法不能抛出检查异常;应用PostConstruct的方法可以是public、protected、packageprivate或private;除了应用程序客户端,方法不能是静态的;方法可以是final;如果方法throwsanuncheckedexception,thentheclassmustnotbeputintoaserviceunlessitisAnEJBthatcanhandleexceptionsandrecoverfromthem.除了上述约定外,如果在Servlet容器中使用,还有一定的时间进行处理.@PostConstruct执行时机下面说的@PostConstruct的执行时机是基于Spring的实现的,@PostConstruct修饰的方法会在服务端加载Servlet时运行,并且只会被执行一次。PostConstruct在构造函数之后和init()方法之前执行。对应的流程图如下:示例演示了对以上基础概念的理解,我们先来看一个示例演示,使用起来非常简单。在基于Java8的SpringBoot项目中添加如下类:@ServicepublicclassOrderService{publicOrderService(){System.out.println("OrderService构造方法执行...");}@PostConstructprivatevoidinit(){System.out.println("调用了PostConstruct注解方法");}@PreDestroyprivatevoidshutdown(){System.out.println("调用了PreDestroy注解方法");}}启动SpringBoot项目,控制台打印日志如下:OrderService构造方法被执行...服务关闭时调用PostConstruct注解方法,会打印:PreDestroy注解方法是通过实例调用的,基本印证了上面说的理论。Java9后续移除可以直接使用Java8中对应的注解,但在Java9及之后的版本中,J2EE弃用了@PostConstruct和@PreDestroy注解,并计划在Java11中将其删除。对于这种情况,我们有两种解决方案:第一,添加额外的依赖;第二,使用其他方法。第一种方案是你使用这个注解,或者你的项目暂时不能放弃这两个注解。然后,可以手动添加依赖:javax.annotationjavax.annotation-api1.3.2即说,虽然它被删除了,但如果你添加它们的依赖项,你仍然可以使用它。但是此时也提醒了我们在项目中尽量不要使用这两个注解。Java11计划删除它们。此时,如果你使用的是Spring项目,可以考虑另外一种基于Spring的InitializingBean和DisposableBean接口实现相同功能的方法:@ServicepublicclassPaymentServiceimplementsInitializingBean,DisposableBean{publicPaymentService(){System.out.println("PaymentService的构造函数是执行...");}@Overridepublicvoiddestroy()throwsException{System.out.println("销毁方法被调用");}@OverridepublicvoidafterPropertiesSet()throwsException{System.out.println("afterPropertiesSet方法被调用");}}启动项目,打印日志如下:执行PaymentService构造方法...调用afterPropertiesSet方法停止项目,打印如下信息:destroy方法被调用。也就是说,在Spring生态中,我们已经有了可以实现的备选方案了,而且是推荐的方式。事实上,Spring并没有遵守这个约定。在上面的约定中,我们在一个类中谈到了“只有一个方法可以用这个注解来注解”。尝试在OrderService中添加另一个@PostConstruct注解方法:@ServicepublicclassOrderService{publicOrderService(){System.out.println("OrderService构造方法执行...");}@PostConstructprivatevoidinit(){System.out.println("PostConstruct注解调用了方法");}@PostConstructprivatevoidinit1(){System.out.println("调用了PostConstructinit1注解方法");}@PreDestroyprivatevoidshutdown(){System.out.println("调用了PreDestroy注解方法");}}启动程序,打印日志:OrderService构造方法被执行...PostConstructinit1注解方法调用的PostConstruct注解方法不仅没有报错,而且两个方法都被执行了。这是什么意思?这意味着有时会打破协议,请记住这种特殊情况。Spring中的实现原理以上是对@PostConstruct的简单介绍,下面将从Spring源码层面简单分析一下实现原理。我们先看一个Spring接口BeanPostProcessor:publicinterfaceBeanPostProcessor{//任何Bean实例化,并且Bean已经填充(填充属性)都会回调这个方法被填充(populatedattribute)后会回调这个方法ObjectpostProcessAfterInitialization(Objectbean,StringbeanName)throwsBeansException;BeanPostProcessor是SpringIOC容器提供的扩展接口,它有两个回调方法。当在SpringIOC容器中注册了一个BeanPostProcessor实现类后,SpringIOC容器创建的每一个bean实例都会在调用初始化方法(如afterPropertiesSet和任何声明的init方法)之前调用BeanPostProcessor中的postProcessBeforeInitialization方法。,bean实例初始化方法调用后,会调用BeanPostProcessor中的postProcessAfterInitialization方法。整个调用顺序可以简单表示如下:-->SpringIOC容器实例化Bean-->调用BeanPostProcessor的postProcessBeforeInitialization方法-->调用bean实例的初始化方法-->调用BeanPostProcessor的postProcessAfterInitialization方法,BeanPostProcessor有一个实现类CommonAnnotationBeanPostProcessor,专门处理@PostConstruct和@PreDestroy注解。其中CommonAnnotationBeanPostProcessor的父类InitDestroyAnnotationBeanPostProcessor中,对应的调用逻辑如下:InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization()InitDestroyAnnotationBeanPostProcessor.findLifecycleMetadata()//组装生命周期元数据InitDestroyAnnotationBeanPostProcessor.buildLifecycleMetadata()//查找@PostConstruct注释的方法InitDestroyAnnotationBeanPostProcessor.initAnnotationType//查找@PreDestroyannotationmethodInitDestroyAnnotationBeanPostProcessor.destroyAnnotationType//Reflectioncallsmetadata.invokeInitMethods(bean,beanName);Thedetailsofbusinesslogicprocessingarenotshownhereonebyone.Ifyouareinterested,youcantrackthesourcecode.Tosummarizethisarticle,weneedtopayattentiontoafewpoints:first,Springonlyimplementsthespecificationdefinedforthe@PostConstructannotationinJava;second,thisannotationisgraduallydiscardedinJava9,anditisnotrecommendedtouseit;third,SpringcanbeusedInitializingBeanandDisposableBeantoreplacethecorrespondingfunctions.