当前位置: 代码迷 >> 综合 >> Spring 注解面面通 之 @PostConstruct 应用解析以及与afterPropertiesSet、init-method对比
  详细解决方案

Spring 注解面面通 之 @PostConstruct 应用解析以及与afterPropertiesSet、init-method对比

热度:10   发布时间:2024-01-17 00:59:48.0

??简介

??JSR-250规范为Bean初始化之后/销毁之前方法指定了两个注解:@PostConstruct@PreDestroy,这两个注解可以应用在方法级别上,@PostConstruct注释方法在Bean实例化之后、应用注入之前调用,@PreDestroy注释方法在Bean实例销毁之前调用。

??@PostConstruct@PreDestroy规范中要去较为严格,但Spring在实现时,并未完全按照其规范实现,在Spring中应用@PostConstruct@PreDestroy这两个注解,以实现约束为准即可。

??注意事项

??InitializingBean注意事项:

??① Bean必须实现InitializingBean接口。

??② BeanafterPropertiesSet不能使用@PostConstruct注释。

??init-method注意事项:

??① init-method指定属性不能为空。

??② Bean不可以实现InitializingBean接口或Beaninit-method方法名不可以为afterPropertiesSet

??③ Beaninit-method方法不能使用@PostConstruct注释。

??@PostConstruct注意事项:

??① 可以应用于任何可见性的方法:publicpackage-protectedprotectedprivate

??② 不能注释在InitializingBean.afterPropertiesSet()init-method方法上,可能导致后两者失效。

??演示示例

??@PostConstructInitializingBeaninit-method都可以用作Bean初始化相关操作,示例将一起演示这三种方式。

??1)InitTestBean,用于进行初始化相关的测试。

??① 实现InitializingBean接口,重写afterPropertiesSet()方法。

??② 添加initMethod()方法,用于进行init-method的配置。

??③ 添加postConstructor()方法,用于@PostConstruct注解注释。

package com.arhorchin.securitit.initbean;import javax.annotation.PostConstruct;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;/*** @author Securitit.* @note Bean初始化测试.*/
public class InitTestBean implements InitializingBean {
    /*** logger.*/private Logger logger = LoggerFactory.getLogger(InitTestBean.class);@Overridepublic void afterPropertiesSet() throws Exception {
    logger.info("调用InitializingBean的afterPropertiesSet方法.");}public void initMethod() throws Exception {
    logger.info("调用init-method的initMethod方法.");}@PostConstructpublic void postConstructor() throws Exception {
    logger.info("调用@PostConstruct注释的方法.");}}

??2) 在Spring的配置文件中增加Bean声明,并指定init-method属性。

<bean class="com.arhorchin.securitit.initbean.InitTestBean" init-method="initMethod"></bean>

??3) 运行程序查看效果,可以看到如下的输出。

2020-12-17 14:42:12 INFO [c.a.s.i.InitTestBean] 调用@PostConstruct注释的方法.
2020-12-17 14:42:12 INFO [c.a.s.i.InitTestBean] 调用InitializingBean的afterPropertiesSet方法.
2020-12-17 14:42:12 INFO [c.a.s.i.InitTestBean] 调用init-method的initMethod方法.

??从运行结果可以看出:

??① @PostConstruct先于InitializingBean.afterPropertiesSet()

??② InitializingBean.afterPropertiesSet()先于init-methodinitMethod()

??自定义注解示例

??@PostConstruct@PreDestroy实现关键在于org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor,而InitDestroyAnnotationBeanPostProcessor允许通过setInitAnnotationType(...)setDestroyAnnotationType(...)来自定义初始化和销毁注解类型。

??1) 自定义注解@DefPostConstruct,用于进行演示。

package com.arhorchin.securitit.initbean;import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** @author Securitit.* @note 自定义初始化注解.*/
@Documented
@Retention (RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface DefPostConstruct {
    }

??2)DefApplicationContextAware,实现ApplicationContextAware,用于通过InitDestroyAnnotationBeanPostProcessorsetInitAnnotationType(...)setDestroyAnnotationType(...)设置初始化和销毁注解类型。

package com.arhorchin.securitit.initbean;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;/*** @author Securitit.* @note 用于测试自定义注解的ApplicationContextAware.*/
public class DefApplicationContextAware implements ApplicationContextAware {
    /*** logger.*/private Logger logger = LoggerFactory.getLogger(DefApplicationContextAware.class);@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    InitDestroyAnnotationBeanPostProcessor initDestroy = null;initDestroy = applicationContext.getBean(InitDestroyAnnotationBeanPostProcessor.class);logger.info("BeanPostProcessor.postProcessBeforeInitialization调用.Bean名称:" + initDestroy);initDestroy.setInitAnnotationType(DefPostConstruct.class);initDestroy.setDestroyAnnotationType(DefPreDestroy.class);}}

??3) 修改InitTestBean,用于进行初始化相关的测试。

package com.arhorchin.securitit.initbean;import javax.annotation.PostConstruct;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;/*** @author Securitit.* @note Bean初始化测试.*/
public class InitTestBean implements InitializingBean {
    /*** logger.*/private Logger logger = LoggerFactory.getLogger(InitTestBean.class);@Overridepublic void afterPropertiesSet() throws Exception {
    logger.info("调用InitializingBean的afterPropertiesSet方法.");}public void initMethod() throws Exception {
    logger.info("调用init-method的initMethod方法.");}@DefPostConstructpublic void postConstructor() throws Exception {
    logger.info("调用@PostConstruct注释的方法.");}}

??4) 在Spring的配置文件中增加Bean声明,并指定init-method属性,同时配置DefApplicationContextAware

<!-- 配置 ApplicationContextAware -->
<beanclass="com.arhorchin.securitit.initbean.DefApplicationContextAware"></bean>
<!-- 配置 InitTestBean -->
<bean class="com.arhorchin.securitit.initbean.InitTestBean"init-method="initMethod"></bean>

??5) 运行程序查看效果,可以看到如下的输出。

2020-12-17 17:01:08 INFO [c.a.s.i.DefApplicationContextAware] ApplicationContextAware.setApplicationContext调用.Bean:org.springframework.context.annotation.CommonAnnotationBeanPostProcessor@3027f7ce
2020-12-17 17:01:08 INFO [c.a.s.i.InitTestBean] 调用@DefPostConstruct注释的方法.
2020-12-17 17:01:08 INFO [c.a.s.i.InitTestBean] 调用InitializingBean的afterPropertiesSet方法.
2020-12-17 17:01:08 INFO [c.a.s.i.InitTestBean] 调用init-method的initMethod方法.

??可以看到,自定义注解同样达到了与@PostConstruct注解相同的效果。

??总结

??本文对@PostConstruct的应用进行了演示,同时一并演示了InitializingBeaninit-method,并通过示例验证了三者之间的调用顺序,充分了解这点,还是十分有用的。

??源码解析基于spring-framework-5.0.5.RELEASE版本源码。

??若文中存在错误和不足,欢迎指正!

  相关解决方案