Spring Init method 和 destroy method 的几种使用方式

1、@Bean注解方式

public class TestBean {
    public TestBean() {
        System.out.println("constructor");
    }

	/**
	* 初始化的方法名称不一定要使用init,
	* 使用init主要是为了使类结构更清晰,并且不会让人误以为此方法是一个普通方法
	*/
    public void init() {
        System.out.println("init");
    }

	/**
	* 销毁的方法名不一定要使用destroy
	*/    
    public void destroy() {
        System.out.println("destroy");
    }
}
  • 添加配置类
    使用此配置类,将bean添加到IoC容器中
    注意@Bean注解中的 initMethod = "init"destroyMethod = "destroy"initdestroy是TestBean 类中的定义的初始化和销毁所对应的方法名
@Configuration
public class BeanConfig {
	/**
	* 如果没有在@Bean注解中指定bean的名称,默认bean的名称是 方法名 也就是 testBean 
	* ps: 可以通过注入ApplicationContext调用getBeanDefinitionNames()方法来获取所有IoC容器中bean的名称
	*/
    @Bean(initMethod = "init", destroyMethod = "destroy")
    public TestBean testBean() {
        return new TestBean();
    }
    
}

运行结果

2021-03-29 11:25:56.426  INFO 10884 --- [           main] com.example.demo.DemoApplication         : No active profile set, falling back to default profiles: default
constructor
init
2021-03-29 11:25:56.845  INFO 10884 --- [           main] com.example.demo.DemoApplication         : Started DemoApplication in 0.817 seconds (JVM running for 1.339)
destroy

2、 使用@PostConstruct和@PreDestroy

public class TestBean {
    public TestBean() {
        System.out.println("constructor");
    }

    @PostConstruct
    public void init() {
        System.out.println("init");
    }

    @PreDestroy
    public void destroy() {
        System.out.println("destroy");
    }
}

配置类

@Configuration
public class BeanConfig {

    @Bean
    public TestBean testBean() {
        return new TestBean();
    }
}

运行结果

2021-03-29 11:32:21.130  INFO 11056 --- [           main] com.example.demo.DemoApplication         : No active profile set, falling back to default profiles: default
constructor
init
2021-03-29 11:32:21.530  INFO 11056 --- [           main] com.example.demo.DemoApplication         : Started DemoApplication in 0.677 seconds (JVM running for 1.217)
destroy

3、xml配置方式

在spring的xml配置文件中进行如下配置

<bean id="testBean" class="com.example.demo" init-method="init" destroy-method="destroy">

可使用以下方式运行

public class MainTest {
	public static void main(String[] args) {
		ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
		context.close();
	}
}



如果不是SpringBoot的项目,可使用如下方式获取应用上下文

public class MainTest {
	public static void main(String[] args) {
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(BeanConfig.class);
		context.close();
	}
}





init与InitializingBean

工作中经常使用到 InitializingBean 接口
实现此接口就要实现其抽象方法:afterPropertiesSet。在此方法中可进行另外的初始化工作,那么init方法和afterPropertiesSet方法的调用顺序是怎样的呢?

1、 使用 @PostConstruct 和 @PreDestroy
public class TestBean implements InitializingBean {

    static {
        System.out.println("静态块");
    }

    private String testStr;

    @Value("${test.str:hello}")
    public void setTestStr(String testStr) {
        System.out.println("set : hello");
        this.testStr = testStr;
    }

    public TestBean() {
        System.out.println("constructor");
    }

    @PostConstruct
    public void init() {
        System.out.println("post construct");
    }

    @PreDestroy
    public void destroy() {
        System.out.println("pre destroy");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("afterPropertiesSet " + this.testStr);
    }
}
@Configuration
public class BeanConfig {

    @Bean
    public TestBean testBean() {
        return new TestBean();
    }
}

运行结果

2021-03-29 14:01:33.594  INFO 10252 --- [           main] com.example.demo.DemoApplication         : No active profile set, falling back to default profiles: default
静态块
constructor
set : hello
post construct
afterPropertiesSet hello
2021-03-29 14:01:33.988  INFO 10252 --- [           main] com.example.demo.DemoApplication         : Started DemoApplication in 0.684 seconds (JVM running for 1.278)
pre destroy

执行的顺序是:
static block(加载类结构) > constructor(执行构造方法进行实例化) > auto wire(为成员变量赋值) > post constructor method > afterPropertiesSet > pre destroy method



2、在@Bean注解中进行配置
public class TestBean implements InitializingBean {

    static {
        System.out.println("静态块");
    }

    private String testStr;

    @Value("${test.str:hello}")
    public void setTestStr(String testStr) {
        System.out.println("set : hello");
        this.testStr = testStr;
    }

    public TestBean() {
        System.out.println("constructor");
    }

    public void init() {
        System.out.println("init");
    }

    public void destroy() {
        System.out.println("destroy");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("afterPropertiesSet " + this.testStr);
    }
}
@Configuration
public class BeanConfig {

    @Bean(initMethod = "init", destroyMethod = "destroy")
    public TestBean testBean() {
        return new TestBean();
    }
}
2021-03-29 14:11:56.940  INFO 6308 --- [           main] com.example.demo.DemoApplication         : No active profile set, falling back to default profiles: default
静态块
constructor
set : hello
afterPropertiesSet hello
init
2021-03-29 14:11:57.345  INFO 6308 --- [           main] com.example.demo.DemoApplication         : Started DemoApplication in 0.687 seconds (JVM running for 1.249)
destroy

执行的顺序是:
static block(加载类结构) > constructor(执行构造方法进行实例化) > auto wire(为成员变量赋值) > afterPropertiesSet > init method > destroy method



3、尝试1和2这两种方式同时使用
public class TestBean implements InitializingBean {

    static {
        System.out.println("静态块");
    }

    private String testStr;

    @Value("${test.str:hello}")
    public void setTestStr(String testStr) {
        System.out.println("set : hello");
        this.testStr = testStr;
    }

    public TestBean() {
        System.out.println("constructor");
    }

    @PostConstruct
    public void postConstruct() {
        System.out.println("postConstruct");
    }

    public void init() {
        System.out.println("init");
    }

    @PreDestroy
    public void preDestroy() {
        System.out.println("preDestroy");
    }

    public void destroy() {
        System.out.println("destroy");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("afterPropertiesSet " + this.testStr);
    }
}
@Configuration
public class BeanConfig {

    @Bean(initMethod = "init", destroyMethod = "destroy")
    public TestBean testBean() {
        return new TestBean();
    }
}

运行结果

2021-03-29 14:16:48.056  INFO 10724 --- [           main] com.example.demo.DemoApplication         : No active profile set, falling back to default profiles: default
静态块
constructor
set : hello
postConstruct
afterPropertiesSet hello
init
2021-03-29 14:16:48.435  INFO 10724 --- [           main] com.example.demo.DemoApplication         : Started DemoApplication in 0.657 seconds (JVM running for 1.244)
preDestroy
destroy


总结:

  • 定义bean的 init 和 destroy 有多种方式可供选择,既可以在@Bean注解中进行定义,也可以使用@PostConstruct@PreDestroy,大多数情况下可达到我们想要的效果。
  • @Bean(initMethod = "")在结合 InitializingBean 使用时,需要注意的是 Spring 会先调用 afterPropertiesSet 再调用 init method; 而如果使用的是 @PostConstruct 来定义 init method,顺序与前者相反,因为PostConstruct直译过来就是构造方法执行后
  1. constructor
  2. auto wire
  3. post construct
  4. afterPropertiesSet
  5. init method

这个细节需要注意一下,避免以后踩到坑。


附上一段InitializingBean相关的源代码

protected void invokeInitMethods(String beanName, final Object bean, RootBeanDefinition mbd) throws Throwable {
    //判断bean是否实现了InitializingBean接口
    boolean isInitializingBean = (bean instanceof InitializingBean);
    if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
        if (logger.isDebugEnabled()) {
            logger.debug("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
        }

        if (System.getSecurityManager() != null) {
            try {
                AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
                    public Object run() throws Exception {
                        //调用afterPropertiesSet
                        ((InitializingBean) bean).afterPropertiesSet();
                        return null;
                    }
                }, getAccessControlContext());
            } catch (PrivilegedActionException pae) {
                throw pae.getException();
            }
        } else {
            //调用afterPropertiesSet
            ((InitializingBean) bean).afterPropertiesSet();
        }
    }
    if (mbd != null) {
        String initMethodName = mbd.getInitMethodName();
        //判断是否指定了init-method
        if (initMethodName != null && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
                !mbd.isExternallyManagedInitMethod(initMethodName)) {
            invokeCustomInitMethod(beanName, bean, mbd);
        }
    }
}



BeanPostProcessor

另外,如果使用到了BeanPostProcessor

@Component
public class PostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if ("testBean".equals(beanName)) {
            System.out.println("------> beforeInitialization " + beanName);
        }

        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if ("testBean".equals(beanName)) {
            System.out.println("------> afterInitialization " + beanName);
        }
        return bean;
    }
}

运行结果:

2021-03-29 16:38:02.670  INFO 3336 --- [           main] com.example.demo.DemoApplication         : No active profile set, falling back to default profiles: default
静态块
constructor
set : hello
------> beforeInitialization testBean
postConstruct
afterPropertiesSet hello
init
------> afterInitialization testBean
2021-03-29 16:38:03.090  INFO 3336 --- [           main] com.example.demo.DemoApplication         : Started DemoApplication in 0.71 seconds (JVM running for 1.329)
preDestroy
destroy

postProcessBeforeInitialization方法的执行要先于@PostConstruct 所标注的方法
postProcessAfterInitialization 方法将在 init method 方法执行后被调用



参考自:
https://blog.youkuaiyun.com/tuzongxun/article/details/53580695
https://blog.youkuaiyun.com/weixin_39222112/article/details/89000959

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值