Search code examples
springaopshiro

Spring DefaultAdvisorAutoProxyCreator with @Transactional causing problems


I'm working on a Spring MVC project and trying to integrate Apache Shiro for the security. Everything was going just swimmingly until I realized Hibernate was prematurely closing the session/connection after a single query and causing a lazyinit exception. Not surprising, what I was doing should be done within a transaction so the session isn't closed.

Dilemmas…

  1. I tried putting @Transactional on my controller method, but I get 404's then. Looking at my logs, I can see that when Spring is bootstrapping it ignores any mappings in my HomeController if that @Transactional annotation is on any method within the controller.

  2. Without the @Transactional and it loads up just fine, and Ih can see the RequestMappingHandlerMapping bean sees all the @RequestMapping annotations in my controller.

  3. With @Transactional but without DefaultAdvisorAutoProxyCreator, and it works except Shiro annotations are simply ignored.

tldr: Shiro requires DefaultAdvisorAutoProxyCreator, but if I create that bean, Spring blows up when using the @Transactional annotation.

I'm asking for help because I'm completely at a loss for how to proceed at this point.


Solution

  • This is typically because when you put @Transactional on a method, Spring creates a dynamic proxy for that bean - if the bean implements an interface then dynamic proxy is created based on that interface, otherwise CGLIB will be used for creating the proxy.

    The problem in your case, I am guessing is because you probably have based your controller on some interface, or you are extending it based on a base class. This is ending up creating a proxy based on that interface, because of this when it comes time for mappings to be created for your request, Spring MVC is probably not finding your mappings from your bean.

    The fix could be a few:

    a. You can try and force proxies to be based on CGLIB for your transactions:

    <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>
    

    b. You can use pure Aspectj,either load time weaving or compile time weaving

    c. You can move the @Transactional into a Service (which has an interface) and delegate the call from the controller to the service, this way avoiding @Transaction on the controller