Bean scopes

4.4 Bean scopes

4.4 Bean scopes

When you create a bean definition what you are actually creating is
a recipe for creating actual instances of the class
defined by that bean definition. The idea that a bean definition is a
recipe is important, because it means that, just like a class, you can
potentially have many object instances created from a single
recipe.

You can control not only the various dependencies and configuration
values that are to be plugged into an object that is created from a
particular bean definition, but also the scope of
the objects created from a particular bean definition. This approach is
very powerful and gives you the flexibility to choose
the scope of the objects you create through configuration instead of
having to 'bake in' the scope of an object at the Java class level. Beans
can be defined to be deployed in one of a number of scopes: out of the
box, the Spring Framework supports exactly five scopes (of which three are
available only if you are using a web-aware
ApplicationContext).

The scopes supported out of the box are listed below:


Table 4.4. Bean scopes

ScopeDescription

singleton

Scopes a single bean definition to a single object
instance per Spring IoC container.

prototype

Scopes a single bean definition to any number of
object instances.

request

Scopes a single bean definition to the lifecycle of a
single HTTP request; that is each and every HTTP request will have
its own instance of a bean created off the back of a single bean
definition. Only valid in the context of a web-aware Spring
ApplicationContext.

session

Scopes a single bean definition to the lifecycle of a
HTTP Session. Only valid in the
context of a web-aware Spring
ApplicationContext.

global
session

Scopes a single bean definition to the lifecycle of a
global HTTP Session. Typically only
valid when used in a portlet context. Only valid in the context of
a web-aware Spring
ApplicationContext.


4.4.1 The singleton scope

When a bean is a singleton, only one shared
instance of the bean will be managed, and all requests for beans with an
id or ids matching that bean definition will result
in that one specific bean instance being returned by the Spring
container.

To put it another way, when you define a bean definition and it is
scoped as a singleton, then the Spring IoC container will create
exactly one instance of the object defined by that
bean definition. This single instance will be stored in a cache of such
singleton beans, and all subsequent requests and
references
for that named bean will result in the cached
object being returned.

Please be aware that Spring's concept of a singleton bean is quite
different from the Singleton pattern as defined in the seminal Gang of
Four (GoF) patterns book. The GoF Singleton hard codes the scope of an
object such that one and only one instance of a
particular class will ever be created per
ClassLoader
. The scope of the Spring
singleton is best described as per container and per
bean
. This means that if you define one bean for a particular
class in a single Spring container, then the Spring container will
create one and only one instance of the class
defined by that bean definition. The singleton scope is the
default scope in Spring
. To define a bean as a singleton in
XML, you would write configuration like so:

<bean id="accountService" class="com.foo.DefaultAccountService"/>

<!-- the following is equivalent, though redundant (singleton scope is the default); using spring-beans-2.0.dtd -->
<bean id="accountService" class="com.foo.DefaultAccountService" scope="singleton"/>

<!-- the following is equivalent and preserved for backward compatibility in spring-beans.dtd -->
<bean id="accountService" class="com.foo.DefaultAccountService" singleton="true"/>

4.4.2 The prototype scope

The non-singleton, prototype scope of bean deployment results in
the creation of a new bean instance every time a
request for that specific bean is made (that is, it is injected into
another bean or it is requested via a programmatic
getBean() method call on the container). As a rule of
thumb, you should use the prototype scope for all beans that are
stateful, while the singleton scope should be used for stateless
beans.

The following diagram illustrates the Spring prototype scope.
Please note that a DAO would not typically be configured as a
prototype, since a typical DAO would not hold any conversational state;
it was just easier for this author to reuse the core of the singleton
diagram.

To define a bean as a prototype in XML, you would write
configuration like so:

<!-- using spring-beans-2.0.dtd -->
<bean id="accountService" class="com.foo.DefaultAccountService" scope="prototype"/>

<!-- the following is equivalent and preserved for backward compatibility in spring-beans.dtd -->
<bean id="accountService" class="com.foo.DefaultAccountService" singleton="false"/>

There is one quite important thing to be aware of when deploying a
bean in the prototype scope, in that the lifecycle of the bean changes
slightly. Spring does not manage the complete lifecycle of a prototype
bean: the container instantiates, configures, decorates and otherwise
assembles a prototype object, hands it to the client and then has no
further knowledge of that prototype instance. This means that while
initialization lifecycle callback methods will be
called on all objects regardless of scope, in the case of prototypes,
any configured destruction lifecycle callbacks will
not be called. It is the responsibility of the
client code to clean up prototype scoped objects and release any
expensive resources that the prototype bean(s) are holding onto. (One
possible way to get the Spring container to release resources used by
prototype-scoped beans is through the use of a custom bean post-processor which
would hold a reference to the beans that need to be cleaned up.)

In some respects, you can think of the Spring containers role when
talking about a prototype-scoped bean as somewhat of a replacement for
the Java 'new' operator. All lifecycle aspects past
that point have to be handled by the client. (The lifecycle of a bean in
the Spring container is further described in the section entitled Section 4.5.1, “Lifecycle callbacks”.)

4.4.3 Singleton beans with prototype-bean dependencies

When using singleton-scoped beans that have dependencies on beans
that are scoped as prototypes, please be aware that
dependencies are resolved at instantiation time.
This means that if you dependency inject a prototype-scoped bean into a
singleton-scoped bean, a brand new prototype bean will be instantiated
and then dependency injected into the singleton bean... but
that is all
. That exact same prototype instance will be the
sole instance that is ever supplied to the singleton-scoped bean, which
is fine if that is what you want.

However, sometimes what you actually want is for the
singleton-scoped bean to be able to acquire a brand new instance of the
prototype-scoped bean again and again and again at runtime. In that case
it is no use just dependency injecting a prototype-scoped bean into your
singleton bean, because as explained above, that only happens
once when the Spring container is instantiating the
singleton bean and resolving and injecting its dependencies. If you are
in the scenario where you need to get a brand new instance of a
(prototype) bean again and again and again at runtime, you are referred
to the section entitled Section 4.3.7, “Method Injection”

[Note]Backwards compatibility note: specifying the lifecycle scope in
XML

If you are referencing the
'spring-beans.dtd' DTD in a bean definition
file(s), and you are being explicit about the lifecycle scope of your
beans you must use the "singleton" attribute to
express the lifecycle scope (remembering that the singleton lifecycle
scope
is the default). If you are referencing the
'spring-beans-2.0.dtd' DTD or the Spring 2.0 XSD
schema, then you will need to use the "scope"
attribute (because the "singleton" attribute was
removed from the definition of the new DTD and XSD files in favor of
the "scope" attribute).

To be totally clear about this, this means that if you use the
"singleton" attribute in an XML bean definition
then you must be referencing the
'spring-beans.dtd' DTD in that
file
. If you are using the "scope"
attribute then you must be referencing either the
'spring-beans-2.0.dtd' DTD or the
'spring-beans-2.5.xsd' XSD in that
file
.

4.4.4 The other scopes

The other scopes, namely request,
session, and global session are
for use only in web-based applications (and can be used irrespective of
which particular web application framework you are using, if indeed
any). In the interest of keeping related concepts together in one place
in the reference documentation, these scopes are described here.

[Note]Note

The scopes that are described in the following paragraphs are
only available if you are using a web-aware
Spring ApplicationContext
implementation (such as
XmlWebApplicationContext). If you try using
these next scopes with regular Spring IoC containers such as the
XmlBeanFactory or
ClassPathXmlApplicationContext, you
will get an
IllegalStateException complaining about an
unknown bean scope.

4.4.4.1 Initial web configuration

In order to support the scoping of beans at the
request, session, and
global session levels (web-scoped beans), some
minor initial configuration is required before you can set about
defining your bean definitions. Please note that this extra setup is
not required if you just want to use the
'standard' scopes (namely singleton and prototype).

Now as things stand, there are a couple of ways to effect this
initial setup depending on your particular Servlet
environment...

If you are accessing scoped beans within Spring Web MVC, i.e.
within a request that is processed by the Spring
DispatcherServlet, or
DispatcherPortlet, then no special setup is
necessary: DispatcherServlet and
DispatcherPortlet already expose all relevant
state.

When using a Servlet 2.4+ web container, with requests processed
outside of Spring's DispatcherServlet (e.g. when using JSF or Struts),
you need to add the following
javax.servlet.ServletRequestListener to
the declarations in your web application's
'web.xml' file.

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

If you are using an older web container (Servlet 2.3), you will
need to use the provided
javax.servlet.Filter implementation.
Find below a snippet of XML configuration that has to be included in
the 'web.xml' file of your web application if you
want to have access to web-scoped beans in requests outside of
Spring's DispatcherServlet on a Servlet 2.3 container. (The filter
mapping depends on the surrounding web application configuration and
so you will have to change it as appropriate.)

<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>

That's it. DispatcherServlet,
RequestContextListener and
RequestContextFilter all do exactly the same
thing, namely bind the HTTP request object to the
Thread that is servicing that request. This
makes beans that are request- and session-scoped available further
down the call chain.

4.4.4.2 The request scope

Consider the following bean definition:

<bean id="loginAction" class="com.foo.LoginAction" scope="request"/>

With the above bean definition in place, the Spring container
will create a brand new instance of the
LoginAction bean using the
'loginAction' bean definition for each and every
HTTP request. That is, the 'loginAction' bean will
be effectively scoped at the HTTP request level. You can change or
dirty the internal state of the instance that is created as much as
you want, safe in the knowledge that other requests that are also
using instances created off the back of the same
'loginAction' bean definition will not be seeing
these changes in state since they are particular to an individual
request. When the request is finished processing, the bean that is
scoped to the request will be discarded.

4.4.4.3 The session scope

Consider the following bean definition:

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

With the above bean definition in place, the Spring container
will create a brand new instance of the
UserPreferences bean using the
'userPreferences' bean definition for the lifetime
of a single HTTP Session. In other
words, the 'userPreferences' bean will be
effectively scoped at the HTTP Session
level. Just like request-scoped beans, you can
change the internal state of the instance that is created as much as
you want, safe in the knowledge that other HTTP
Session instances that are also using
instances created off the back of the same
'userPreferences' bean definition will not be
seeing these changes in state since they are particular to an
individual HTTP Session. When the HTTP
Session is eventually discarded, the
bean that is scoped to that particular HTTP
Session will also be discarded.

4.4.4.4 The global session scope

Consider the following bean definition:

<bean id="userPreferences" class="com.foo.UserPreferences" scope="globalSession"/>

The global session scope is similar to the
standard HTTP Session scope (described immediately
above
), and really only makes sense in the context of
portlet-based web applications. The portlet specification defines the
notion of a global Session that is
shared amongst all of the various portlets that make up a single
portlet web application. Beans defined at the global
session
scope are scoped (or bound) to the lifetime of the
global portlet Session.

Please note that if you are writing a standard Servlet-based web
application and you define one or more beans as having global
session
scope, the standard HTTP
Session scope will be used, and no
error will be raised.

4.4.4.5 Scoped beans as dependencies

Being able to define a bean scoped to a HTTP request or
Session (or indeed a custom scope of your
own devising) is all very well, but one of the main value-adds of the
Spring IoC container is that it manages not only the instantiation of
your objects (beans), but also the wiring up of collaborators (or
dependencies). If you want to inject a (for example) HTTP request
scoped bean into another bean, you will need to inject an AOP proxy in
place of the scoped bean. That is, you need to inject a proxy object
that exposes the same public interface as the scoped object, but that
is smart enough to be able to retrieve the real, target object from
the relevant scope (for example a HTTP request) and delegate method
calls onto the real object.

[Note]Note

You do not need to use the
<aop:scoped-proxy/> in conjunction with
beans that are scoped as singletons or
prototypes. It is an error to try to create a
scoped proxy for a singleton bean (and the resulting
BeanCreationException will certainly
set you straight in this regard).

Let's look at the configuration that is required to effect this;
the configuration is not hugely complex (it takes just one line), but
it is important to understand the “why” as well as the
how” behind it.

<?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
           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
           http://www.springframework.org/schema/aop
           http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

    <!-- a HTTP Session-scoped bean exposed as a proxy -->
    <bean id="userPreferences" class="com.foo.UserPreferences" scope="session">

          <!-- this next element effects the proxying of the surrounding bean -->
          <aop:scoped-proxy/>
    </bean>

    <!-- a singleton-scoped bean injected with a proxy to the above bean -->
    <bean id="userService" class="com.foo.SimpleUserService">

        <!-- a reference to the proxied 'userPreferences' bean -->
        <property name="userPreferences" ref="userPreferences"/>

    </bean>
</beans>

To create such a proxy, you need only to insert a child
<aop:scoped-proxy/> element into a scoped
bean definition (you may also need the CGLIB library on your classpath
so that the container can effect class-based proxying; you will also
need to be using Appendix A, XML Schema-based configuration). So, just why do you
need this <aop:scoped-proxy/> element in the
definition of beans scoped at the request,
session, globalSession and
'insert your custom scope here' level? The reason
is best explained by picking apart the following bean definition
(please note that the following 'userPreferences'
bean definition as it stands is
incomplete):

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

<bean id="userManager" class="com.foo.UserManager">
    <property name="userPreferences" ref="userPreferences"/>
</bean>

From the above configuration it is evident that the singleton
bean 'userManager' is being injected with a
reference to the HTTP Session-scoped
bean 'userPreferences'. The salient point here is
that the 'userManager' bean is a singleton... it
will be instantiated exactly once per container,
and its dependencies (in this case only one, the
'userPreferences' bean) will also only be injected
(once!). This means that the 'userManager' will
(conceptually) only ever operate on the exact same
'userPreferences' object, that is the one that it
was originally injected with. This is not what
you want when you inject a HTTP
Session-scoped bean as a dependency
into a collaborating object (typically). Rather, what we
do want is a single
'userManager' object, and then, for the lifetime of
a HTTP Session, we want to see and use
a 'userPreferences' object that is specific to said
HTTP Session.

Rather what you need then is to inject some sort of object that
exposes the exact same public interface as the
UserPreferences class (ideally an object that
is a UserPreferences
instance) and that is smart enough to be able to go off and fetch the
real
UserPreferences object from whatever underlying
scoping mechanism we have chosen (HTTP request,
Session, etc.). We can then safely
inject this proxy object into the 'userManager'
bean, which will be blissfully unaware that the
UserPreferences reference that it is holding
onto is a proxy. In the case of this example, when a
UserManager instance invokes a method
on the dependency-injected UserPreferences
object, it is really invoking a method on the proxy... the proxy will
then go off and fetch the real UserPreferences
object from (in this case) the HTTP
Session, and delegate the method
invocation onto the retrieved real
UserPreferences object.

That is why you need the following, correct and complete,
configuration when injecting request-,
session-, and
globalSession-scoped beans into collaborating
objects:

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

<bean id="userManager" class="com.foo.UserManager">
    <property name="userPreferences" ref="userPreferences"/>
</bean>
Choosing the type of proxy created

By default, when the Spring container is creating a proxy for
a bean that is marked up with the
<aop:scoped-proxy/> element, a
CGLIB-based class proxy will be created
. This means that
you need to have the CGLIB library on the classpath of your
application.

Note: CGLIB proxies will only intercept public
method calls!
Do not call non-public methods on such a
proxy; they will not be delegated to the scoped target
object.

You can choose to have the Spring container create 'standard'
JDK interface-based proxies for such scoped beans by specifying
'false' for the value of the
'proxy-target-class' attribute of the
<aop:scoped-proxy/> element. Using JDK
interface-based proxies does mean that you don't need any additional
libraries on your application's classpath to effect such proxying,
but it does mean that the class of the scoped bean must implement at
least one interface, and all of the
collaborators into which the scoped bean is injected must be
referencing the bean via one of its interfaces.

<!-- DefaultUserPreferences implements the UserPreferences interface -->
<bean id="userPreferences" class="com.foo.DefaultUserPreferences" scope="session">
    <aop:scoped-proxy proxy-target-class="false"/>
</bean>

<bean id="userManager" class="com.foo.UserManager">
    <property name="userPreferences" ref="userPreferences"/>
</bean>

The section entitled Section 8.6, “Proxying mechanisms” may also
be of some interest with regard to understanding the nuances of
choosing whether class-based or interface-based proxying is right
for you.

4.4.5 Custom scopes

As of Spring 2.0, the bean scoping mechanism in Spring is
extensible. This means that you are not limited to just the bean scopes
that Spring provides out of the box; you can define your own scopes, or
even redefine the existing scopes (although that last one would probably
be considered bad practice - please note that you
cannot override the built-in
singleton and prototype
scopes).

4.4.5.1 Creating your own custom scope

Scopes are defined by the
org.springframework.beans.factory.config.Scope
interface. This is the interface that you will need to implement in
order to integrate your own custom scope(s) into the Spring container,
and is described in detail below. You may wish to look at the
Scope implementations that are supplied
with the Spring Framework itself for an idea of how to go about
implementing your own. The Scope
Javadoc
explains the main class to implement when you need
your own scope in more detail too.

The Scope interface has four methods dealing
with getting objects from the scope, removing them from the scope and
allowing them to be 'destroyed' if needed.

The first method should return the object from the underlying
scope. The session scope implementation for example will return the
session-scoped bean (and if it does not exist, return a new instance
of the bean, after having bound it to the session for future
reference).

Object get(String name, ObjectFactory objectFactory)

The second method should remove the object from the underlying
scope. The session scope implementation for example, removes the
session-scoped bean from the underlying session. The object should be
returned (you are allowed to return null if the object with the
specified name wasn't found)

Object remove(String name)

The third method is used to register callbacks the scope should
execute when it is destroyed or when the specified object in the scope
is destroyed. Please refer to the Javadoc or a Spring scope
implementation for more information on destruction callbacks.

void registerDestructionCallback(String name, Runnable destructionCallback)

The last method deals with obtaining the conversation identifier
for the underlying scope. This identifier is different for each scope.
For a session for example, this can be the session identifier.

String getConversationId()
4.4.5.2 Using a custom scope

After you have written and tested one or more custom
Scope implementations, you then need to
make the Spring container aware of your new scope(s). The central
method to register a new Scope with the
Spring container is declared on the
ConfigurableBeanFactory interface
(implemented by most of the concrete
BeanFactory implementations that ship
with Spring); this central method is displayed below:

void registerScope(String scopeName, Scope scope);

The first argument to the
registerScope(..) method is the unique name
associated with a scope; examples of such names in the Spring
container itself are 'singleton' and
'prototype'. The second argument to the
registerScope(..) method is an actual
instance of the custom Scope
implementation that you wish to register and use.

Let's assume that you have written your own custom
Scope implementation, and you have
registered it like so:

// note: the ThreadScope class does not ship with the Spring Framework
Scope customScope = new ThreadScope();
beanFactory.registerScope("thread", customScope);

You can then create bean definitions that adhere to the scoping
rules of your custom Scope like
so:

<bean id="..." class="..." scope="thread"/>

If you have your own custom Scope
implementation(s), you are not just limited to only programmatic
registration of the custom scope(s). You can also do the
Scope registration declaratively, using
the CustomScopeConfigurer class.

The declarative registration of custom
Scope implementations using the
CustomScopeConfigurer class is shown
below:

<?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
           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
           http://www.springframework.org/schema/aop
           http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

    <bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
        <property name="scopes">
            <map>
                <entry key="thread">
                    <bean class="com.foo.ThreadScope"/>
                </entry>
            </map>
        </property>
    </bean>

    <bean id="bar" class="x.y.Bar" scope="thread">
        <property name="name" value="Rick"/>
        <aop:scoped-proxy/>
    </bean>

    <bean id="foo" class="x.y.Foo">
        <property name="bar" ref="bar"/>
    </bean>

</beans>
[Note]Note

Note that, when placing a <aop:scoped-proxy/> in a
FactoryBean implementation, it is the
factory bean itself that is scoped, not the object returned from
getObject().

posted on 2012-05-31 09:20  lexus 阅读( ...) 评论( ...) 编辑 收藏

转载于:https://www.cnblogs.com/lexus/archive/2012/05/31/2527855.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值