Announcement Announcement Module
Collapse
No announcement yet.
interceptors and autodetected components Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • interceptors and autodetected components

    Hello,

    I'm using an annotation-based Spring configuration - my bean classes are annotated with @Service and @Repository and use @Autowired on their constructors, and in my XML configuration I only have some <context:component:scan>-Elements. This works fine so far.

    Now I want to add interceptors to my beans, in particular the org.springframework.aop.interceptor.DebugIntercept or, roughly as described here:

    http://java.dzone.com/articles/using-spring-aop-trace

    The example given there, however, uses explicitly defined beans. How can I add a DebugInterceptor to my autodetected components? I've seen some MVC-specific solution using org.springframework.web.servlet.mvc.annotation.Def aultAnnotationHandlerMapping. I'm not using MVC, however, so I am looking for a more general approach.

  • #2
    Just remember that using @Resource (or @Service, @Controller etc.) on your classes and component-scanning for their packages is the same as defining the classes as beans in xml: they become registered components in Spring context and so they are advisable by Spring AOP, it doesn't matter if you configure aop in xml or via annotations.
    So you could use xml-defined aop also for annotation-defined beans; nonetheless,new annotated aop configuration using AspectJ support is cool. If you want to learn it, you can refer to the official Spring 3 reference documentation, chapter 7.2 (pag. 187 & following).

    Comment


    • #3
      Hello Enrico,

      thank you for setting me on the right track!

      Beside a mistake of my own (@Autowired fields must have an interface type, I know...), here's what I had to add to my configuration:

      <aop:config>
      <aop:advisor advice-ref="debugLog" pointcut="execution(* *.*(..))"/>
      </aop:config>

      <aop:aspectj-autoproxy/>

      <bean id="debugLog" class="org.springframework.aop.interceptor.SimpleT raceInterceptor">
      <property name="useDynamicLogger" value="true" />
      </bean>

      This works pretty well, except for one nasty side-effect: I have unit tests that are derived from AbstractTransactionalTestNGSpringContextTests. So all these tests are supposed to run in a transaction that is rolled back at the end of the test. Using the pointcut as given above, however, the transaction is committed!? I have no clue how adding a TraceInterceptor can change a completely unrelated feature in such a way. It's really just a matter of the pointcut. If I change it to

      pointcut="execution(* com.mycompany.*.*(..))"

      then the transactions are rolled back as expected, but unfortunately the trace does not work either. Looks like I need to use another package name.

      Any idea on this?

      Comment


      • #4
        This is strange indeed. Can you post your aspect class and your full configuration? (remember to use [ CODE ] [ /CODE ] tags to post your code).

        Also, have you set the log to debug or trace level? It should give some useful information about what's really happening...

        Comment


        • #5
          Ok, here's my spring config:

          Code:
          <?xml version="1.0" encoding="UTF-8"?>
          <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:context="http://www.springframework.org/schema/context"
          	xmlns:tx="http://www.springframework.org/schema/tx"
          	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
          		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
          		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
          		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
          
          	<aop:config>
          		<aop:advisor advice-ref="debugLog" pointcut="execution(* *.*(..))"/>
          	</aop:config>
          	
          	<aop:aspectj-autoproxy/>
          
          	<context:property-placeholder location="classpath:database.properties"/>
          	
          	<context:component-scan base-package="com.mycompany.myproject.backend.services.impl" >
          		<context:include-filter type="annotation" expression="org.springframework.stereotype.Service" />
          	</context:component-scan>
          
          	<context:component-scan base-package="com.mycompany.myproject.backend.dao.hibernate">
          		<context:include-filter type="annotation" expression="org.springframework.stereotype.Repository" />
          	</context:component-scan>
          
          	<bean id="debugLog" class="org.springframework.aop.interceptor.SimpleTraceInterceptor">
          		<property name="useDynamicLogger" value="true" />
          	</bean>
          	
          	<tx:annotation-driven />
          
          	<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
          		<property name="driverClassName" value="${hibernate.connection.driver_class}" />
          		<property name="url" value="${hibernate.connection.url}" />
          		<property name="username" value="${hibernate.connection.username}" />
          		<property name="password" value="${hibernate.connection.password}" />
          	</bean>
          
          	<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
          		<property name="dataSource">
          			<ref local="dataSource" />
          		</property>
          		<property name="configLocation">
          			<value>classpath:hibernate.cfg.xml</value>
          		</property>
          		<property name="entityInterceptor">
          			<bean class="com.mycompany.myproject.backend.util.hibernate.AuditInterceptor" />
          		</property>
          	</bean>
          
          	<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
          		<property name="sessionFactory" ref="sessionFactory" />
          	</bean>
          	<bean id="transactionProxy" abstract="true" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
          		<property name="transactionManager">
          			<ref local="transactionManager" />
          		</property>
          	</bean>
          </beans>
          So you see it's not a custom-made aspect, it's just Spring AOP's SimpleTraceInterceptor.

          I omit the DAO classes, they are really straight forward. As stated before they all are annotated with @Repoository.

          And here's the test code (imports omitted for brevity). The purpose of initSession() is to clean the DB (only within the transaction, of course) to avoid side effects with existing objects, and then create one single object we need initially:

          Code:
          package com.mycompany.myproject.backend.dao.hibernate;
          
          @ContextConfiguration(locations = { "classpath:applicationContext-backend.xml" })
          public abstract class AbstractDAOTest extends AbstractTransactionalTestNGSpringContextTests
          {
          	@Autowired
          	protected SessionFactory sessionFactory;
          	
          	@Autowired 
          	private BenutzerDAO benutzerDAO;
          	
          	//...more DAOs injected
          	
          
          	@BeforeMethod
          	protected void initSession()
          	{
          		//cleanup Database
          		benutzerDAO.deleteAll();
          		
          		//...call deleteAll() on more DAOs
          		
          		sessionFactory.getCurrentSession().flush();
          		
          		Benutzer testUser = new Benutzer();
          		testUser.setKennung("00");
          		testUser.setNachname("Tester");
          		testUser.setVorname("Bester");
          		testUser.setPasswort("pass");
          		testUser.setRolle(Rolle.ARBEITSVORBEREITUNG);
          		
          		UserSession.setUser(testUser);
          		
          		benutzerDAO.saveOrUpdate(testUser);
          
          		sessionFactory.getCurrentSession().flush();
          	}
          
          }
          
          
          public class BenutzerDAOHibernateTest extends AbstractDAOTest 
          {
          	@Autowired
          	private BenutzerDAO benutzerDAO;
          	
          	@Test
          	public void testFindByNummer()
          	{
          		//ein passendes Objekt in der DB anlegen...
          		Benutzer benutzer = new Benutzer();
          		benutzer.setKennung("47");
          		benutzer.setPasswort("pass");
          		benutzer.setNachname("nachname");
          		benutzer.setVorname("vorname");
          		benutzerDAO.saveOrUpdate(benutzer);
          		
          		//...und finden!
          		Benutzer result = benutzerDAO.findByKennung("47");
          		assertNotNull(result);
          		assertSame(result, benutzer);
          		
          		result = benutzerDAO.findByKennung("48");
          		assertNull(result);
          	}
          
          }

          I have not found anything suspicious in the traces, regardless on how I define the pointcut. The traces always say that the transaction that the test runs in is rolled back. When I define the pointcut as pointcut="execution(* *.*(..))", however, the transaction is actually committed, and the SimpleTraceInterceptor is working (I see the output in the traces). When I define the pointcut as pointcut="execution(* com.mycompany.*.*(..))" (that's how I understand it is supposed to be for tracing all classes from mycompany) then no commit, but also no output from SimpleTraceInterceptor.

          Comment


          • #6
            A colleague of mine found the solution (though not an explanation): there is a bug in the pointcut expression, it should read

            pointcut="execution(* com.mycompany..*(..))"

            Mysetup is now working correctly. An explanation why one of the other pointcuts changed the transactional behavior is still wanted (but rather for curiosity).

            Comment

            Working...
            X