简介

  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);
    
    @Override
    public void afterPropertiesSet() throws Exception {
        logger.info("调用InitializingBean的afterPropertiesSet方法.");
    }
    
    public void initMethod() throws Exception {
        logger.info("调用init-method的initMethod方法.");
    }
    
    @PostConstruct
    public 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);

    @Override
    public 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);
    
    @Override
    public void afterPropertiesSet() throws Exception {
        logger.info("调用InitializingBean的afterPropertiesSet方法.");
    }
    
    public void initMethod() throws Exception {
        logger.info("调用init-method的initMethod方法.");
    }
    
    @DefPostConstruct
    public void postConstructor() throws Exception {
        logger.info("调用@PostConstruct注释的方法.");
    }

}

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

<!-- 配置 ApplicationContextAware -->
<bean
	class="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版本源码。

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