Announcement Announcement Module
Collapse
No announcement yet.
Pointcut on handleRequest of _some_ Controllers Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Pointcut on handleRequest of _some_ Controllers

    I want to make _some_ of my Spring MVC controller transactional.

    My platform setup: JDK 1.5, Tomcat 5.5, OpenSessionInViewInterceptor on the URL mappings, and HibernateTransactionManager.

    As I subclass (e.g.) SimpleFormController I can't annotate handleProcess() directly using @Transactional.

    Using TransactionProxyFactoryBean I can proxy just the controller I want to be transactional and every other controller use the "open session in view" for read operations. The down side is I need to wire a separate proxy bean for every transactional controller.

    Now I wanted to use <tx:advice> and <aop:config> to configure which controller are transactional. This works perfectly for _all_ controllers.

    <tx:advice id="transactionAdvice">
    <tx:attributes><tx:method name="handleRequest"/></tx:attributes>
    </tx:advice>

    <aop:config>
    <aop:advisor pointcut="execution(* org.springframework.web.servlet.mvc.Controller+.ha ndleRequest(..))" advice-ref="transactionAdvice"/>
    </aop:config>

    But I don't want to create a transaction for my read-only controllers.

    Reading the documentation and http://forum.springframework.org/showthread.php?t=30933 I figured I just need to create a further restriction on the pointcut expression either by
    1.) marking my read/write controller classes as @Transactional
    2.) putting a marker interface on them e.g. TransactionalControllerInterface (maybe subclassing Controller and specifying handleRequest again)
    3.) having them all named in a special way e.g. *TxController
    but I couldn't get anything to work.

    Why wouldn't this work?

    A) pointcut="execution(* org.springframework.web.servlet.mvc.Controller+.ha ndleRequest(..)) and @annotation(org.springframework.transaction.annota tion.Transactional)"

    or

    B) pointcut="execution(* org.springframework.web.servlet.mvc.Controller+.ha ndleRequest(..)) and target(com.foo.TransactionControllerInterface)"

    or

    C) pointcut="execution(* org.springframework.web.servlet.mvc.Controller+.ha ndleRequest(..)) and target(com.foo.SomeRealTransactionController)"

    or

    D) pointcut="execution(* com.foo.TransactionControllerInterface+.handleRequ est(..))"

    Any idea what approach to take to avoid having to wire all transactional controllers individually is greatly appreciated.

    Thanks
    Christian

    PS:
    1. Is there any balanced discussion on the web about the "transactional controller" approach?
    2. How can I automatically detect if a controller changed object state but wasn't setup transactional?

  • #2
    Following up on my previous post: one clarification about what I mean by "not working": my controller is not proxied leaving me without a read/write transaction to commit after the request is done.

    After comparing long startup log files with different pointcut definitions I'm clueless as to what the cause is as there is no message (at least as far as I can tell) indication a problem.

    I also tried yet another pointcut expression

    E) pointcut="@within(org.springframework.transaction. annotation.Transactional)"

    Thanks
    Christian

    Comment


    • #3
      I solved this by defining my pointcut using a small Java class mapped to a Spring bean. If anybody can tell me how to achieve the same thing just using some Spring config, please let me know ...

      Code:
      /**
       * Defines pointcut on the handleRequest method of all classes implementing org.springframework.web.servlet.mvc.Controller and having a
       * org.springframework.transaction.annotation.Transactional annotation
       */
      
      public class TransactionPointcut extends StaticMethodMatcherPointcut
      {
      
          // private static final Logger log = Logger.getLogger(TransactionPointcut.class);
      
          public ClassFilter getClassFilter()
          {
              return new ClassFilter()
              {
                  public boolean matches(Class clazz)
                  {
      
                      return org.springframework.web.servlet.mvc.Controller.class.isAssignableFrom(clazz) &&
                             clazz.isAnnotationPresent(org.springframework.transaction.annotation.Transactional.class);
                  }
              };
          }
      
          public boolean matches(Method method, Class aClass)
          {
              return ("handleRequest".equals(method.getName()));
          }
      }

      Comment

      Working...
      X