Announcement Announcement Module
Collapse
No announcement yet.
@Transactional interferes with annotation based AOP Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • @Transactional interferes with annotation based AOP

    I've run into a weird problem lately. I am trying to get an around advice applied to my service layer. The ServiceImpls already have @Transactional annotations on the types and method calls.

    The aspect I'm trying to use looks something like this:

    Code:
    @Aspect
    class CachingAspect
    {
    
      @Around("this(com.company.Service) && execution(public * *(..)) && @annotation(annotation)")
      public Object possiblyCache(ProceedingJoinPoint pjp, Cacheable annotation)
      ...
    }
    This CachingAspect never gets applied to any Service that sports a @Transactional annotation (in addition to the @Cacheable annotation). If the Service does not have @Transactional anywhere in the target class, then the CachingAspect is applied to those methods that have the @Cacheable annotation applied. If @Transactional is anywhere in the target class, the CachingAspect is not seen applied to that target.

    As soon as I take out the @annotation in my in-line pointcut expression:

    Code:
    @Aspect
    class CachingAspect
    {
    
      @Around("this(com.company.Service) && execution(public * *(..))")
      public Object possiblyCache(ProceedingJoinPoint pjp)
      ...
    }
    Then I am able to use the CachingAspect with ServiceImpls that use @Transactional as well. However, I need to use the @Cacheable annotation in these scenarios.

    Any ideas? I'm a little stumped as to why the @Transactional annotation is interfering.

    thanks
    Collin

  • #2
    Tested your configuration:
    Code:
    @Aspect
    public class CustomAnnotTestAspect {
    	@Around("this(com.dao.SimpleDAO) && execution(public * *(..)) && @annotation(annotation)")
    	public Object possiblyCache(ProceedingJoinPoint pjp, MyAnnotation annotation) throws Throwable {
    		System.out.println("BEFORE CALL");
    		Object obj = pjp.proceed();
    		System.out.println("AFTER CALL");
    		
    		return obj;
    	}
    }
    Code:
    public class SimpleDAOImpl implements SimpleDAO {
    	
    	@MyAnnotation
    	@Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
    	public void addTable(SimpleTable table) {
    		System.out.println("ADD TABLE");
    	}
    }
    and it works.

    Any difference between our configurations ?

    Comment


    • #3
      Andrei,

      Wow, I did not expect that. I must have something else in my environment which is causing this. I suppose it's time to start with a fresh environment and start adding things until it breaks. I appreciate your guidance here; I'll post back with what I find.

      -Collin

      Comment


      • #4
        Well, I was able to reproduce the error condition, and I have a pretty simple integration test that exhibits the behavior. The integration test points to a small environment file that loads as little as possible to reproduce the error. The test and environment file as shown should fail. In order to get it to pass, comment out or remove the <tx:annotation-driven transaction-manager="txManager"/>.

        Thanks for any tips/pointers.

        The integration test:

        Code:
        import java.lang.annotation.ElementType;
        import java.lang.annotation.Retention;
        import java.lang.annotation.RetentionPolicy;
        import java.lang.annotation.Target;
        import org.aspectj.lang.ProceedingJoinPoint;
        import org.aspectj.lang.annotation.Around;
        import org.aspectj.lang.annotation.Aspect;
        import org.springframework.test.AbstractDependencyInjectionSpringContextTests;
        import org.springframework.transaction.annotation.Transactional;
        
        public class TestAnnotationAdvice extends AbstractDependencyInjectionSpringContextTests
        {
            // the aspect that will have around advice applied
            @Aspect
            public static class DogWatcher
            {
                protected boolean observed;
        
                @Around("this(TestAnnotationAdvice$IDog) && execution(public * *(..)) && @annotation(annotation)")
                public Object observeWoof(ProceedingJoinPoint pjp, Collared annotation) throws Throwable
                {
                    Object result = pjp.proceed();
                    observed = true;
                    return result;
                }
            }
        
            // the target class
            public static class Dog implements IDog
            {
                @Collared
                @Transactional
                public Boolean bark()
                {
                    return Boolean.TRUE;
                }
            }
        
            // the interface that the target implements
            public static interface IDog
            {
                public Boolean bark();
            }
        
            @Retention(RetentionPolicy.RUNTIME)
            @Target(ElementType.METHOD)
            public static @interface Collared {
            }
        
            private IDog dog;
            private DogWatcher watcher;
        
            /**
             * Tests that when also using @Transactional, the advice is also executed
             */
            public void testFiresWhenAlsoUsingTransactionalAnnotation()
            {
                dog.bark();
                assertTrue(watcher.observed);
            }
        
            @Override
            protected String[] getConfigLocations()
            {
                return new String[]
                {
                    "classpath:applicationContextAspectTestCase.xml"
                };
            }
        
            public void setDog(IDog dog)
            {
                this.dog = dog;
            }
        
            public void setWatcher(DogWatcher watcher)
            {
                this.watcher = watcher;
            }
        
        }
        And the environment XML file (you might need to change the database settings, of course):

        Code:
        <beans xmlns="http://www.springframework.org/schema/beans"
        	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        	xmlns:aop="http://www.springframework.org/schema/aop"
        	xmlns:tx="http://www.springframework.org/schema/tx"
        	xsi:schemaLocation="
               http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
               http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd
               http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">
        
        	<!-- comment out the tx:annotation-driven in order to get the integration test to pass -->
        	<tx:annotation-driven transaction-manager="txManager"/>
        	
        	<bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        		<property name="sessionFactory" ref="sessionFactory" />
        	</bean>
        	<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
        		<property name="dataSource">
        			<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        				<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        				<property name="url" value="jdbc:mysql://localhost:3306/mydatabase?useUnicode=true&amp;amp;characterEncoding=UTF-8"/>
        				<property name="username" value="root"/>
        				<property name="password" value=""/>
        			</bean>
        		</property>
        	</bean>
        	
        	<bean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator"/>
        	<bean class="TestAnnotationAdvice$DogWatcher"/>
        	<bean class="TestAnnotationAdvice$Dog"/>
        </beans>

        Comment


        • #5
          Tested your application and there seems to be a problem. Test your case with <aop:aspectj-autoproxy /> instead of AnnotationAwareAspectJAutoProxyCreator. I'll create a JIRA issue for this.

          Comment


          • #6
            We found that it works when we use

            <aop:aspectj-autoproxy/>

            Instead of

            <bean class="org.springframework.aop.aspectj.annotation. AnnotationAwareAspectJAutoProxyCreator"/>

            I was under the assumption that these two were more or less the same. Maybe now, a little less than more

            Comment


            • #7
              Originally posted by Andrei Stefan View Post
              Tested your application and there seems to be a problem. Test your case with <aop:aspectj-autoproxy /> instead of AnnotationAwareAspectJAutoProxyCreator. I'll create a JIRA issue for this.
              Thanks for your help Andrei.

              Comment


              • #8
                http://opensource.atlassian.com/proj...rowse/SPR-3714

                Comment

                Working...
                X