Results 1 to 3 of 3

Thread: Transaction AOP on an advice bean doesn't work

  1. #1
    Join Date
    Oct 2008
    Location
    Toronto
    Posts
    4

    Default 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. #2
    Join Date
    May 2007
    Location
    Saint Petersburg, Russian Federation
    Posts
    1,189

    Default

    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.

  3. #3
    Join Date
    Oct 2008
    Location
    Toronto
    Posts
    4

    Default

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

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •