Announcement Announcement Module
Collapse
No announcement yet.
Request scope - how to Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Request scope - how to

    The Spring Framework is great in applications which can be wired of several stateless beans. However it is not clear what to do when we need some bean to change its behavior upon certain conditions (i.e. stateful beans).

    Let's take a look at simple example which demonstrates it.
    The task is to make Velocity render the templates from different resource loaders depending on request GET parameter.

    For instance, http://localhost/index.html?resource=file would load index.vm template from FileResourceLoader, while http://localhost/index.html?resource=class would load the same template from ClasspathResourceLoader.

    Although the objective seems very easy, it is not an easy thing to implement in Spring, since we need to modify beans behavior depending on Request, and we cannot modify the behavior (configuration) of beans defined in application context because it violates thread safety.

    I'd really appreciate any ideas on how it can be implemented in "best practices" style, because the solution which comes into my mind is too clumsy for such simple requirements.

  • #2
    Actually, I happily managed to figure out a good working solution (more like workaround).

    If the beans in your WebApplicationContext need to change behavior depending on Request, there is a thread-safe way to pass the beans the request.

    Simply create a ThreadLocal bean in the Context and inject dependency to the target "request-scope" bean.

    Then, create a small Interceptor which will implement preHandle method and put current Request to the ThreadLocal bean (don't forget to inject dependency between this Interceptor and ThreadLocal as well).

    Code:
    public class RequestSetterInterceptor extends HandlerInterceptorAdapter {
        private ThreadLocal threadLocal = null;
    
        public ThreadLocal getThreadLocal() {
            return threadLocal;
        }
    
        public void setThreadLocal(ThreadLocal threadLocal) {
            this.threadLocal = threadLocal;
        }
    
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) throws Exception {       
            threadLocal.set(request);
            return true;
        }
    }
    Finally, in the target "request-scope" bean, access request saved in ThreadLocal object.

    Code:
    class RequestScopeBean {
    //...
            HttpServletRequest request = (HttpServletRequest)getThreadLocal().get();
    //...
    }
    Now since we access "request" object via ThreadLocal, the problem of concurrent access to the RequestScopeBean is solved.

    This is a little tricky and takes some time to figure out, but it's workable and easy to implement solution. It's just a shame that such trivial use cases are not described properly in the docs.


    BTW, any comments regarding this workaround are welcome, if you believe it won't work properly or have any questions.

    Comment


    • #3
      Yet another, prettier solution would be to use Spring AOP. We can use ThreadLocalTargetSource class to implement
      ThreadLocal instance pooling strategy:

      Code:
          <bean id="requestScopeBean" class="org.springframework.aop.framework.ProxyFactoryBean">
              <property name="targetSource">
                  <ref local="threadLocalTargetSource"/>
              </property>
          </bean>
      
          <bean id="threadLocalTargetSource"       class="org.springframework.aop.target.ThreadLocalTargetSource">
              <property name="targetBeanName">
                  <value>targetRequestScopeBean</value>
              </property>
          </bean>
       
          <bean name="targetRequestScopeBean"
              class="...RequestScopeBean"
              singleton="false" />
      This way we don't have to implement our custom ThreadLocal request container, since the targetRequestScopeBean will be now created per each thread and thus the thread-safety issue will be resolved just fine.

      The RequestSetterInterceptor will not be aware of ThreadLocal strategy at all in this case:

      Code:
      public class RequestSetterInterceptor extends HandlerInterceptorAdapter &#123;
          private RequestScopeBean requestScopeBean;
          // getter and setter omitted
      
          public boolean preHandle&#40;HttpServletRequest request, HttpServletResponse response, Object o&#41; throws Exception &#123;       
              requestScopeBean.setRequest&#40;request&#41;;
              return true;
          &#125;
      &#125;
      The same applies to RequestScopeBean itself - the implementation does not know about ThreadLocal involved. This solution looks more mature, however
      I'm not sure if performance remains good enough in this case.

      Hope this helps to someone like me guessing how simple things can be done in Spring in a good fashion.

      Comment

      Working...
      X