Results 1 to 6 of 6

Thread: interceptors and autodetected components

  1. #1
    Join Date
    Dec 2010
    Posts
    4

    Default 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. #2
    Join Date
    Jul 2010
    Location
    Venice, Italy
    Posts
    709

    Default

    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).

  3. #3
    Join Date
    Dec 2010
    Posts
    4

    Default

    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?

  4. #4
    Join Date
    Jul 2010
    Location
    Venice, Italy
    Posts
    709

    Default

    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...

  5. #5
    Join Date
    Dec 2010
    Posts
    4

    Default

    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.

  6. #6
    Join Date
    Dec 2010
    Posts
    4

    Default

    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).

Posting Permissions

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