这篇文章笔者和大家来聊一聊Spring中的两个标签配置范围,即singleton和prototype,前者意味单例的,后者则是多例的。
Scope取值 | 实例化个数 | 实例化时机 | 对象创建 | 对象运行 | 对象销毁 |
singleton(单例,饿汉式) | 1个 | 当Spring文件被加载时,实例话配置的Bean实例(只创建一次) | 创建容器时,对象向就被创建 | 容器在,对象一直活着 | 容器销毁,对象被销毁 |
prototype(多例,懒汉式) | 多个 | 当调用getBean()方法时实例化Bean(每次从容器中获得Bean时都会创建新的实例) | 当使用对象时,创建新的实例 | 对象在使用,对象就一直活着 | 对象长时间不用,对象被销毁 |
由此可见:作用域在单例模式下,对象和容器密切相连,且对象只创建一次(首次才创建),容器创建,运行,销毁,对象也跟着创建,运行,销毁;
作用域在多例模式下,就大不相同,对象在每次获得Bean的情况下会创建新的实例,对象的创建,运行,销毁都与对象是否被使用而挂钩。
以上笔者通过对两个标签 的介绍,接下来我们上代码,通过代码来联系上文,大家才能更好的理解。在笔者的代码中有懒加载的操作,笔者会通过延迟加载和测试类的运行结果来证明两个标签创建的实例。。
singleton单例模式:
笔者这里创建两次单例,第一次不加延迟加载的标签,第二次加入延迟加载的标签,我们来看看有何不同?
创建Eagle类
public class Eagle {
//属性
//无参构造和有参构造
//初始化方法
}
<?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 id="first" class="com.kaifamiao.ioc.scope.Eagle" init-method="init" scope="singleton">
<constructor-arg name="name" value="fist"/>
</bean>
<bean id="second" class="com.kaifamiao.ioc.scope.Eagle" init-method="init" scope="singleton" lazy-init="true">
<constructor-arg name="name" value="second"/>
</bean>
</beans>
package com.kaifamiao.ioc.scope;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author 后来的老李
* @version 1.0
*/
public class singletonTest {
private static String configs="classpath*:com/**/scope/singleton.xml";
private static AbstractApplicationContext context;
public @BeforeClass static void setUp(){
context = new ClassPathXmlApplicationContext(configs);
System.err.println( "- ".repeat( 30 ) );
}
public @Test void singletonTest1(){
Eagle a = context.getBean("first", Eagle.class);
Assert.assertNotNull( a );
System.out.println(a);
Eagle b= context.getBean("first", Eagle.class);
Assert.assertNotNull( b );
System.out.println(b);
Assert.assertEquals( a , b );
Assert.assertTrue( a == b );
}
public @Test void singletonTest2(){
Eagle a = context.getBean("second", Eagle.class);
Assert.assertNotNull( a );
System.out.println(a);
Eagle b = context.getBean("second", Eagle.class);
Assert.assertNotNull( b );
System.out.println(b);
Assert.assertEquals( a , b );
Assert.assertTrue( a == b );
}
public @AfterClass static void tearDown(){
context.close();
}
}
第一次我们用的是单例(single)且没有加入延迟加载,结果是:hash值是相同的说明是同一个对象,证明出单例模式下就算获得两次Bean也只创建一次对象。
接下来我们加入延迟加载,当 lazy-init 为 true 时为懒汉式,容器初始化时并不会 完成对该 bean 的实例化和初始化,首次从容器获取时才完成实例化和初始化。这里的首次和第二次我们看作当bean="second"时,测试类中的测试方法中testSingleton2创建的两个second.
这一次我们在单例模式下,加入了延迟加载,运行结果中两个hash值也是一样的,这说明什么?说明了在在延迟加载的情况下,第一个second的创建后是首次创建,对象创建一次,加入延迟加载,延迟的是什么?延迟的是第二个Bean的调用,但是因为单例模式下,对象只首次创建,第二次就不在创建了。
笔者希望大家能把单例模式下的两种理解清楚,那么多例就更容易理解了。接下来我们说一说,多例模式下的加入延迟加载又什么不同?
prototype多例模式:
老样子,笔者这里创建两次多例,第一次不加延迟加载的标签,第二次加入延迟加载的标签,我们来看看有何不同?
<?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:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="first" class="com.kaifamiao.ioc.scope.Eagle" scope="prototype" init-method="init">
<constructor-arg name="name" value="first"/>
</bean>
<bean id="second" class="com.kaifamiao.ioc.scope.Eagle" scope="prototype" init-method="init" lazy-init="true">
<constructor-arg name="name" value="second"/>
</bean>
</beans>
package com.kaifamiao.ioc.scope;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author 后来的老李
* @version 1.0
*/
public class prototypeTest {
private static String configs="classpath*:com/**/scope/prototype.xml";
private static AbstractApplicationContext context;
public @BeforeClass static void setUp(){
context = new ClassPathXmlApplicationContext(configs);
System.out.println("-".repeat(30));
}
public @Test void prototypeTest1(){
Eagle a = context.getBean("first", Eagle.class);
Assert.assertNotNull(a);
System.out.println(a.hashCode());
Eagle b = context.getBean("first", Eagle.class);
Assert.assertNotNull(b);
System.out.println(b.hashCode());
}
public @Test void prototypeTest2(){
Eagle a = context.getBean("second", Eagle.class);
Assert.assertNotNull(a);
System.out.println(a.hashCode());
Eagle b = context.getBean("second", Eagle.class);
Assert.assertNotNull(b);
System.out.println(b.hashCode());
}
public @AfterClass static void tearDown(){
context.close();
}
}
这一次我们在多例模式下没有加入延迟加载,当测试类两次调用Bean创建的对象的hash值不一样,这说明什么?在多例模式下,容器每次从容器中创建都会创建新的实例并初始化。
接下来我们再来看看,多例模式下,加入延迟加载结果又会有什么不同?
这次是在多例模式下,加入延迟加载,但是结果依然不同,为什么?我们就可以用多例模式下每次获得bean,都会创建的新的实例,既然每次都会创建新的实例,那么是不是就相当于每次都是第一次,每次都是新的,每次都不同,那么延迟加载是不是就对多例没有意义了呢。