Spring容器可以管理singleton作用域的Bean的生命周期,可以调用创建、初始化、销毁等生命周期的方法。
对于prototype作用域的Bean,Spring容器只负责创建,创建后Bean的实例就交给客户端代码来管理,Spring容器不再跟踪其生命周期。
Bean的生命周期
1、检查此Bean是否有依赖的Bean,如果所依赖的Bean尚未实例化,则先实例化依赖的bean;
如果依赖的bean都已实例化,则调用此Bean的构造方法或工厂方法创建此Bean的实例
2、利用依赖注入注入所需依赖(给成员变量赋值)
3、如果Bean实现了BeanNameAware接口,则调用setBeanName()方法传入当前Bean的name。
4、如果Bean实现了BeanFactoryAware接口,则调用setBeanFactory()传入当前工厂实例的引用
5、如果Bean实现了ApplicationContextAware接口,则调用setApplicationContext()方法传入当前ApplicationContext实例的引用
6、如果BeanPostProcessor和Bean关联,则调用预初始化方法postProcessBeforeInitialzation()进行加工操作,Spring AOP即利用此实现。
7、容器前处理(可以实现一些自定义的操作)
8、如果BeanPostProcessor和Bean关联,则调用初始化方法postProcessAfterInitialization()。此时,Bean已经可以被正常使用了。
9、如果指定了作用域为singleton,则将实例放在Spring IoC的缓存池中,并触发Spring容器对该Bean的生命周期管理,如果指定作用域为prototype,则将该Bean交给调用者,由调用者管理该Bean的生命周期。
10、容器后处理(销毁bean的实例时要做的操作)
说明:
以上接口中,均只有一个方法。
并不建议让Bean实现多个接口,因为继承多个接口会使代码耦合较高。
容器前处理
将bean的实例放到spring容器之前做一些处理。
有3种方式可以实现容器前处理:
- 使用@PostConstruct标注要调用的方法
- 实现InitializingBean接口,在afterPropertiesSet()中写代码
- 在xml中使用init-method属性指定要调用的方法
容器后处理
在spring容器销毁bean的实例之前做一些处理。
同样有3种方式可以实现容器后处理:
- 使用@PreDestroy标注要调用的方法
- 实现DisposableBean接口,在destory()中写代码
- 在xml中使用destory-method属性指定要调用的方法
不管是容器前处理,还是容器后处理,这三种方式都可以一起使用。
一起使用时,都是先执行注解标注的方法,再执行接口中的方法,最后执行xml中指定的方法。
有的教程说 init-method <=> @PostConstruct , destroy-method <=> @PreDestroy,
如果只使用三种中的一种,这三种的效果是一样的,这句话是对的;
如果同时使用2种或3种,则执行有先后之分,这句话是错的。
lazy-init
<bean name="" class="" lazy-init="true" />
默认scope="singleton",spring容器启动时就实例化scope="singleton"的bean。
lazy-init配置是否延迟加载bean:
- true 延迟加载,需要时才创建
- false 默认值。不延迟加载,spring容器启动时就实例化scope="singleton"的bean
虽然所有<bean>都能配置lazy-init,但lazy-init属性只对scope="singleton"的<bean>起作用。
示例
依赖:
@Component @Scope("prototype") public class B { public B(){ System.out.println("正在创建B类实例..."); } }
为避免B类在spring容器启动时就创建实例,将其作用域设置为prototype。
主调类:
@Component public class A implements InitializingBean, DisposableBean { private B b; @Autowired public A(B b) { System.out.println("正在执行A的构造函数..."); this.b = b; } @PostConstruct public void postConstruct(){ System.out.println("正在执行@PostConstruct注解指定的方法..."); } @Override public void afterPropertiesSet() throws Exception { System.out.println("正在执行InitializingBean接口中的afterPropertiesSet()方法..."); } public void xmlInit(){ System.out.println("正在执行xml中init-method指定的方法..."); } @PreDestroy public void preDestroy(){ System.out.println("正在执行@PreDestroy注解指定的方法..."); } @Override public void destroy() throws Exception { System.out.println("正在执行DisposableBean接口中的destroy()方法..."); } public void xmlDestroy(){ System.out.println("正在执行xml中destroy-method指定的方法..."); } }
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" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <context:component-scan base-package="com.chy.bean"/> <bean name="a" class="com.chy.bean.A" init-method="xmlInit" destroy-method="xmlDestroy" /> </beans>
测试类:
public class Test { public static void main(String[] args) { //创建spring容器的实例(会实例化所有scope="singleton"的bean) ClassPathXmlApplicationContext applicationContext=new ClassPathXmlApplicationContext("spring-config.xml"); //关闭spring容器(会销毁所有spring容器中的bean实例) applicationContext.close(); } }
在基于Web的Spring容器中,WEB服务器(比如Tomcat)已经提供了对应的设置,在Web应用关闭时,会自动关闭Spring容器。
在非Web的Spring容器中,需要手动调用close()方法关闭spring容器。
ApplicationContext即Spring容器,是一个接口,没有声明close()方法,如果把spring容器声明为ApplicatioContext,则不能使用close()方法。
ApplicationContext的实现类提供了close()方法,要声明为实现类(比如ClassPathXmlApplicationContext)才能使用close()方法。
运行程序,控制台输出如下:
正在创建B类实例...
正在执行A的构造函数...
正在执行@PostConstruct注解指定的方法...
正在执行InitializingBean接口中的afterPropertiesSet()方法...
正在执行xml中init-method指定的方法...
正在执行@PreDestroy注解指定的方法...
正在执行DisposableBean接口中的destroy()方法...
正在执行xml中destroy-method指定的方法...
执行过程分析:
A依赖B,B是prototype,需要时才创建实例,A是singleton,spring容器启动时就创建实例。
(1)A所依赖的B尚未实例化,先创建B的实例
(2)创建A的实例,注入B的实例
(3)执行容器前处理(先注解,再接口,最后xml)
(4)A的作用域是singleton,将创建的A的实例放到spring容器中(如果是prototype,则不放到容器中)
(5)spring容器关闭,销毁spring容器中所有的bean,执行容器后处理,销毁A的实例。