Reputation: 11069
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
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
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
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