对象均有声明生命,而spring对对象的创建生命周期有spring控制,我们有必要其探究其对对象声明周期的创建,更有利于我们更为微观的掌握spring
生命周期:对象的创建、存活、消亡全过程
声明周期分为三个阶段
- 创建
- 初始化
- 销毁
1.对象创建
Product.java代码交代
package base.lifecycle;
/**
* @author yuhl
* @Date 2020/11/1 15:18
* @Classname Product
* @Description 对象生命周期
*/
public class Product {
String name;
Double price;
public Product() {
System.out.println("*****************Product.Product");
}
public Product(String name, Double price) {
this.name = name;
this.price = price;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
}
1.1单例模式对象的创建
- 配置文件
<!--生命周期-->
<bean id="product" class="base.lifecycle.Product"/>
- 测试
@Test
public void test16() {
ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");
}
- 结果
*****************Product.Product
说明单例模式下,在创建工厂时创建了对象。调用了构造函数。
1.2非单例模式对象的创建
配置文件:
<!--生命周期-->
<bean id="product" class="base.lifecycle.Product" scope="prototype"/>
测试:
@Test
public void test16() {
ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");
}
结果:
在创建工厂的时候没有创建对象。
但是可以在获得对象的时候创建对象
测试:
@Test
public void test16() {
ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");
Product product = (Product) ctx.getBean("product");
//System.out.println(product);
/*Account account2 = (Account) ctx.getBean("account");
System.out.println(account1);
System.out.println(account2);*/
}
此时的结果为:
*****************Product.Product
说明在获得对象的时候创建了对象,而不是在创建工厂的时候创建的。
1.3单例模式对象的创建延时加载
那么单例模式可否不让其在创建工厂的时候创建对象呢?让他在获得对象的时候创建?可以的使用 lazy-init=“true”。
配置文件:
<!--生命周期-->
<bean id="product" class="base.lifecycle.Product" lazy-init="true"/>
测试:
@Test
public void test16() {
ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");
// Product product = (Product) ctx.getBean("product");
//System.out.println(product);
/*Account account2 = (Account) ctx.getBean("account");
System.out.println(account1);
System.out.println(account2);*/
}
结果:未调用构造器
在获取的再次测试:
@Test
public void test16() {
ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");
Product product = (Product) ctx.getBean("product");
//System.out.println(product);
/*Account account2 = (Account) ctx.getBean("account");
System.out.println(account1);
System.out.println(account2);*/
}
结果:
*****************Product.Product
最终结论:在默认单例模式下,可以使用lazy-init="true"来延时加创建对象,待到真正使用的时候再去创建。
2.对象初始化
初始化就是spring给程序员以及机会,让我们有机会再对象创建完成后写一些自己的逻辑代码,比如多资源初始化、数据库、io网络等(但是实际开发中很少使用)。也就是说这个初始化方法我们程序员来定义,但是有spring框架去执行。
这里有两种写初始化的方法
2.1第一种:实现接口,重写方法
实现implements InitializingBean接口,重写:afterPropertiesSet()方法
- 看代码:
package base.lifecycle;
import org.springframework.beans.factory.InitializingBean;
/**
* @author yuhl
* @Date 2020/11/1 15:18
* @Classname Product
* @Description 对象生命周期
*/
public class Product implements InitializingBean {
String name;
Double price;
public Product() {
System.out.println("*****************Product.Product");
}
public Product(String name, Double price) {
this.name = name;
this.price = price;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("Product.afterPropertiesSet");
}
}
- 配置文件:
<bean id="product" class="base.lifecycle.Product"/>
- 测试:
@Test
public void test17() {
ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");
}
- 结果:
*****************Product.Product
Product.afterPropertiesSet
说明在初始化后调用了Product.afterPropertiesSet()方法
2.2第二种:自定义初始化方法,然后通过通配置文件告诉spring是那个方法
不实现spring提供的类,自定义一个初始化方法myInit()然后在配置文件中告诉spring即可。
- 实体类代码
package base.lifecycle;
import org.springframework.beans.factory.InitializingBean;
/**
* @author yuhl
* @Date 2020/11/1 15:18
* @Classname Product
* @Description 对象生命周期
*/
//public class Product implements InitializingBean {
public class Product {
String name;
Double price;
public Product() {
System.out.println("*****************Product.Product");
}
public Product(String name, Double price) {
this.name = name;
this.price = price;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
/* @Override
public void afterPropertiesSet() throws Exception {
System.out.println("Product.afterPropertiesSet");
}*/
/**
* 自定义的init方法,不在实现接口
* @throws Exception
*/
public void myInit() throws Exception {
System.out.println("Product.myInit");
}
}
- 配置文件:
<bean id="product" class="base.lifecycle.Product" init-method="myInit"/>
注意此处的init-method=“myInit”
- 运行结果:
*****************Product.Product
Product.myInit
2.3上面两个方式均调用了初始化方法,如果均有,则调用先后顺序怎样?
- 类代码:
package base.lifecycle;
import org.springframework.beans.factory.InitializingBean;
/**
* @author yuhl
* @Date 2020/11/1 15:18
* @Classname Product
* @Description 对象生命周期
*/
public class Product implements InitializingBean {
//public class Product {
String name;
Double price;
public Product() {
System.out.println("*****************Product.Product");
}
public Product(String name, Double price) {
this.name = name;
this.price = price;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("Product.afterPropertiesSet");
}
/**
* 自定义的init方法,不在实现接口
* @throws Exception
*/
public void myInit() throws Exception {
System.out.println("Product.myInit");
}
}
- 配置文件
<bean id="product" class="base.lifecycle.Product" init-method="myInit"/>
- 测试
*****************Product.Product
Product.afterPropertiesSet
Product.myInit
- 结论
先调用afterPropertiesSet() 再调用myInit()方法。
2.4如果再加上对成员变量的注入,则先后顺序怎样呢?
先给出结论,afterPropertiesSet() 从方法明上也能看出,实在propertie set之后diay9ong的,即,先setter注入,然后afterPropertiesSet() 再调用myInit()方法。
证明:
- Product.java对setter方法添加打印日志
package base.lifecycle;
import org.springframework.beans.factory.InitializingBean;
/**
* @author yuhl
* @Date 2020/11/1 15:18
* @Classname Product
* @Description 对象生命周期
*/
public class Product implements InitializingBean {
//public class Product {
String name;
Double price;
public Product() {
System.out.println("*****************Product.Product");
}
public Product(String name, Double price) {
this.name = name;
this.price = price;
}
public String getName() {
return name;
}
public void setName(String name) {
System.out.println("Product.setName");
this.name = name;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
System.out.println("Product.setPrice");
this.price = price;
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("Product.afterPropertiesSet");
}
/**
* 自定义的init方法,不在实现接口
* @throws Exception
*/
public void myInit() throws Exception {
System.out.println("Product.myInit");
}
}
- 测试据结果:
*****************Product.Product
Product.setName
Product.setPrice
Product.afterPropertiesSet
Product.myInit
印证了结论。
3.对象销毁
- 销毁和初始化一样可以实现接口DisposableBean重写destroy()方法,或者自定义销毁方法Mydestroy() ,通过配置文件告诉spring。
- 那么在什么时候会调用这个销毁方法呢?在ClassPathXmlApplicationContext.close()的时候会调用销毁方法。
- 注意因为ApplicationContext类中没有close()方法,所以需要声明为ClassPathXmlApplicationContext类,在嗲用close方法的时候先调用重写的销毁方法,还是先调用自己手写的销毁方法呢?
先给出答案,现代用spring的方法,再调用自己重写的销毁方法。
- 查看Product.java 实现这个接口:DisposableBean
package base.lifecycle;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
/**
* @author yuhl
* @Date 2020/11/1 15:18
* @Classname Product
* @Description 对象生命周期
*/
public class Product implements InitializingBean , DisposableBean {
//public class Product {
String name;
Double price;
public Product() {
System.out.println("*****************Product.Product");
}
public Product(String name, Double price) {
this.name = name;
this.price = price;
}
public String getName() {
return name;
}
public void setName(String name) {
System.out.println("Product.setName");
this.name = name;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
System.out.println("Product.setPrice");
this.price = price;
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("Product.afterPropertiesSet");
}
/**
* 自定义的init方法,不在实现接口
* @throws Exception
*/
public void myInit() throws Exception {
System.out.println("Product.myInit");
}
@Override
public void destroy() throws Exception {
System.out.println("Product.destroy");
}
public void Mydestroy() throws Exception {
System.out.println("Product.Mydestroy");
}
}
- 配置文件
<!--生命周期-->
<bean id="product" class="base.lifecycle.Product" init-method="myInit" destroy-method="Mydestroy" scope="singleton">
<property name="name" value="张三"/>
<property name="price" value="5.00"/>
</bean>
- 测试
@Test
public void test18() {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");
ctx.close();
}
- 测试结果
*****************Product.Product
Product.setName
Product.setPrice
Product.afterPropertiesSet
Product.myInit
2020-11-01 20:05:48 DEBUG ClassPathXmlApplicationContext:987 - Closing org.springframework.context.support.ClassPathXmlApplicationContext@2d6a9952, started on Sun Nov 01 20:05:48 CST 2020
Product.destroy
Product.Mydestroy
从这个结果可看出先调用springg的销毁方法,再调用自定义的方法。
5. 注意:对于销毁的方法,仅对于singleton对象有效,对于prototype无效,因为prototype对象有jvm控制其销毁过程spring生命周期不管他。
4.总结
通过上面的分析我们知道spring对象的声明周期分为三个阶段
- 创建
- 初始化
- 销毁
具体调用的方法如下图。便于记忆和理解。