众所周知,默认情况下,我们在Spring容器中将bean作为单例,如果我们有一个基于Spring框架的Web应用程序,那么在这种情况下,我们真的需要实现Singleton设计模式来保存全局数据,而不仅仅是通过spring创建bean 。
如果我无法解释我实际上要问的问题,请忍受。
Answers:
Spring中的单例豆和单例模式有很大不同。Singleton模式表示,每个类加载器将只创建一个特定类的一个实例。
Spring单例的范围被描述为“每个容器每个bean”。这是每个Spring IoC容器的单个对象实例的bean定义范围。Spring的默认范围是Singleton。
即使默认范围是单例,您也可以通过指定<bean ../>
element 的scope属性来更改bean的范围。
<bean id=".." class=".." scope="prototype" />
Spring中的Singleton范围表示Spring上下文中的单个实例
。Spring容器仅一次又一次返回同一实例,以供后续调用以获取Bean。
而且,无论bean的类是否被编码为单例,spring都不会打扰;实际上,如果该类被编码为构造函数为私有的单例,Spring则使用BeanUtils.instantiateClass(此处为Javadoc )将构造函数设置为可访问并调用它。
另外,我们可以像这样在bean定义中使用factory-method属性
<bean id="exampleBean" class="example.Singleton" factory-method="getInstance"/>
让我们以最简单的示例为例:您拥有一个应用程序,而仅使用默认的类加载器。您有一个类,无论出于何种原因,您都决定该类在应用程序中不应有多个实例。(考虑一个由几个人共同处理应用程序的场景)。
如果您不使用Spring框架,则Singleton模式可确保您的应用程序中一个类的实例不超过一个。那是因为构造函数是私有的,因此无法通过执行“ new”来实例化类的实例。获取该类实例的唯一方法是调用该类的某些静态方法(通常称为“ getInstance”),该方法始终返回相同的实例。
说您在应用程序中使用Spring框架,仅意味着除了获取类实例的常规方法(返回类实例的新方法或静态方法)之外,您还可以要求Spring协助您该类的实例,Spring会确保即使您未使用Singleton模式编写该类,只要您向其要求该类的实例,它将始终返回相同的实例。换句话说,即使该类具有公共构造函数,如果您始终向Spring请求该类的实例,则Spring在应用程序生命周期内只会调用该构造函数一次。
通常,如果使用Spring,则应仅使用Spring创建实例,并且可以为该类提供一个公共构造函数。但是,如果您的构造函数不是私有的,那么您实际上并不会通过绕过Spring来阻止任何人直接创建该类的新实例。
如果您确实想要该类的单个实例,即使您在应用程序中使用Spring并将Spring中的类定义为单例,那么确保该类的唯一方法就是也使用Singleton模式实现该类。这样可以确保只有一个实例,无论人们使用Spring获取实例还是绕过Spring。
我发现“ 每个容器每个豆”很难理解。我会说“ 容器中的每个bean id一个bean ”。让我们举个例子来理解它。我们有一个bean类Sample。我在bean定义中从此类定义了两个bean,例如:
<bean id="id1" class="com.example.Sample" scope="singleton">
<property name="name" value="James Bond 001"/>
</bean>
<bean id="id7" class="com.example.Sample" scope="singleton">
<property name="name" value="James Bond 007"/>
</bean>
因此,当我尝试获取ID为“ id1”的bean时,spring容器将创建一个bean,将其缓存并在id1引用过的地方返回相同的bean。如果我尝试使用id7来获取它,那么将从Sample类创建另一个bean,每次您使用id7引用该bean时,都将对其进行缓存并返回。
单例模式不太可能。在Singlton模式中,总是为每个类加载器创建一个对象。但是在Spring中,将范围设为Singleton并不会限制容器从该类创建许多实例。它只是再次限制了相同ID的新对象的创建,当请求具有相同ID的对象时,将返回先前创建的对象。参考
Spring中的Singleton作用域意味着该bean将仅由Spring实例化一次。与原型范围(每次都有新实例)相反,请求范围(每个请求一次),会话范围(每个HTTP会话一次)。
从技术上讲,单例作用域与单例设计模式无关。您不必将bean实施为单例即可将它们放入单例范围。
Spring中的Singleton bean和基于Singleton设计模式的类完全不同。
单例模式可确保每个类加载器都只能创建一个特定类的一个实例,而Spring单例bean的范围被描述为“每个容器每个容器”。Spring中的Singleton作用域意味着该bean将仅由Spring实例化一次。Spring容器仅一次又一次返回相同的实例,以供后续调用以获取Bean。
两者之间有一个非常根本的区别。在使用Singleton设计模式的情况下,每个classLoader仅创建一个类的实例,而对于Spring Singleton,情况并非如此,因为在稍后的情况下,将为每个IoC容器创建给定ID的一个共享bean实例。
例如,如果我有一个名为“ SpringTest”的类,并且我的XML文件看起来像这样:-
<bean id="test1" class="com.SpringTest" scope="singleton">
--some properties here
</bean>
<bean id="test2" class="com.SpringTest" scope="singleton">
--some properties here
</bean>
因此,现在在主类中,如果您要检查上面两个的引用,则将根据Spring文档返回false:
当一个bean是单例时,将仅管理该bean的一个共享实例,并且对所有具有与该bean定义匹配的id或id的bean的请求都将导致Spring容器返回一个特定的bean实例。
因此,就像我们的情况一样,这些类是相同的,但是我们提供的id是不同的,因此导致创建了两个不同的实例。
EX:“每个容器每个豆”。
<bean id="myBean" class="com.spring4hibernate4.TestBean">
<constructor-arg name="i" value="1"></constructor-arg>
<property name="name" value="1-name"></property>
</bean>
<bean id="testBean" class="com.spring4hibernate4.TestBean">
<constructor-arg name="i" value="10"></constructor-arg>
<property name="name" value="10-name"></property>
</bean>
</beans>
public class Test {
@SuppressWarnings("resource")
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("ws.xml");
TestBean teatBean = (TestBean) ac.getBean("testBean");
TestBean myBean1 = (TestBean) ac.getBean("myBean");
System.out.println("a : " + teatBean.test + " : " + teatBean.getName());
teatBean.setName("a TEST BEAN 1");
System.out.println("uPdate : " + teatBean.test + " : " + teatBean.getName());
System.out.println("a1 : " + myBean1.test + " : " + myBean1.getName());
myBean1.setName(" a1 TEST BEAN 10");
System.out.println("a1 update : " + teatBean.test + " : " + myBean1.getName());
}
}
public class TestBean {
public int test = 0;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
private String name = "default";
public TestBean(int i) {
test += i;
}
}
JAVA SINGLETON:
public class Singleton {
private static Singleton singleton = new Singleton();
private int i = 0;
private Singleton() {
}
public static Singleton returnSingleton() {
return singleton;
}
public void increment() {
i++;
}
public int getInt() {
return i;
}
}
public static void main(String[] args) {
System.out.println("Test");
Singleton sin1 = Singleton.returnSingleton();
sin1.increment();
System.out.println(sin1.getInt());
Singleton sin2 = Singleton.returnSingleton();
System.out.println("Test");
sin1.increment();
System.out.println(sin1.getInt());
}
春季单例bean被描述为“每个容器每个容器”。Spring中的Singleton作用域意味着在相同内存位置的同一对象将返回到相同的bean id。如果创建多个具有相同类的不同ID的bean,则容器将使不同的对象返回不同的id。这就像一个键值映射,其中key是bean id,value是一个spring容器中的bean对象。其中,Singleton模式可确保每个类加载器只能创建一个特定类的一个实例。
到目前为止,所有答案至少都集中于解释设计模式和Spring单例之间的区别,而没有解决您的实际问题:应该使用Singleton设计模式还是使用Spring单例bean?什么是更好的?
在我回答之前,请允许我先声明一下,您可以同时进行。您可以将bean实现为Singleton设计模式,并使用Spring作为Spring singleton bean将其注入到客户端类中。
现在,问题的答案很简单:不要使用Singleton设计模式!
使用Spring的singleton bean作为带有公共构造函数的类来实现。
为什么?因为Singleton设计模式被认为是反模式。主要是因为它使测试变得复杂。(如果您不使用Spring注入它,那么所有使用单例的类现在都将紧紧地绑定到它),并且您不能替换或扩展它。可以在Google上搜索“ Singleton反模式”以获得更多信息,例如Singleton反模式
使用Spring singleton是可行的方法(使用Singleton bean不是作为Singleton设计模式实现的,而是使用公共构造函数实现的),以便可以轻松地测试Spring singleton bean,并且使用该类的类不会紧密耦合到它,但是,Spring将单例(作为接口)注入到需要它的所有Bean中,并且可以随时用另一种实现替换该单例Bean,而不会影响使用它的客户端类。