我刚开始学习Spring,读到了这个内容:
一个bean有它的作用域(scope),定义了它在应用程序中的存在方式。
Singleton:表示每个Spring IOC容器中只有一个bean定义对应一个对象实例。
Prototype:表示一个bean定义可以对应多个对象实例。
那么,“对象实例”是什么?
我刚开始学习Spring,读到了这个内容:
一个bean有它的作用域(scope),定义了它在应用程序中的存在方式。
Singleton:表示每个Spring IOC容器中只有一个bean定义对应一个对象实例。
Prototype:表示一个bean定义可以对应多个对象实例。
那么,“对象实例”是什么?
Prototype scope = 每次注入/查找时都会创建一个新对象。它将每次使用new SomeClass()
。
Singleton scope = (默认) 每次注入/查找时返回相同的对象。此处将实例化一个SomeClass
实例,然后每次返回它。
另请参阅:
让我们通过代码简单查找一下。
以下是一个默认为singleton
作用域的TennisCoach Bean。
@Component
@Scope("singleton")
public class TennisCoach implements Coach {
public TennisCoach(){
}
@Autowired
public void setFortuneService(FortuneService fortuneService) {
this.fortuneService = fortuneService;
}
@Override
public String getDailyWorkout() {
return "Practice your backhand volley";
}
@Override
public String getDailyFortune() {
return "Tennis Coach says : "+fortuneService.getFortune();
}
}
以下是一个原型作用域的TennisCoach Bean
@Component
@Scope("prototype")
public class TennisCoach implements Coach {
public TennisCoach(){
System.out.println(">> TennisCoach: inside default constructor");
}
@Autowired
public void setFortuneService(FortuneService fortuneService) {
System.out.println(">> Tennis Coach: inside setFortuneService");
this.fortuneService = fortuneService;
}
@Override
public String getDailyWorkout() {
return "Practice your backhand volley";
}
@Override
public String getDailyFortune() {
return "Tennis Coach says : "+fortuneService.getFortune();
}
}
以下是一个主类:
public class AnnotationDemoApp {
public static void main(String[] args) {
// read spring config file
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext("applicationContext.xml");
// get the bean from the spring container
Coach theCoach = context.getBean("tennisCoach",Coach.class);
Coach alphaCoach = context.getBean("tennisCoach",Coach.class);
// call a method on the bean
System.out.println("Are the two beans same :" + (theCoach==alphaCoach));
System.out.println("theCoach : " + theCoach);
System.out.println("alphaCoach: "+ alphaCoach);
context.close()
}
}
对于singleton作用域,输出结果如下:
Are the two beans same :true
theCoach : com.springdemo.TennisCoach@2a53142
alphaCoach: com.springdemo.TennisCoach@2a53142
对于原型作用域,输出结果为:
Are the two beans same :false
theCoach : com.springdemo.TennisCoach@1b37288
alphaCoach: com.springdemo.TennisCoach@1a57272
在上面所说的基础上,不要混淆Java中的单例模式。 根据Java规范,单例模式意味着每个JVM只会创建一个该bean的实例。 但是在Spring中,单例模式意味着每个应用程序上下文中只会创建一个该特定bean的实例。 因此,如果您的应用程序有多个上下文,则仍然可以为该bean创建多个实例。
它们都是创建性设计模式。
Singleton(单例)在第一次调用时创建一个新实例,并在后续调用中返回该实例。
Prototype(原型)每次都会返回一个新的实例。
例如: 假设我们已经定义了一个bean当您定义一个bean定义并将其作为单例进行作用域限定时, Spring IoC容器会创建该bean定义所定义的对象的唯一实例。 此单个实例存储在此类单例bean的缓存中,并且所有后续请求和 引用该命名bean都会返回缓存的对象...
accountDao
如下:<bean id="accountDao" class="" />
还有另外两个 bean 使用了这个 accountDao
bean。
<bean id="someBean" ref="accountDao" />
<bean id="anotherBean" ref="accountDao" />
Spring首先创建了accountDao
bean并将其缓存。然后对于someBean
和anotherBean
,它将提供相同的accountDao
实例。
注意:如果在bean定义中没有指定范围,则Singleton是默认范围。
原型范围:对于原型范围,每次请求bean时都会创建并返回一个新的bean实例。这类似于在java中为类调用new运算符。
例如:假设我们已经定义了一个accountDao
bean如下:
<bean id="accountDao" class="" scope="prototype"/>
还有另外两个bean,使用了这个accountDao
bean
<bean id="someBean" ref="accountDao" />
<bean id="anotherBean" ref="accountDao" />
对于someBean和anotherBean,Spring将返回accountDao对象的两个单独实例。
一个重要的区别是,对于原型(prototype)作用域,Spring不管理bean的完整生命周期,清理工作需要由客户端代码完成。
来自Spring文档:
Spring不管理原型bean的完整生命周期:容器实例化、配置和组装原型对象,并将其交给客户端,没有进一步记录该原型实例。因此,虽然初始化生命周期回调方法在所有对象上被调用而不管它们的作用域,但对于原型,配置的销毁生命周期回调不会被调用。客户端代码必须清理原型作用域对象并释放原型bean所持有的昂贵资源。
注意:如果其他bean引用并使用应用程序上下文调用,则将创建任何作用域的bean。
检查此示例代码。
public class PrototypeClass {
PrototypeClass()
{
System.out.println("prototype class is created"+this.toString());
}
}
for(int i=0;i<10;i++) {
PrototypeClass pct= (PrototypeClass) context.getBean("protoClass");
}
Bean定义是
<bean id="protoClass" class="Spring.PrototypeClass" scope="prototype</bean>
我现在把bean定义中的作用域(scope)改为singleton。在上下文初始化期间,构造函数仅被调用一次。接下来,我去掉了作用域属性(scope attribute),发现它的行为和singleton相同。
因此,根据上面的部分所述,每个bean定义都可以被视为一个类(从面向对象的角度)。根据您在其中定义的数据(例如作用域等),此类(或bean定义)可能只有一个对象实例(仅通过共享实例的单例作用域)或任何数量的对象实例(例如原型作用域,每次请求特定bean时都会创建一个新的bean实例)。当您创建一个bean定义时,您创建了一个用于创建由该bean定义定义的类的实际实例的配方。 bean定义是一个配方的概念很重要,因为它意味着,与类一样,您可以从单个配方创建许多对象实例。
public interface Coach {
public String getDailyWorkout();
public String getDailyFortune();
}
public class TrackCoach implements Coach {
private FortuneService fortuneService;
public TrackCoach(FortuneService fortuneService) {
this.fortuneService = fortuneService;
}
@Override
public String getDailyWorkout() {
return "Run a hard 5k";
}
@Override
public String getDailyFortune() {
return "Just Do it: " + fortuneService.getFortune();
}
}
public interface FortuneService {
public String getFortune();
}
public class HappyFortuneService implements FortuneService {
@Override
public String getFortune() {
return "Today is your lucky day!";
}
}
让我们使用 Xml 将这两个类进行连接,并将一个类的对象 bean 注入到另一个类中。让我们执行依赖注入。请注意,我们也可以使用 Java 注释来实现这一点。
<?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
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- Define your beans here -->
<!-- define the dependency -->
<bean id = "myFortuneService"
class = "com.luv2code.springdemo.HappyFortuneService">
</bean>
<bean id = "myCoach"
class = "com.luv2code.springdemo.TrackCoach"
scope = "singleton">
<!-- set up construction injection -->
<constructor-arg ref = "myFortuneService" />
</bean>
</beans>
scope = singleton
。
现在让我们定义一个BeanScopeDemoApp,它有我们的主方法。
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class BeanScopeDemoApp {
public static void main(String[] args) {
// load the spring configuration file
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext("beanScope-applicationContext.xml");
// retrieve bean from spring container
Coach theCoach = context.getBean("myCoach", Coach.class);
Coach alphaCoach = context.getBean("myCoach", Coach.class);
// check if they are the same
boolean result = (theCoach == alphaCoach);
// print out the results
System.out.println("\nPointing to the same object: " + result);
System.out.println("\nMemory location for theCoach: " + theCoach);
System.out.println("\nMemory location for alphaCoach: " + alphaCoach +"\n");
// close the context
context.close();
}
}
Pointing to the same object: true
Memory location for theCoach: com.luv2code.springdemo.TrackCoach@16515bb7
Memory location for alphaCoach: com.luv2code.springdemo.TrackCoach@16515bb7
scope = prototype
,保存并再次运行BeanScopeDemoApp。Pointing to the same object: false
Memory location for theCoach: com.luv2code.springdemo.TrackCoach@6d4d203d
Memory location for alphaCoach: com.luv2code.springdemo.TrackCoach@627fbcda
单例模式是在整个应用程序中都使用同一个实例。
原型模式是每次调用 getBean 方法时都会创建一个新的实例。