Oscar Borrallo
Oscar Borrallo

Reputation: 1

Error creating bean implementing ApplicationListener with @Cacheable method

We've been having issues while deploying our application, and, after a while of research, we've found the problem source, but we don't know why is it happening.

The application tries to wire a bean 'bar' on a bean 'foo' on Spring context initialization. 'Bar' bean implements interface ApplicationListener, and has a @Cacheable method as well. For some reason we don't know, there's a crash on bean creation. Removing @Cacheable or making the bean not being an instance of ApplicationListener makes the app running OK.

Maybe is a misconception of ApplicationListener and/or @Cacheable, but what's the reason of this error to occur? Why is 'bar' bean 'illegal'? Spring version is 3.2.9.

Spring context file

<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:cache="http://www.springframework.org/schema/cache"
   xmlns:task="http://www.springframework.org/schema/task"
   xsi:schemaLocation="http://www.springframework.org/schema/beans
   http://www.springframework.org/schema/beans/spring-beans.xsd
   http://www.springframework.org/schema/cache
   http://www.springframework.org/schema/cache/spring-cache.xsd
   http://www.springframework.org/schema/task
   http://www.springframework.org/schema/task/spring-task.xsd">

<bean id="applicationContextProvder" class="com.mycompany.ApplicationContextProvider"/>

<!-- CACHE -->
<cache:annotation-driven cache-manager="springEhCacheManager"/>

<bean id="ehCacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
    <property name="configLocation" value="/WEB-INF/conf/ehcache.xml"/>
</bean>

<bean id="springEhCacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">
    <constructor-arg ref="ehCacheManager"/>
</bean>

<bean id="cacheProvider" class="org.springmodules.cache.provider.ehcache.EhCacheFacade">
    <property name="cacheManager" ref="ehCacheManager"/>
</bean>

<bean id="cacheService" class="com.myapp.cache.DefaultEHCacheServiceImp">
    <constructor-arg index="0" ref="ehCacheManager"/>
</bean>

<!-- ALIVE -->
<bean id="voldemortAlive"
      class="com.myapp.voldemort.VoldemortAliveImp">
</bean>

<bean id="atlasAlive"
      class="com.myapp.atlas.AtlasAliveImp">
</bean>



<task:annotation-driven executor="myExecutor" scheduler="myScheduler"/>
<task:executor id="myExecutor" pool-size="5"/>
<task:scheduler id="myScheduler" pool-size="10"/>

<!-- custom beans defined -->

<bean id="updatersSynchronizedTaskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor"/>
<bean id="updatersSynchronizedTaskExporter" class="org.springframework.jmx.export.MBeanExporter" lazy-init="false"/>

Foo class

package com.foo

@Service
public class Foo {

    @Autowired
    private Bar bar;

    ... lots of stuff here ...

    @PostConstruct
    protected void init() {
         ... method body ...
    }

... more logic ...

}

Bar class

package com.bar

@Repository
public class Bar extends BarParent implements ApplicationListener<BarEvent>   {

... more stuff ...

    @Cacheable("cache_name")
    public List<Baz> cachedMethod(final Integer code) {
        ... some logic here ...
    }
...
}

Exception given

Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.bar.Bar com.foo.Foo.bar; nested exception is java.lang.IllegalArgumentException: Can not set com.bar.Bar field com.foo.Foo.bar to $Proxy86
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:517)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:87)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:286)
... 63 more
Caused by: java.lang.IllegalArgumentException: Can not set com.bar.Bar field com.foo.Foo.bar to $Proxy86
at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:146)
at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:150)
at sun.reflect.UnsafeObjectFieldAccessorImpl.set(UnsafeObjectFieldAccessorImpl.java:63)
at java.lang.reflect.Field.set(Field.java:657)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:513)
... 65 more

Thank you in advance.

Upvotes: 0

Views: 953

Answers (1)

manish
manish

Reputation: 20135

Change

<cache:annotation-driven cache-manager="springEhCacheManager"/>

to

<cache:annotation-driven cache-manager="springEhCacheManager" proxy-target-class="true"/>

Since one of the methods is @Cacheable, Spring generates a proxy for Bar. Since the default proxy generation strategy is based on Java dynamic proxies and Bar implements the interface ApplicationListener, the generated proxy is a subtype of ApplicationListener and not Bar. Therefore, when @Autowired Bar is encountered, Spring attempts to inject a proxy instance that is not a subtype of Bar, hence the error.

By setting proxy-target-class to true, Spring is forced to generate class based proxies using CGLIB and @Autowired Bar will inject a Bar instance.

Upvotes: 2

Related Questions