Tuesday 7 October 2014

Spring Framework Tutorial

Bean scopes (v3+)


A bean definition in Spring is actually a set of instructions of how to create the bean instance. You can control the scope of the objects created from a particular bean definition through configuration.

Out of the box, the Spring Framework supports five scopes:


  • Two general purpose scopes:


    • 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

  • And  three web scopes

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




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.

The singletone instance will be stored in a cache of singleton beans, and all requests and references for that 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 comon  Singleton pattern.

  • The scope of the Spring singleton is best described as per container and per bean.

  • The common Singleton pattern 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 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"/>





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.

Please note that a DAO would not typically be configured as a prototype, since a typical DAO would not hold any conversational state.

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





Life Cycle of Prototype Bean


There is one quite important thing to be aware of when deploying a bean in the prototype scope. After the container hands it to the client, it has no further knowledge of that prototype instance. This means that destruction lifecycle callbacks will not be called. (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.)




Preparing for Web Based Scopes


Web based scopes are namely:
  • request
  • session
  • global session




Web Based Scopes Containers


The web based scopes are only available if you are using a web-aware Spring ApplicationContext, such as XmlWebApplicationContext. If you try using web based scopes with regular Spring IoC containers, such as the XmlBeanFactory or ClassPathXmlApplicationContext, you will get an IllegalStateException complaining about an unknown bean scope.




Using the Spring Web MVC

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.




Using the Servlet 2.4+ web container

When using a Servlet 2.4+ web container, with requests processed outside of Spring's DispatcherServlet, 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>





Using the Servlet 2.3- web container


If you are using an older web container (Servlet 2.3), you will need to use the provided javax.servlet.Filter implementation. (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>





Web-Based Scopes Configuration Summary


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.




The Request scope



Prerequisites


First read the section "Preparing for web-based scopes"



Details


Consider the following bean definition:

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




With the above bean definition, the Spring container will create a brand new instance of the LoginAction bean for each and every HTTP request. That is, the 'loginAction' bean will be effectively scoped at the HTTP request level. When the request is finished processing, the bean that is scoped to the request will be discarded.




The Session scope



Prerequisites


First read the section "Preparing for web-based scopes"



Details


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




The Spring container will create a brand new instance of the UserPreferences bean for the lifetime of a single HTTP Session. In other words, the 'userPreferences' bean will be effectively scoped at the HTTP Session level. When the HTTP Session is eventually discarded, the bean that is scoped to that particular HTTP Session will also be discarded.




The Global Session scope



Prerequisites


First read the section "Preparing for web-based scopes"



Details




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, 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. Beans defined at the global session scope are scoped to the lifetime of the global portlet Session.




Scoped Beans As Dependencies


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 (See the proxy design pattern).


Let's look at the configuration that is required to effect this:

<?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).

The singleton bean 'userManager' is being injected with a reference to the HTTP Session-scoped bean 'userPreferences'. What we 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.

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 fetch the real UserPreferences object from (in this case) the HTTP Session, and delegate the method invocation onto the retrieved real UserPreferences object.




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.




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




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. 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()





Using a custom scope

You 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 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().