Announcement Announcement Module
Collapse
No announcement yet.
Transaction AOP on an advice bean doesn't work Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Transaction AOP on an advice bean doesn't work

    I have transaction advice declared on methods of sessionManager:
    Code:
        <aop:config>
            <aop:advisor id="sessionManagerTx" 
                advice-ref="sessionManagerTxAdvice" 
                pointcut="execution(* foo.system.SessionManager.*(..))" order="0"/>
        </aop:config>
        
         
        <tx:advice id="sessionManagerTxAdvice">
            <tx:attributes>
                <tx:method name="*" propagation="REQUIRED" rollback-for="Exception"/>
            </tx:attributes> 
        </tx:advice>
    
        <bean id="userSessionLogger" class="foo.webapp.listener.UserSessionLogger">
            <property name="sessionManager" ref="sessionManager"/>
        </bean>
    
        <bean id="sessionManager" class="foo.system.SessionManagerImpl">
            <property name="sessionDao" ref="sessionDao"/>
            <property name="userDao" ref="userDao"/>
            <property name="sessionRegistry" ref="sessionRegistry"/>
        </bean>
    That works fine, whenever the userSessionLogger picks up HttpSessionEvents, it writes to the DB within a transaction. Now if I add my own custom AOP as shown below, that uses the sessionManager as advice methods (SessionManager.invalidateSessions), the userSessionLogger no longer uses a proxied instance of SessionManagerImpl:
    Code:
        <!-- automatic session expiry when User's roles change or if they are disabled -->
        <aop:config>
            <aop:aspect id="sessionExpiryAspect" ref="sessionManager" order="3">
                <aop:after
                    pointcut="execution(* foo.service.UserManager.disableUser(foo.model.User)) and args(user)"
                    arg-names="user"
                    method="invalidateSessions"
                />
                <aop:after
                    pointcut="execution(* foo.service.UserManager.update*Roles(foo.model.User,..)) and args(user,..)"
                    arg-names="user"
                    method="invalidateSessions"
                />
            </aop:aspect>
        </aop:config>
    The userManager bean is declared in another file. If I remove the above aop, userSessionLogger uses a proxied instance of SessionManagerImpl. What's going on? I'm using Spring 2.5.2.

  • #2
    Spring functions as designed there. Reference documentation describes that case - 3.7.1. Customizing beans using BeanPostProcessors:
    BeanPostProcessors and AOP auto-proxying

    Classes that implement the BeanPostProcessor interface are special, and so they are treated differently by the container. All BeanPostProcessors and their directly referenced beans will be instantiated on startup, as part of the special startup phase of the ApplicationContext, then all those BeanPostProcessors will be registered in a sorted fashion - and applied to all further beans. Since AOP auto-proxying is implemented as a BeanPostProcessor itself, no BeanPostProcessors or directly referenced beans are eligible for auto-proxying (and thus will not have aspects 'woven' into them.
    Here your 'sessionExpiryAspect' directly references 'sessionManager' bean, hence transactional aspect is not woven to the 'sessionManager'.

    There is a number of options to resolve the problem. For example, you can introduce a dedicated advice like:
    Code:
    public class ExpirationAdvice {
    
    	private SessionManager sessionManager;
    
    	public void invalidateSessions(User user) {
    		sessionManager.invalidateSessions(user);
    	}
    
    	public void setSessionManager(SessionManager sessionManager) {
    		this.sessionManager= sessionManager;
    	}
    }
    and use that advice at aspect configuration:
    HTML Code:
        <bean id="expirationAdvice" class="ExpirationAdvice">
            <property name="sessionManager" ref="sessionManager"/>
        </bean>
    
        <!-- automatic session expiry when User's roles change or if they are disabled -->
        <aop:config>
            <aop:aspect id="sessionExpiryAspect" ref="sessionManager" order="3">
                <aop:after
                    pointcut="execution(* foo.service.UserManager.disableUser(foo.model.User)) and args(user)"
                    arg-names="user"
                    method="invalidateSessions"
                />
                <aop:after
                    pointcut="execution(* foo.service.UserManager.update*Roles(foo.model.User,..)) and args(user,..)"
                    arg-names="user"
                    method="invalidateSessions"
                />
            </aop:aspect>
        </aop:config>
    Another solution is to weave one or both of conflicting aspects via aspectj weaver.

    Comment


    • #3
      Great explanation Denis, thanks a lot. This makes sense to me now.

      Comment

      Working...
      X