Spring IOC(一)单例、非单例+容器关闭了,getBean获取的对象(小老弟)你咋还在蹦跶? day–07
一路绿灯学过去的就不记了,只记录重点和实验过程,另外内容顺序排列可能引起不适,但是是根我碰到问题顺序走的,,,在这向有一丁点可能看到这篇文章的您抱歉。
一、.Spring IOC 获取对象及Spring对Bean的生命周期控制(部分)
1.单利模式和非单例模式
1)单利模式下getBean获取对象以及对象创建时间
测试getBean获取的对象
默认单例模式
创建一个pojo (衣服类)包含颜色和大小 添加无参构造函数,打印内存地址
public class Clothes {
public Clothes()
{
System.out.println("一件衣服被创建了"+System.identityHashCode(this));
}
private String color;
private int size;
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public int getSize() {
return size;
}
public void setSize(int size) {
this.size = size;
}
public void destory(){
//System.identityHashCode(Object)方法可以返回对象的内存地址
System.out.println("这件衣服被销毁了"+System.identityHashCode(this));
}
}
resources目录下添加 applicationContext.xml (销毁时调用destory方法)
<?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.xsd">
<bean class="my.test.Clothes" id="clothes" destroy-method="destory" >
<property name="color" value="red"/>
<property name="size" value="33"/>
</bean>
</beans>
测试类
public class SpringTest {
public static void main(String args[])
{
ApplicationContext applicationContext =new ClassPathXmlApplicationContext(new String[]{"applicationContext.xml"});
Clothes clothes1 = (Clothes)applicationContext.getBean("clothes");
System.out.println(clothes1.getColor()+clothes1.getSize());
Clothes clothes2 = (Clothes)applicationContext.getBean("clothes");
System.out.println(clothes2.getColor()+clothes2.getSize());
((ClassPathXmlApplicationContext) applicationContext).close();
}
}
打印结果
一件衣服被创建了1210830415
red33
red33
这件衣服被销毁了1210830415
进程完成,退出码 0
设置clothes1 的颜色为 Green
System.out.println(clothes.getColor()+clothes.getSize());
clothes1.setColor("Green");
打印结果
一件衣服被创建了282265585
red33
Green33
这件衣服被销毁了282265585
发现修改clothes1 的属性 再次获取 clothes2 并不会因为clothes1 做出修改而给出新实例。
测试结果:scope 设置为 singleton 一个容器中 只要获取这个实例返回的都是同一个对象
测试对象是在getBean时候创建还是容器初始化时创建
修改上述java代码,在容器创建后打印一行字段
ApplicationContext applicationContext =new ClassPathXmlApplicationContext(new String[]{"applicationContext.xml"});
System.out.println("容器被创建完成");
输出结果
一件衣服被创建了282265585
容器被创建完成
red33
Green33
这件衣服被销毁了282265585
默认情况下lazy-init 为false 容器被创建后自动加载Bean并实例化。
设置lazy-int 为true
<bean class="my.test.Clothes" id="clothes" lazy-init="true" destroy-method="destory" >
运行结果
容器被创建完成
一件衣服被创建了1740846921
red33
Green33
这件衣服被销毁了1740846921
思考:
单例模式下,一个bean对应一个对象,可以对每一个对象进行实例化,那么非单利模式下呢 lazy-init 属性是否还会生效呢? 在下一步进行测试。
单例模式下生命周期,在非单例模式下(2)中有很多描述,在那里碰到的问题引发的思考 往那插眼 忘记了就tp过去
2)非单利模式下getBean获取对象以及对象创建时间
当scope 设置成 propert 每次getBean Spring都会new一个Bean实例,不会负责Bean实例生命周期的维护
设置scope="prototype"
<bean class="my.test.Clothes" id="clothes" scope="prototype" destroy-method="destory" >
<property name="color" value="red"/>
<property name="size" value="33"/>
</bean>
测试代码(回到最初的起点)
ApplicationContext applicationContext =new ClassPathXmlApplicationContext(new String[]{"applicationContext.xml"});
Clothes clothes1 = (Clothes)applicationContext.getBean("clothes");
System.out.println(clothes1.getColor()+clothes1.getSize());
clothes1.setColor("Green");
Clothes clothes2 = (Clothes)applicationContext.getBean("clothes");
System.out.println(clothes2.getColor()+clothes2.getSize());
((ClassPathXmlApplicationContext) applicationContext).close();
结果
一件衣服被创建了1833848849
red33
一件衣服被创建了252480153
red33
这里命名设置了destroy-method=“destory”,为什么容器关闭对象却没有销毁呢?对象还在吗?
在容器关闭以后加入对clothes1的打印
((ClassPathXmlApplicationContext) applicationContext).close();
System.out.println("我是clothes1 我还活着哦,看我的内存地址:"+System.identityHashCode(clothes1));
打印结果
一件衣服被创建了1833848849
red33
一件衣服被创建了252480153
red33
我是clothes1 我还活着哦,看我的内存地址:1833848849
非单例模式容器关闭并没有调用destory-method指定的方法,多例模式下Spring不负责销毁(管理Bean实例生命周期),所以没有调用destroy方法,Spring 容器关闭了可是却没有销毁对象clothes1 这个货仍然在内存中。
destory-method指定Bean销毁前所做的操作
看到这想到单例模式了,单例情况下呢,测试结果如下
一件衣服被创建了282265585
red33
Green33
这件衣服被销毁了282265585
我是clothes1 我还活着哦,看我的内存地址:282265585
进程完成,退出码 0
这里可以看到 destory-method指定的方法已经被执行了,那么下面的对象为什么没有被销毁呢?
这里可能是理解上出了问题,好多资料上说,单例模式下Spring负责管理Bean实例的生命周期,这种说话方式让我这种对Spring理解不深的人陷入那无尽的深渊,比如这样一段话:对于singleton作用域的Bean,客户端的每次请求都返回同一个Bean实例,客户端代码不能控制Bean的销毁,它的生命周期都在Spring的掌握之中。这么一来,容器就可以管理实例化结束后(某些资源的申请)和销毁之前(进行某些资源的回收)的行为。
这里很关键的一句客户端代码不能控制Bean的销毁下面测试
ApplicationContext applicationContext =new ClassPathXmlApplicationContext(new String[]{"applicationContext.xml"});
Clothes clothes1 = (Clothes)applicationContext.getBean("clothes");
System.out.println(clothes1.getColor()+clothes1.getSize());
clothes1.setColor("Green");
//设置clothes1 为null
clothes1=null;
Clothes clothes2 = (Clothes)applicationContext.getBean("clothes");
System.out.println(clothes2.getColor()+clothes2.getSize());
// ((ClassPathXmlApplicationContext) applicationContext).destroy();
((AbstractApplicationContext)applicationContext).registerShutdownHook();
((ClassPathXmlApplicationContext) applicationContext).close();
System.out.println("我是clothes1 我还活着哦,看我的内存地址:"+System.identityHashCode(clothes1));
这里设置clothes1为null,(客户端的每次请求都返回同一个Bean实例) 也就是clothes1是 Bean的唯一实例化对象,将clothes1设置成null 这个唯一的bean实例化对象就变成了null?? 上面也说(客户端代码不能控制Bean的销毁,它的生命周期都在Spring的掌握之中),是不是将clothes1设置成null,这个单例Bean并没有变为null呢 看结果:
测试结果
一件衣服被创建了282265585
red33
Green33
这件衣服被销毁了282265585
我是clothes1 我还活着哦,看我的内存地址:0
结果可以看出clothes1 确实被设置成null,clothes2再次获取这个Bean 依然可以获取到,这是为什么呢? 其实上面的话很容易误导新人,我这样的。稍微思考下 getBean返回的是这个Bean的Java实例的一个引用地址? 这么正确理解上面那句话呢?(也不一定正确我是这么理解的)
参考
//Spring容器创建时来创建c1
Clothes c1=new Clothes();
//getBean时候给c2 赋予c1指向的地址 (堆栈思考)
Clothes c2=c1;
// c2 指向空
c2=null;
// c3 和 c1 指向同一块内存地址
Clothes c3=c1;
//c2指向 为null
System.out.println(c2);
//c2 和c1 指向相同
System.out.println(c3);
总结:
c1单例 被 Spring 来管理,客户端无法操作c1 但能通过引用来修改其内部的信息。
再进一步思考:假如设置lazy-init=“true” 是不是相当于 c2 = new Clothes(); 再执行c2 = null; 是不是到时候 就 没有任何对象指向这个 实例的Bean(new Clothes()) 也就是这个单例Bean 就会丢失呢 ?
答案是不会的,即使设置成lazy-init=“true” getBean 时实例化这个bean,也是先在容器内创建一个对象再给你而不是容器本身不包含这对象引用(也就是还是会走 先创建c1 给Spring管理),直接给你(c2)。有点绕口不过很容易理解了吧。
记住上面这个例子,下面马上用奥?
下面是从一篇对Bean理解很有帮助的文章中截取部分字段
原文地址
1)管理Bean的生命周期行为主要有两个时机:注入依赖关系后,销毁实例之前
2)Spring 的 Bean 和 JavaBean比较:
规范:Spring容器对Bean 没有特殊要求,不像JavaBean 一样遵循一些规范(为每个属性提供相应的setter 和 getter 方法),不过对于设值注入的Bean,一定要提供setter 方法。
作用:Spring 中的Bean 是 java 实例,java组件,它的作用几乎无所不包,任何应用组件都被称为Bean,而传统的Java应用中的JavaBean通常作为DTO(数据传输对象),来封装值对象,在各层之间传递数据。
传统的JavaBean作为值对象传递,不接受任何容器管理其生命周期,Spring中的Bean有Spring管理其生命周期行为。
Spring中的Bean由Spring管理其生命周期行为+Spring 中的Bean 是 java 实例
这两句加起来很容易让人联想到java实例化对象的创建和销毁,都是由Spring来完成的,那么为什么还会看到容器已经关闭,getBean获取的数据还在蹦跶。
两个知识点:
1)关闭容器只会调用设定的destroy方法。
2)对象从内存中清除是GC来完成的,保留这个对象的引用,怎么可能会被销毁!!!
原谅我会思考这个问题这么久:哈哈哈
上面示例拷过来
//Spring容器创建时来创建c1
Clothes c1=new Clothes();
//getBean时候给c2 赋予c1指向的地址 (堆栈思考)
Clothes c2=c1;
// c2 指向空
c2=null;
// c3 和 c1 指向同一块内存地址
Clothes c3=c1;
//c2指向 为null
System.out.println(c2);
//Spring 容器关闭
c1=null;
//c2 和c1 指向相同
System.out.println(c3);
跟上面一样的比方:Spring容器创建 创建c1 c1有指向对象 ,下面c3也指向c1的指向对象(这时候c3 c1共用同一个对象,有点邪恶) Sprng 容器关闭(c1) 这时候我们怎么找到那个对象呢? 当然可以用c3来找啊,当然,如果这个对象没人要,这个对象就将会被GC销毁(引发思考:单身狗这么死亡的吗???)