InitialingBean和DisposableBean

InitialingBean是一个接口,提供了一个唯一的方法afterPropertiesSet()。

DisposableBean也是一个接口,提供了一个唯一的方法destory()。

这两个接口是一组的,功能类似,因此放在一起:前者顾名思义在Bean属性都设置完毕后调用afterPropertiesSet()方法做一些初始化的工作,后者在Bean生命周期结束前调用destory()方法做一些收尾工作。下面看一下例子,为了能明确地知道afterPropertiesSet()方法的调用时机,加上一个属性,给属性set方法,在set方法中打印一些内容:

public class LifecycleBean implements InitializingBean, DisposableBean
{
    @SuppressWarnings("unused")
    private String    lifeCycleBeanName;
    
    public void setLifeCycleBeanName(String lifeCycleBeanName)
    {
        System.out.println("Enter LifecycleBean.setLifeCycleBeanName(), lifeCycleBeanName = " + lifeCycleBeanName);
        this.lifeCycleBeanName = lifeCycleBeanName;
    }

    public void destroy() throws Exception
    {
        System.out.println("Enter LifecycleBean.destroy()");
    }

    public void afterPropertiesSet() throws Exception
    {
        System.out.println("Enter LifecycleBean.afterPropertiesSet()");
    }
    
    public void beanStart()
    {
        System.out.println("Enter LifecycleBean.beanStart()");
    }
    
    public void beanEnd()
    {
        System.out.println("Enter LifecycleBean.beanEnd()");
    }
}

配置一个spring.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-4.1.xsd">
    
    <bean id="lifeCycleBean" class="org.xrq.bean.lifecycle.LifecycleBean">
        <property name="lifeCycleBeanName" value="lifeCycleBean" />
    </bean>
</beans>

启动Spring容器,LifecycleBean执行的结果为:

Enter LifecycleBean.setLifeCycleBeanName(), lifeCycleBeanName = lifeCycleBean
Enter LifecycleBean.afterPropertiesSet()
Enter LifecycleBean.beanStart()
Enter LifecycleBean.destroy()
Enter LifecycleBean.beanEnd()

执行结果和我们想的一样,afterPropertiesSet()方法就如同它的名字所表示的那样,是在Bean的属性都被设置完毕之后,才会调用。

关于这两个接口,我总结几点:

1、InitializingBean接口、Disposable接口可以和init-method、destory-method配合使用,接口执行顺序优先于配置

2、InitializingBean接口、Disposable接口底层使用类型强转.方法名()进行直接方法调用,init-method、destory-method底层使用反射,前者和Spring耦合程度更高但效率高,后者解除了和Spring之间的耦合但是效率低,使用哪个看个人喜好

3、afterPropertiesSet()方法是在Bean的属性设置之后才会进行调用,某个Bean的afterPropertiesSet()方法执行完毕才会执行下一个Bean的afterPropertiesSet()方法,因此不建议在afterPropertiesSet()方法中写处理时间太长的方法

关于在spring  容器初始化 bean 和销毁前所做的操作定义方式还有以下种:

第一种:通过@PostConstruct和@PreDestroy注解实现初始化和销毁bean之前进行的操作。

注:@PostConstruct和@PreDestroy 标注不属于 Spring,它是在J2EE库- common-annotations.jar。

第二种:通过在xml中定义init-method和destory-method方法。

@PostConstruct 和 @PreDestroy

一个 CustomerService Bean使用 @PostConstruct 和 @PreDestroy 注释

package com.yiibai.customer.services;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

public class CustomerService
{
	String message;
	
	public String getMessage() {
	  return message;
	}

	public void setMessage(String message) {
	  this.message = message;
	}
	
	@PostConstruct
	public void initIt() throws Exception {
	  System.out.println("Init method after properties are set : " + message);
	}
	
	@PreDestroy
	public void cleanUp() throws Exception {
	  System.out.println("Spring Container is destroy! Customer clean up");
	}
	
}

默认情况下,Spring不会意识到@PostConstruct和@PreDestroy注解。要启用它,要么注册“CommonAnnotationBeanPostProcessor”,要么在bean配置文件的<context:annotation-config />‘ 指定

1. CommonAnnotationBeanPostProcessor

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

	<bean class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor" />

	<bean id="customerService" class="com.yiibai.customer.services.CustomerService">
		<property name="message" value="i'm property message" />
	</bean>
		
</beans>

2. <context:annotation-config />

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
	http://www.springframework.org/schema/context
	http://www.springframework.org/schema/context/spring-context-2.5.xsd">

	<context:annotation-config />

	<bean id="customerService" class="com.yiibai.customer.services.CustomerService">
		<property name="message" value="i'm property message" />
	</bean>
		
</beans>
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.yiibai.customer.services.CustomerService;

public class App 
{
    public static void main( String[] args )
    {
    	ConfigurableApplicationContext context = 
    	  new ClassPathXmlApplicationContext(new String[] {"Spring-Customer.xml"});
	
    	CustomerService cust = (CustomerService)context.getBean("customerService");
    	
    	System.out.println(cust);
    	
    	context.close();
    }
}

输出结果

Init method after properties are set : im property message
com.yiibai.customer.services.CustomerService@47393f
...
INFO: Destroying singletons in org.springframework.beans.factory.
support.DefaultListableBeanFactory@77158a: 
defining beans [customerService]; root of factory hierarchy
Spring Container is destroy! Customer clean up

initIt()方法(@PostConstruct)被调用时,消息属性设置后 cleanUp() 方法(@PreDestroy)是在context.close()执行后被调用;

Constructor  @Autowired  @PostConstruct 执行顺序:

其实从依赖注入的字面意思就可以知道,要将对象p注入到对象a,那么首先就必须得生成对象p与对象a,才能执行注入。所以,如果一个类A中有个成员变量p被@Autowired注解,那么@Autowired注入是发生在A的构造方法执行完之后的。

如果想在生成对象时候完成某些初始化操作,而偏偏这些初始化操作又依赖于依赖注入,那么就无法在构造函数中实现。为此,可以使用@PostConstruct注解一个方法来完成初始化,@PostConstruct注解的方法将会在依赖注入完成后被自动调用。

Constructor >> @Autowired >> @PostConstruct

Spring@PostConstruct注解和构造方法的调用顺序

父类class,被@component注解修饰,说明会被spring扫描并创建。在默认构造方法里加上输出打印,init方法被@PostConstruct修饰

1 @Component
 2 public class ParentBean implements InitializingBean{
 3     
 4     public ParentBean() {
 5         System.out.println("ParentBean construct");
 6     }
 7     
 8     @PostConstruct
 9     public void init(){
10         System.out.println("ParentBean init");
11     }
12 
13     public void afterPropertiesSet() throws Exception {
14         System.out.println("ParentBean afterPropertiesSet");
15     }
16 
17 }

子类class,也被 @component注解修饰,其余配置和父类class一样

1 @Component
 2 public class SonBean extends ParentBean{
 3     public SonBean() {
 4         System.out.println("SonBean construct");
 5     }
 6     
 7     @PostConstruct
 8     public void init(){
 9         System.out.println("SonBean init");
10     }
11     @Override
12     public void afterPropertiesSet() throws Exception {
13         System.out.println("SonBean afterPropertiesSet");
14     }
15     
16 }

然后我们使用maven命令 jetty:run,控制台输出如下:

spring 范型接口的实现类_System

[INFO] Initializing Spring root WebApplicationContext
ParentBean construct
ParentBean init
ParentBean afterPropertiesSet
ParentBean construct
SonBean construct
SonBean init
SonBean afterPropertiesSet
[INFO] Set web app root system property: 'webapp.root' = [J:\androidworkspace\com\src\main\webapp\]
[INFO] Initializing log4j from [classpath:log4j.properties]
[INFO] Started SelectChannelConnector@0.0.0.0:8088
[INFO] Started Jetty Server
[INFO] Starting scanner at interval of 3 seconds.

可以看出优先执行依然是构造方法,这个是java的语言决定的,毕竟spring只是建立在java之上的框架。然后才是被PostConstruct修饰的方法,要注意的是这个方法在对象的初始化和依赖都完成之后才会执行,所以不必担心执行这个方法的时候有个别成员属性没有被初始化为null的情况发生。在init方法之后执行的才是afterPropertiesSet方法,这个方法必须实现InitializingBean接口,这个接口很多spring的bean都实现了他,从他的方法名就能看出在属性都被设置了之后执行,也属于springbean初始化方法。