Scope of the Spring bean
By default, beans in the application context are created as singletons. That is, no matter how many times a given bean is injected into other beans, the same instance is injected each time.
In most cases, singleton beans are ideal and can be reused again and again. However, sometimes the bean we need is mutable, it will store some of its own state, if the singleton mode is used, the operation of the same bean every time, will pollute the bean, may have unexpected problems.
There are multiple scopes defined in Spring on which beans can be created.
- Singleton: Only one instance of the bean is created throughout the application.
- Prototype: A new bean instance is created each time it is injected or fetched from the Spring context.
- Session: In a Web application, one bean instance is created for each Session.
- Request: In a Web application, a bean instance is created for each Request.
Configure the scope of the bean
To specify the Scope of a bean, we can use the @scope annotation. If beans are discovered and declared by Component scanning, it can be used with the @Component annotation to declare them as configured scoped beans
@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)// Declare it as a prototype bean
public class Demo {... }Copy the code
If a bean is declared through a Java configuration class, it can be used with @Beans
@Bean
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Demo demo(a){
return new Demo();
}
Copy the code
To configure beans using XML, we can use the scope attribute of the
element to configure the scope of the bean
<bean id="demo" class="com.abiao.Demo" scope="prototype" />
Copy the code
The examples I’ve listed are declared prototype scopes that create new instances each time the bean is injected or retrieved from the Spring application context, so we get our own bean instance for each operation.
Session scope and request scope
Above we introduced the declaration of singleton and stereotype scope. However, in some scenarios we use the first two scopes, which are not appropriate. For example, implementing a shopping cart in a Web application.
With singleton scope, we add items to a cart every time we add to a cart, which doesn’t make sense. With a prototype, we create a shopping cart every time we add an item, and the next time we get the cart it won’t be the same and it won’t make sense. In terms of shopping cart beans, the session scope is most appropriate, creating a shopping cart for each user session.
Declare session scoped beans
To specify the session scoped bean, we also use the @scope annotation. In addition to @ Scope annotations to configure the value attribute, we also configured the proxyMode attributes (proxy mode), ScopedProxyMode. INTERFACES said dynamic proxy mode based on interface (JDK). Configuring proxyMode (proxyMode) can help us solve the problem of injecting session scoped beans into singleton beans.
@Component
@ the Scope (value = WebApplicationContext SCOPE_SESSION, / / constant values for the session proxyMode = ScopedProxyMode. INTERFACES)// Proxy mode based on interface implementation
public class Demo {... }Copy the code
Let’s take store and ShoppingCart as examples, store StoreService (singleton bean), ShoppingCart ShoppingCart (session bean). Inject the ShoppingCart Bean in the setShoppingCart() method of StoreService.
@Component
public class StoreService {
/** shopping cart */
private ShoppingCart shoppingCart;
@Autowired
public void setShoppingCart(ShoppingCart shoppingCart){
this.shoppingCart = shoppingCart; }}Copy the code
Because StoreService is a singleton bean, it is created when the Spring application context is loaded. At creation time, Spring tries to inject the ShoppingCart into the setShoppingCart() method. But the ShoppingCart is a session-scoped bean and does not exist at this time. The ShoppingCart instance does not appear until the user enters the system and creates a session.
The problem is that there are multiple ShoppingCarts in the system, one for each user who enters the system and creates a session. But StoreService is a singleton and can only be injected one, and we don’t want to inject a fixed ShoppingCart instance into StoreService. What we want is that when StoreService processes the ShoppingCart, the injected ShoppingCart is exactly the one that corresponds to the current session.
Instead of injecting a ShoppingCart Bean into StoreService, Spring injects a proxy for the ShoppingCart Bean. This agent exposes the same methods as ShoppingCart, so StoreService treats it like a ShoppingCart. When StoreService calls a ShoppingCart method, the proxy lazily parses it and delegates the call to the real ShoppingCart Bean in the session scope.
ProxyMode property setting
-
If the ShoppingCart is an interface, its implementation class @ Scope proxyMode attribute is set to ScopedProxyMode. INTERFACES, said the agency to implement ShoppingCart interface, and would be the delegate to the bean. This proxy pattern is also ideal.
-
If ShoppingCart were a class, Spring would not be able to create an interface-based proxy, in which case it would have to use CGLib to generate a class-based proxy. Set the proxyMode property to scopedProxymode.target_class and Spring generates the proxy to be created by extending the class based on the target class.
We’ve seen that session scopes can cause this kind of problem, and the same request-scoped beans face the same assembly problems. Therefore, requesting scoped beans should also be injected using scoped proxies.
Figure: The scoped agent delays the injection of request and session-scoped beans
Declaration of scoped proxies in XML
Instead of using the @scope annotation to declare the Scope proxy, we can use an XML file instead. Scopes can be declared directly in XML through the scope attribute, but declaring scoped proxies requires the use of an element of the Spring AOP namespace < AOP :scoped-proxy />. < AOP :scoped-proxy /> Creates a proxy for the target class in CGLib mode by default.
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation=" http://www.springframework.org/schema/aop http://www.springframework.org/schema/beans/spring-aop.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="cart"
class="com.abiao._03_scope.ShoppingCart"
scope="session">
<aop:scoped-proxy />
</bean>
</beans>
Copy the code
We can also have the <aop:scoped-proxy /> tag create scoped proxies based on the interface by setting the proxy-target-class property of the <aop:scoped-proxy /> tag to false.
<bean id="cart"
class="com.abiao._03_scope.ShoppingCart"
scope="session">
<aop:scoped-proxy proxy-target-class="false"/>
</bean>
Copy the code
That concludes your understanding of Spring Bean scope.
Make a little progress every day, the slower the faster!