Java Spring 中的单例和原型 Bean 范围
Bean Scopes指的是 Bean 的生命周期,这意味着 Bean 的对象何时被实例化,该对象存在多长时间,以及在整个过程中将为该 bean 创建多少对象。基本上,它控制 bean 的实例创建,并由 spring 容器管理。
Spring 中的 Bean 作用域
spring 框架为一个 bean 提供了五个作用域。我们只能在 web 感知Spring ApplicationContext的上下文中使用其中的三个,其余的两个可用于IoC 容器和 Spring-MVC 容器。以下是为 bean 提供的不同范围:
- 单例:每个 Spring IoC 容器只会为单个 bean 定义创建一个实例,并且为对该 bean 发出的每个请求共享相同的对象。
- 原型:每次对该 bean 发出请求时,都会为单个 bean 定义创建一个新实例。
- 请求:每次对该 bean 发出 HTTP 请求时,都会为单个 bean 定义创建一个新实例。但仅在 Web 感知 Spring ApplicationContext 的上下文中有效。
- 会话:将单个 bean 定义限定为 HTTP 会话的生命周期。但仅在 Web 感知 Spring ApplicationContext 的上下文中有效。
- Global-Session:将单个 bean 定义限定为全局 HTTP 会话的生命周期。它也仅在 Web 感知 Spring ApplicationContext 的上下文中有效。
让我们详细看看其中的一些:
单例范围:
如果作用域是单例,那么每个 Spring IoC 容器只会实例化该 bean 的一个实例,并且每个请求都将共享同一个实例。也就是说,当一个 bean 的范围被声明为单例时,每当对该 bean 发出新请求时,spring IOC 容器首先检查该 bean 的实例是否已经创建。如果它已经创建,则 IOC 容器返回相同的实例,否则它仅在第一次请求时创建该 bean 的新实例。默认情况下,bean 的范围是单例。
让我们通过一个例子来理解这个范围。
- Step1:让我们首先创建一个bean(即),它是spring框架中应用程序的主干。
Java
// Java program to illustrate a bean
// created in the spring framework
package bean;
public class HelloWorld {
public String name;
// Create a setter method to
// set the value passed by user
public void setName(String name)
{
this.name = name;
}
// Create a getter method so that
// the user can get the set value
public String getName()
{
return name;
}
}
XML
< bean
id = "hw"
class= "bean.HelloWorld"
scope = "singleton" / >
Java
// Java program to illustrate
// the client to perform the
// request to the defined bean
package driver;
import org.springframework
.context.ApplicationContext;
import org.springframework
.context.support
.ClassPathXmlApplicationContext;
import bean.HelloWorld;
// Client Class to request the
// above defined bean
public class Client {
public static void main(String[] args)
{
// Load the Spring XML configuration
// file into IoC container
ApplicationContext
ap
= new ClassPathXmlApplicationContext(
"resources/spring.xml");
// Get the "HelloWorld" bean object
// and call getName() method
HelloWorld Geeks1
= (HelloWorld)ap.getBean("hw");
// Set the name
Geeks1.setName("Geeks1");
System.out.println(
"Hello object (hello1)"
+ " Your name is: "
+ Geeks1.getName());
// Get another "HelloWorld" bean object
// and call getName() method
HelloWorld Geeks2
= (HelloWorld)ap.getBean("hw");
System.out.println(
"Hello object (hello2)"
+ " Your name is: "
+ Geeks2.getName());
// Now compare the references to see
// whether they are pointing to the
// same object or different object
System.out.println(
"'Geeks1' and 'Geeks2'"
+ " are referring"
+ "to the same object: "
+ (Geeks1 == Geeks2));
// Print the address of both
// object Geeks1 and Geeks2
System.out.println(
"Address of object Geeks1: "
+ Geeks1);
System.out.println(
"Address of object Geeks2: "
+ Geeks2);
}
}
Java
// Java program to illustrate a bean
// created in the spring framework
package bean;
public class HelloWorld {
public String name;
// Create a setter method to
// set the value passed by user
public void setName(String name)
{
this.name = name;
}
// Create a getter method so that
// the user can get the set value
public String getName()
{
return name;
}
}
XML
< beans>
< bean
id = "hw"
class = "bean.HelloWorld"
scope = "prototype" / >
beans>
Java
// Java program to illustrate
// the client to perform the
// request to the defined bean
package driver;
import org.springframework
.context.ApplicationContext;
import org.springframework.context.support
.ClassPathXmlApplicationContext;
import bean.HelloWorld;
public class Client {
public static void main(String[] args)
{
// Load the Spring XML configuration
// file into IoC container
ApplicationContext ap
= new ClassPathXmlApplicationContext(
"resources/spring.xml");
// Get the "HelloWorld" bean object
// and call getName() method
HelloWorld Geeks1
= (HelloWorld)ap.getBean("hw");
// Set the name
Geeks1.setName("Geeks1");
System.out.println(
"Hello object (hello1)"
+ " Your name is: "
+ Geeks1.getName());
// Get another "HelloWorld" bean object
// and call getName() method
HelloWorld Geeks2
= (HelloWorld)ap.getBean("hw");
System.out.println(
"Hello object (hello2)"
+ "Your name is: "
+ Geeks2.getName());
// Now compare the references to see
// whether they are pointing to the
// same object or different object
System.out.println(
"'Geeks1' and 'Geeks2'"
+ "are referring "
+ "to the same object: "
+ (Geeks1 == Geeks2));
// Print the address of both
// object Geeks1 and Geeks2
System.out.println(
"Address of object Geeks1: "
+ Geeks1);
System.out.println(
"Address of object Geeks2: "
+ Geeks2);
}
}
- 第 2 步:现在,我们编写一个 Spring XML 配置文件“spring.xml”并配置上面定义的 bean。
XML
< bean
id = "hw"
class= "bean.HelloWorld"
scope = "singleton" / >
- 第三步:最后,编写一个驱动类“Client. Java”来请求上面的bean。
Java
// Java program to illustrate
// the client to perform the
// request to the defined bean
package driver;
import org.springframework
.context.ApplicationContext;
import org.springframework
.context.support
.ClassPathXmlApplicationContext;
import bean.HelloWorld;
// Client Class to request the
// above defined bean
public class Client {
public static void main(String[] args)
{
// Load the Spring XML configuration
// file into IoC container
ApplicationContext
ap
= new ClassPathXmlApplicationContext(
"resources/spring.xml");
// Get the "HelloWorld" bean object
// and call getName() method
HelloWorld Geeks1
= (HelloWorld)ap.getBean("hw");
// Set the name
Geeks1.setName("Geeks1");
System.out.println(
"Hello object (hello1)"
+ " Your name is: "
+ Geeks1.getName());
// Get another "HelloWorld" bean object
// and call getName() method
HelloWorld Geeks2
= (HelloWorld)ap.getBean("hw");
System.out.println(
"Hello object (hello2)"
+ " Your name is: "
+ Geeks2.getName());
// Now compare the references to see
// whether they are pointing to the
// same object or different object
System.out.println(
"'Geeks1' and 'Geeks2'"
+ " are referring"
+ "to the same object: "
+ (Geeks1 == Geeks2));
// Print the address of both
// object Geeks1 and Geeks2
System.out.println(
"Address of object Geeks1: "
+ Geeks1);
System.out.println(
"Address of object Geeks2: "
+ Geeks2);
}
}
- 输出:
Hello object (hello1) Your name is: Geeks1
Hello object (hello2) Your name is: Geeks1
'Geeks1' and 'Geeks2' are referring to the same object: true
Address of object Geeks1: bean.HelloWorld@627551fb
Address of object Geeks2: bean.HelloWorld@627551fb
- 说明:当我们使用“Geeks1”和“Geeks2”的引用调用getName()方法时,我们得到相同的输出。这意味着两个引用都在调用同一个对象的 getName() 方法。此外,当我们比较引用“Geeks1”和“Geeks2”时,输出为“true”,这意味着在“Geeks1”和“Geeks2”之间共享同一个对象。所以很明显,当我们第一次发出请求时,会创建一个新的 bean 实例(HelloWorld),并且对于每个新请求,都共享同一个对象。
原型范围:
如果范围被声明为原型,那么每次对该特定 bean 发出请求时,spring IOC 容器都会创建该 bean 的一个新实例。可以使用getBean()方法以编程方式向 bean 实例发出请求,也可以通过 XML 进行次要类型的依赖注入。通常,我们对所有有状态的 bean 使用原型范围,而对无状态 bean 使用单例范围。
让我们通过一个例子来理解这个范围:
- 第1步:让我们首先创建一个bean(即),它是spring框架中应用程序的主干。
Java
// Java program to illustrate a bean
// created in the spring framework
package bean;
public class HelloWorld {
public String name;
// Create a setter method to
// set the value passed by user
public void setName(String name)
{
this.name = name;
}
// Create a getter method so that
// the user can get the set value
public String getName()
{
return name;
}
}
- 第 2 步:现在,我们编写一个 Spring XML 配置文件“spring.xml”并配置上面定义的 bean。
XML
< beans>
< bean
id = "hw"
class = "bean.HelloWorld"
scope = "prototype" / >
beans>
- 第三步:最后,编写一个驱动类“Client. Java”来请求上面的bean。
Java
// Java program to illustrate
// the client to perform the
// request to the defined bean
package driver;
import org.springframework
.context.ApplicationContext;
import org.springframework.context.support
.ClassPathXmlApplicationContext;
import bean.HelloWorld;
public class Client {
public static void main(String[] args)
{
// Load the Spring XML configuration
// file into IoC container
ApplicationContext ap
= new ClassPathXmlApplicationContext(
"resources/spring.xml");
// Get the "HelloWorld" bean object
// and call getName() method
HelloWorld Geeks1
= (HelloWorld)ap.getBean("hw");
// Set the name
Geeks1.setName("Geeks1");
System.out.println(
"Hello object (hello1)"
+ " Your name is: "
+ Geeks1.getName());
// Get another "HelloWorld" bean object
// and call getName() method
HelloWorld Geeks2
= (HelloWorld)ap.getBean("hw");
System.out.println(
"Hello object (hello2)"
+ "Your name is: "
+ Geeks2.getName());
// Now compare the references to see
// whether they are pointing to the
// same object or different object
System.out.println(
"'Geeks1' and 'Geeks2'"
+ "are referring "
+ "to the same object: "
+ (Geeks1 == Geeks2));
// Print the address of both
// object Geeks1 and Geeks2
System.out.println(
"Address of object Geeks1: "
+ Geeks1);
System.out.println(
"Address of object Geeks2: "
+ Geeks2);
}
}
- 输出:
Hello object (hello1) Your name is: Geeks1
Hello object (hello2) Your name is: null
'Geeks1' and 'Geeks2' are referring to the same object: false
Address of object Geeks1: bean.HelloWorld@47ef968d
Address of object Geeks2: bean.HelloWorld@23e028a9
- 说明:当我们使用引用“Geeks1”和“Geeks2”调用getName () 方法时,我们会得到不同的输出,这意味着两个引用都在调用不同对象的 getName() 方法。此外,当我们比较引用“Geeks1”和“Geeks2”时,输出为“false”,这意味着两个引用都指向不同的对象。因此很明显,在对该 bean 发出的每个请求时都会创建一个新的 bean 实例 (HelloWorld)。
单例和原型之间的区别
Singleton | Prototype |
---|---|
Only one instance is created for a single bean definition per Spring IoC container | A new instance is created for a single bean definition every time a request is made for that bean. |
Same object is shared for each request made for that bean. i.e. The same object is returned each time it is injected. | For each new request a new instance is created. i.e. A new object is created each time it is injected. |
By default scope of a bean is singleton. So we don’t need to declare a been as singleton explicitly. | By default scope is not prototype so you have to declare the scope of a been as prototype explicitly. |
Singleton scope should be used for stateless beans. | While prototype scope is used for all beans that are stateful |