IO /spring-fram…

1.5 Object scope

When you create a bean definition, you set how the class defined by that bean creates the actual instance. You can control not only the various dependencies and configuration values to be inserted into the objects created by a particular bean definition, but also the scope of the objects created by a particular bean definition. This approach is powerful and flexible, because you can select the scope of objects to create through configuration. A bean can be defined to be deployed in one of multiple scopes. The Spring framework supports six scopes, four of which are available only if you use the Web-aware ApplicationContext. You can also create custom scopes.

Scope Description
singleton The single bean definition scope for each Spring IoC container is set (by default) to a single object instance.
prototype Restrict a single bean definition to any number of object instances.
request Restrict a single bean definition to the life cycle of a single HTTP request. That is, each HTTP request has its own bean instance, which is created based on a single bean definition. Only valid in the context of web-aware Spring ApplicationContext.
session Restrict the individual bean definition to the lifetime of the HTTP Session. Only valid in the context of web-aware Spring ApplicationContext.
application Apply the lifecycle of the individual bean definition to the ServletContext. Only valid in the context of web-aware Spring ApplicationContext.
websocket Apply a single bean definition to the life cycle of the WebSocket. Only valid in the context of web-aware Spring ApplicationContext.

1.5.1 the singleton

A singleton bean with only one shared instance is managed by the container, and all requests for beans defined by that bean cause the Spring container to return that particular bean instance.

In other words, when you define a bean definition and limit its scope to singleton, the Spring IoC container creates an instance of the object defined by that bean. This single instance is stored in the cache of such a singleton bean, and all subsequent requests and references to the named bean return the cached object. The following image shows how the singleton scope works:

Spring’s singleton concept is different from the singleton pattern defined in GoF. GoF singletons use hard coding so that each loader creates only one instance of a class. The scope of a Spring singleton object describes each container and each bean. This means that if you define a singleton for a particular class in a single Spring container, the Spring container will create one and only one instance of the class defined by that bean. The singleton scope is Spring’s default scope. You can define a singleton bean in XML as follows:

<bean id="accountService" class="com.something.DefaultAccountService"/>
<! -- the following is equivalent, though redundant (singleton scope is the default) -->
<bean id="accountService" class="com.something.DefaultAccountService" scope="singleton"/>
Copy the code

1.5.2 the Prototype

The non-singleton scope of a bean results in the creation of a new bean instance each time a request is made to a particular bean. That is, the bean is injected into another bean, or it returns a newly constructed object when requested by a getBean() method call. The following rules exist: you should use the Prototype scope for all stateful objects and the Singleton scope for stateless objects.

The following diagram illustrates Spring’s Prototype scope:

Data access Objects (DAOs) are not normally configured to prototype because typical DAOs do not hold any state and singleton is more appropriate.

The following example uses XML to define a bean as prototype:

<bean id="accountService" class="com.something.DefaultAccountService" scope="prototype"/>
Copy the code

In contrast to other scopes, Spring does not manage the full life cycle of prototype beans. After the container instantiates, configures, and assembles a Prototype object, and passes it to the client, there is no further record of the Prototype instance. Under any scope, the container calls the object’s initialization lifecycle callback, but under Prototype, the configured destruction lifecycle callback is not called. Client code must clean up prototype-scoped objects and free up the expensive resources it holds. To get the Spring container to release resources held by prototype-scoped beans, try a custom bean post-handler that uses references to beans that need to be cleaned up.

In some ways, the Spring container’s role relative to the Prototype scope is a substitute for the Java New operator. All life cycle management after object creation must be handled by the client. (For more information about the lifecycle of beans in the Spring container, see Lifecycle Callbacks.)

1.5.3 Singleton Bean with Prototype dependencies

When you use Singleton beans that depend on Prototype beans, note that the dependencies are resolved at instantiation time. Therefore, if you inject a prototype-scoped bean into a Singleton-scoped bean, a new bean will be instantiated and the dependency injected into the Singleton bean. The Prototype instance is the only instance that provides the Singleton Bean.

However, suppose you want the Singleton Bean to repeatedly get a new instance of the Prototype bean at run time. You cannot inject a Prototype bean dependency into a singleton bean because such injection occurs only once when the Spring container instantiates the singleton bean parsing and injects its dependencies. If you need a new instance of the Prototype bean more than once at run time, see method injection

1.5.4 Request, Session, Application, and WebSocket

The Request, Session, Application, and WebSocket scopes are only available if you use the Web’s Spring ApplicationContext implementation (such as XmlWebApplicationContext). If you put these scope with conventional Spring IoC container (such as ClassPathXmlApplicationContext) are used together, will throw a an IllegalStateException anomalies.

Initialize the Web configuration

To support request, Session, Application, and WebSocket-level (Web scoped) scopes, some minor initial configuration is required before defining beans. (Standard scope: Singleton and Prototype do not require this initial setting.) How this initial setting is accomplished depends on the specific Servlet environment.

  • No special setup is required if you access the scoped bean in the Spring Web MVC and actually in a request handled by the Spring DispatcherServlet. The DispatcherServlet configuration is complete.

  • If you use the Servlet 2.5 Web container to process requests outside of Spring’s DispatcherServlet (for example, when using JSF or Struts), You need to register org. Springframework. Web. Context. Request. RequestContextListener ServletRequestListener. For the Servlet 3.0 +, this can be done through the use of WebApplicationInitializer interface programmatically. Or, for older containers, add the following declaration to your Web application’s web.xml file:

<web-app> ... <listener> <listener-class> org.springframework.web.context.request.RequestContextListener </listener-class> </listener> . </web-app>Copy the code

Also, if you have problems with your listener setup, consider using Spring’s RequestContextFilter. The filter mapping depends on the Web application configuration, so you must change it appropriately. The following listing shows the filter part configuration for a Web application:

<web-app>
    ...
    <filter>
        <filter-name>requestContextFilter</filter-name>
        <filter-class>org.springframework.web.filter.RequestContextFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>requestContextFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    ...
</web-app>
Copy the code

The DispatcherServlet, RequestContextListener, and RequestContextFilter all do the same thing: bind the HTTP request object to the thread that serves the request. This makes request-scoped and session-scoped beans available further down the invocation chain.

The Request scope

The XML configuration is as follows:

<bean id="loginAction" class="com.something.LoginAction" scope="request"/>
Copy the code

The Spring container creates a new instance of the LoginAction bean by using the LoginAction bean definition for each HTTP request. That is, the loginAction bean is scoped at the HTTP request level. You can change the internal state of the created instance as much as you like, because other instances created from the same loginAction bean definition will not see these state changes. When the Request completes processing, beans scoped to Request are discarded.

When using annotation-driven components or Java configurations, you can use the @RequestScope annotation to configure scopes for classes:

@RequestScope
@Component
public class LoginAction {
    // ...
}
Copy the code

Session scope

The XML configuration is as follows:

<bean id="userPreferences" class="com.something.UserPreferences" scope="session"/>
Copy the code

The Spring container creates a new instance of the UserPreferences Bean by using the UserPreferences Bean definition during the life of a single HTTP Session. In other words, the userPreferences bean is effectively restricted to the HTTP Session level. As with the Request scope, you can change instances of internal state that are invisible to other HTTP Session objects created through the userPreferences bean definition because they are specific to a single HTTP Session. When the HTTP Session is finally discarded, beans scoped for that particular HTTP Session are also discarded.

When using annotation-driven components or Java configurations, you can use the @sessionScope annotation to configure scopes for classes:

@SessionScope
@Component
public class UserPreferences {
    // ...
}
Copy the code

Application scope

The XML configuration is as follows:

<bean id="appPreferences" class="com.something.AppPreferences" scope="application"/>
Copy the code

The Spring container uses the AppPreferences Bean definition for the entire Web application to create a new instance of the AppPreferences Bean. That is, the appPreferences bean is scoped at the ServletContext level and stores the general ServletContext properties. This is similar to Singleton, but different in two important respects: it is a Singleton based on a ServletContext rather than Spring ApplicationContext(which may contain several ServletContexts).

When using annotation-driven components or Java configurations, you can use the @ApplicationScope annotation to configure scopes for classes:

@ApplicationScope
@Component
public class AppPreferences {
    // ...
}
Copy the code

Scoped objects as dependencies

The Spring IoC container manages not only the instantiation of objects (beans), but also the wiring of collaborators (or dependencies). If you want to inject, for example, an HTTP request-scoped bean into another long-scoped bean, you can choose to inject an AOP proxy instead of the scoped bean. That is, you need to inject a proxy object that exposes the same public interface as the scoped object, but also retrieves the actual target object from the relevant scope (such as HTTP requests) and makes delegate method calls on the actual object.

The configuration in the following example is only one line, but it is important to understand the “why” and “how” :

<? The 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:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"> <! -- an HTTP Session-scoped bean exposed as a proxy --> <bean id="userPreferences" class="com.something.UserPreferences" scope="session"> <! -- instructs the container to proxy the surrounding bean --> <aop:scoped-proxy/> </bean> <! -- a singleton-scoped bean injected with a proxy to the above bean --> <bean id="userService" class="com.something.SimpleUserService"> <! -- a reference to the proxied userPreferences bean --> <property name="userPreferences" ref="userPreferences"/> </bean> </beans>Copy the code

To create such a proxy, you need to insert the AOP :scoped-proxy/ element into a Scoped Bean definition (see Selecting the type of proxy to create and XML schema-based configuration). Why do YOU need aop:scoped-proxy/ elements to define beans at the request, session, and custom scoped levels? Consider the following singleton bean definitions and compare them to what you need to define for the scope above (note that the following userPreferences Bean definitions are incomplete):

<bean id="userPreferences" class="com.something.UserPreferences" scope="session"/>

<bean id="userManager" class="com.something.UserManager">
    <property name="userPreferences" ref="userPreferences"/>
</bean>
Copy the code

In the previous example, the singleton bean (userManager) is injected with a reference to the HTTP Session scope (userPreferences). What stands out here is that the userManager Bean is a singleton object: it is only instantiated once per container, and its dependencies (only one in this case, the userPreferences bean) are injected only once. This means that the userManager bean operates only on the exact same userPreferences object (that is, the object it was originally injected into).

This is not the behavior you want when you inject a short-scoped bean into a long-scoped bean (for example, an HTTP session-scoped collaboration bean into a singleton bean as a dependency). Instead, you need a userManager object, and for the lifetime of an HTTP session, you need an HTTP session-specific userPreferences object. Thus, the container creates an object that exposes exactly the same public interface as the UserPreferences class (ideally, this object is an instance of UserPreferences), which can get the real UserPreferences object from the scope mechanism (HTTP requests, sessions, and so on). The container injects the proxy object into the userManager bean, which is unaware that the UserPreferences reference is a proxy. In this case, when the UserManager instance calls a method on the UserPreferences object for dependency injection, it is actually calling a method on the proxy. The proxy then fetches the real UserPreferences object from the HTTP Session(in this case) and delegates the method call to the retrieved real UserPreferences object.

Therefore, when injecting request – and session-scoped beans into collaboration objects, you need the following (correct and complete) configuration, as shown in the following example:

<bean id="userPreferences" class="com.something.UserPreferences" scope="session">
    <aop:scoped-proxy/>
</bean>

<bean id="userManager" class="com.something.UserManager">
    <property name="userPreferences" ref="userPreferences"/>
</bean>
Copy the code

Select the proxy type

By default, when the Spring container creates a proxy for beans marked with the < AOP: scope-proxy /> element, a Cglib-based class proxy is created.

The CGLIB proxy only intercepts public method calls! Do not call non-public methods on such proxies. They are not delegated to the actual scoped target object.

Alternatively, you can configure the Spring container to create standard proxies based on JDK interfaces for such scoped beans by specifying false to the value of the proxy-target-class attribute of the AOP :scoped-proxy/ element. Using a proxy based on a JDK interface means that you don’t need to add additional libraries to your application’s classpath to implement this proxy. However, this also means that the class of the scoped bean must implement at least one interface, and all collaborators injected into the scoped bean must reference the bean through one of its interfaces. The following example shows an interface-based proxy:

<! -- DefaultUserPreferences implements the UserPreferences interface --> <bean id="userPreferences" class="com.stuff.DefaultUserPreferences" scope="session"> <aop:scoped-proxy proxy-target-class="false"/> </bean> <bean id="userManager" class="com.stuff.UserManager"> <property name="userPreferences" ref="userPreferences"/> </bean>Copy the code

For more information about choosing class-based or interface-based proxies, see: docs. Spring. IO /spring-fram…

1.5.5 Customizing scopes

Create a custom scope

Will your custom scopes are integrated into the Spring container, you need to implement described in this section of the org. Springframework. Beans. Factory. Config. The Scope interface. To learn how to implement custom scopes, see several Scope implementations provided by the Spring framework and Scope Javadoc docs.spring. IO /spring-fram…

The Scope interface has four methods for getting objects from Scope, removing objects from Scope, and destroying them.

For example, the session-scoped implementation returns a session-scoped bean(if it does not exist, the method binds the bean to the session for future reference and returns a new instance of the bean). The following methods return objects from the underlying scope:

Object get(String name, ObjectFactory<? > objectFactory)Copy the code

For example, the Session scoping implementation removes the session scoped bean from the underlying session. This method returns the object, but null if no object with the specified name is found. The following method removes an object from the base scope:

Object remove(String name)
Copy the code

The following methods register a callback function that should be called when the scope is destroyed, or when the specified object in the scope is destroyed:

void registerDestructionCallback(String name, Runnable destructionCallback)
Copy the code

For more information about destruction callbacks, see Javadoc or Spring scoped implementation.

Get the session identifier for the underlying scope as follows:

String getConversationId()
Copy the code

This identifier is different for each scope. For a session scoped implementation, this identifier can be a session identifier.

Use custom scopes

After writing and testing one or more custom Scope implementations, you need to let the Spring container know about your new Scope. The following method is a way to register a new Scope with the Spring container:

void registerScope(String scopeName, Scope scope);
Copy the code

This method is declared on the ConfigurableBeanFactory interface

The first parameter is the unique name associated with the scope. Examples of such names in the Spring container itself are Singleton and Prototype.

The second parameter is the actual instance of the custom Scope implementation that you want to register and use.

Suppose you write a custom Scope implementation, and then register it as shown in the following example.

The next example uses SimpleThreadScope, which is included with Spring but is unregistered by default. For your own custom Scope implementation, the instructions will be the same.

Scope threadScope = new SimpleThreadScope();
beanFactory.registerScope("thread", threadScope)
Copy the code

You can then create bean definitions that conform to custom Scope Scope rules, as follows:

<bean id="..." class="..." scope="thread">
Copy the code

With a custom Scope implementation, you are not limited to programming registries for that Scope. You can also declaratively register scopes by using the CustomScopeConfigurer class, as shown in the following example:

<? The 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:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"> <bean class="org.springframework.beans.factory.config.CustomScopeConfigurer"> <property name="scopes"> <map> <entry key="thread"> <bean class="org.springframework.context.support.SimpleThreadScope"/> </entry> </map> </property> </bean> <bean id="thing2" class="x.y.Thing2" scope="thread"> <property name="name" value="Rick"/> <aop:scoped-proxy/> </bean> <bean id="thing1" class="x.y.Thing1"> <property name="thing2" ref="thing2"/> </bean> </beans>Copy the code