Dan
Dan

Reputation: 11069

How to get Spring to create AOP Proxies for my beans

In my Spring application, I have a whole bunch of beans (in this case DAOs) that are created as simple <bean>s in my XML configuration. In these methods are various annotations, including specifically @Transactional. I naturally have <tx:annotation-driven /> as well.

But for some of these objects - though only some of them - no proxies are created (I confirmed this by enabling debug logging) and the @Transactional annotation has no effect. Instead the objects that contain (usually autowired) references to these DAOs get wired a reference to the direct class, not a proxy.

All of the classes have corresponding interfaces and the autowired references are always through these interfaces.

I can't figure out which classes get the proxies and which don't. I want them all to. So my question is:

a) under what circumstances does Spring not create a proxy for a class even though it implements some interfaces?

b) how can I force Spring to create the proxies I need?

Note that I have not done anything to explicitly enable proxying, but I haven't needed to in the past. It usually just works.

Tried with both Spring 3.1.3 and 3.2.2.

I don't have a SSCCE for this. Essentially my XML is

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"       
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:sec="http://www.springframework.org/schema/security"    
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:ehcache="http://ehcache-spring-annotations.googlecode.com/svn/schema/ehcache-spring"                       
       xmlns:cache="http://www.springframework.org/schema/cache"       
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
       http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
       http://ehcache-spring-annotations.googlecode.com/svn/schema/ehcache-spring
       http://ehcache-spring-annotations.googlecode.com/svn/schema/ehcache-spring/ehcache-spring-1.2.xsd
       http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd
">

       <bean id="userDao" class="com.soschat.dao.spring.SpringUserDAO"/>

           <tx:annotation-driven transaction-manager="transactionManager" />

    <bean id="transactionManager" class=" org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
        <property name="globalRollbackOnParticipationFailure" value="false" />
    </bean>

... etc ...
</beans>

and my code is more or less

public class UserDaoImpl implements UserDao {

   @Override
   @Transactional
   @Cacheable
   public User getUserById(long userId) {
         // do stuff
   }
}

Not sure how much more I need to add in without excessive detail.

One interesting addition - I am able to force it to create the proxy using BeanNameAutoProxyCreator. But none of the annotations I put in there actually take effect.

Upvotes: 4

Views: 3225

Answers (3)

yayatip
yayatip

Reputation: 79

We encountered the same issue and realized that the bean was initialized before org.springframework.cache.ehcache.EhCacheCacheManager in the application start-up, hence @Cacheable was not working.

We ended up moving cache configuration to xml from java configuration class which forced (not sure how), EHCacheManager initialization before any of the application beans. We have both xml and java configuration in our application.

<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager" p:cache-manager-ref="ehcache"/>
    <bean id="ehcache" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean" p:config-location="classpath:ehcache.xml" p:shared="true"/>

Upvotes: 0

Dan
Dan

Reputation: 11069

Eventually I figured out that it was failing because of some kind of dependency chain starting with a bean that was somehow being loaded "too early" - in my case, a Spring Security PermissionEvaluator. I still don't have a SSCCE, but I'm adding this answer here in case someone else had a similar problem.

The solution was to use very-late binding. I made my PermssionEvaluator into ApplicationContextAware, then wrote a private method to load the local been references from the ApplicationContext, and this method is only called when it is first needed, which is not during initialization.

In other words I had a bean like

private AuthorizationServices authServices;

and then in my main method something like

if (authServices == null)
   initBeans();

and then

private void initBeans() {
    authServices = ac.getBean(AuthorizationServices.class);
}

Upvotes: 2

Sotirios Delimanolis
Sotirios Delimanolis

Reputation: 279890

The @Component annotation will tell Spring to create and process a bean of that class. You'll need to have your application (or other) context scan that class' package with the <component-scan> element.

@Service, @Repository, @Controller work just like @Component.

As for proxying, Spring doesn't proxy everything, only instances of classes it needs to add behavior to. For example, with @Transactional, it needs to add begin/commit/rollback transaction behavior. To do this it wraps your class' methods with its own code, so it needs to proxy. For a @Controller class, it doesn't need to add any behavior, so it'll simply instantiate your class.

Upvotes: 4

Related Questions