我正在将Couchbase缓存管理器配置从我们的旧xml迁移到Java Config。
但是我得到了一个java.lang.ClassCastException:com.sun.proxy。$ Proxy219无法转换为atorrico.cache.CouchbaseCache。
这是XML文件
<context:annotation-config />
<cache:annotation-driven />
<bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
<property name="caches">
<set>
<ref local="mainCache" />
</set>
</property>
</bean>
<bean id="mainCache" class="atorrico.cache.CouchbaseCache" destroy-method="shutdown">
<constructor-arg index="0" value="${cache.main.name}" />
<constructor-arg index="1" value="${cache.servers}" />
<constructor-arg index="2" value="${cache.main.bucket.name}" />
<constructor-arg index="3" value="${cache.main.bucket.password}" />
<constructor-arg index="4" ref="couchbaseJaxb2Transcoder" />
<constructor-arg index="5" value="${cache.main.ttl}" />
<property name="operationTimeoutMillis" value="${cache.main.operationTimeoutMillis}" />
<property name="clientResetIntervalSeconds" value="${cache.main.clientResetIntervalSeconds}" />
<property name="enabled" value="${cache.main.enabled}" />
</bean>
<bean id="couchbaseJaxb2Transcoder" class="atorrico.couchbase.CouchbaseJaxb2Transcoder">
<property name="marshaller" ref="cacheJaxb2Marshaller" />
</bean>
<bean id="cacheJaxb2Marshaller" class="atorrico.couchbase.TweakedJaxb2Marshaller">
<property name="contextPath"
value="${cache.main.contextPath}" />
</bean>
这是Java Config文件
@Configuration
@EnableCaching
@EnableMBeanExport
public class CacheConfiguration {
@Value("${cache.main.name}")
private String mainCacheName;
@Value("${cache.servers}")
private String mainCacheServers;
@Value("${cache.main.bucket.name}")
private String mainCacheBucketName;
@Value("${cache.main.bucket.password}")
private String mainCacheBucketPassword;
@Value("${cache.main.ttl}")
private Integer mainCacheTtl;
@Value("${cache.main.operationTimeoutMillis}")
private Integer mainCacheOperationTimeoutMillis;
@Value("${cache.main.clientResetIntervalSeconds : -1}")
private Integer mainClientResetIntervalSeconds;
@Value("${cache.main.enabled}")
private Boolean mainCacheEnabled;
@Value("${cache.main.operation.queue.length : -1}")
private Integer mainCacheOperationQueueLength;
@Value("${cache.main.contextPath}")
private Integer mainCacheContextPath;
@Bean
public CacheManager cacheManager() {
SimpleCacheManager cacheManager = new SimpleCacheManager();
cacheManager.setCaches(Arrays.asList(mainCouchbaseCache()));
return cacheManager;
}
@Bean(name = "mainCache", destroyMethod = "shutdown")
@Qualifier("mainCache")
public CouchbaseCache mainCouchbaseCache() {
CouchbaseCache couchbaseClient = new CouchbaseCache(mainCacheName, mainCacheServers, mainCacheBucketName, mainCacheBucketPassword,
mainCouchbaseJaxb2Transcoder(), mainCacheTtl);
couchbaseClient.setOperationTimeoutMillis(mainCacheOperationTimeoutMillis);
couchbaseClient.setClientResetIntervalSeconds(mainClientResetIntervalSeconds);
couchbaseClient.setEnabled(mainCacheEnabled);
couchbaseClient.setOperationQueueLength(mainCacheOperationQueueLength);
return couchbaseClient;
}
@Bean(name = "mainCouchbaseJaxb2Transcoder")
public CouchbaseJaxb2Transcoder mainCouchbaseJaxb2Transcoder() {
CouchbaseJaxb2Transcoder couchbaseJaxb2Transcoder = new CouchbaseJaxb2Transcoder();
couchbaseJaxb2Transcoder.setMarshaller(mainJaxb2Marshaller());
return couchbaseJaxb2Transcoder;
}
@Bean(name = "mainJaxb2Marshaller")
public TweakedJaxb2Marshaller mainJaxb2Marshaller() {
TweakedJaxb2Marshaller txStoreJaxb2Marshaller = new TweakedJaxb2Marshaller();
txStoreJaxb2Marshaller.setContextPath(mainCacheContextPath);
return txStoreJaxb2Marshaller;
}
我认为两个版本之间的唯一区别是在xml中,我有一个
<ref local="mainCache" />
注意使用本地而不是bean。
这是Couchbase客户端的Java类的层次结构。
public interface CouchbaseClient {
....
}
public interface CouchbaseClientManagement {
....
}
public class CouchbaseClientImpl implements CouchbaseClient, CouchbaseClientManagement {
.....
}
public class CouchbaseCache extends CouchbaseClientImpl implements Cache, CouchbaseClientManagement {
....
}
这是痕迹
Caused by: java.lang.ClassCastException: com.sun.proxy.$Proxy219 cannot be cast to atorrico.cache.CouchbaseCache
at atorrico.cache.configuration.CacheConfiguration$$EnhancerBySpringCGLIB$$dd4e20b8.mainCouchbaseCache(<generated>)
at atorrico.cache.configuration.CacheConfiguration.cacheManager(CacheConfiguration.java:76)
at atorrico.cache.configuration.CacheConfiguration$$EnhancerBySpringCGLIB$$dd4e20b8.CGLIB$cacheManager$0(<generated>)
at atorrico.cache.configuration.CacheConfiguration$$EnhancerBySpringCGLIB$$dd4e20b8$$FastClassBySpringCGLIB$$a8a6f2da.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:228)
at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:309)
at atorrico.cache.configuration.CacheConfiguration$$EnhancerBySpringCGLIB$$dd4e20b8.cacheManager(<generated>)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:162)
... 67 more
为什么Java Config失败,但XML Config可以正常工作?
谁能知道这是怎么回事?
谢谢!
最佳答案
Spring通过一些额外的字节码增强了CacheConfiguration类。可以通过创建作为CacheConfiguration子类的代理来完成此操作。您可以在堆栈跟踪中将此作为类CacheConfiguration $$ EnhancerBySpringCGLIB $$ dd4e20b8看到。
当Spring实例化CacheManager时,它首先通过在子类中生成的cacheManager方法进行调用,然后调用原始的cacheManager方法,该方法调用mainCouchbaseCache方法。
这就是开始变得有趣的地方。您的cacheManager方法不是调用mainCouchbaseCache方法,而是调用子类中生成的方法。生成的方法将调用mainCouchbaseCache,您的方法将生成CouchbaseCache对象并返回它。方法返回后,控制权返回到生成的子类,然后将其返回的CouchbaseCache包装在生成的代理中。
我不确定为什么Spring会生成代理,但是由于某种原因,它决定需要拦截对CouchbaseCache方法的调用。
问题在于,因为CouchbaseCache实现了某些接口,所以Spring创建了JDK动态代理,而JDK动态代理的局限在于它们只能实现接口,而不能扩展类。 (它们扩展了java.lang.reflect.Proxy。)因此,您的cacheManager方法看到的是从mainCouchbaseCache方法返回的值不是CouchbaseCache的实例,而是实现缓存的java.lang.reflect.Proxy的某些子类的实例。和CouchbaseClientManagement接口。
这适用于XML bean文件,因为没有Spring生成的代理与mainCouchbaseCache方法的返回值混淆。
我认为您可以通过使mainCouchbaseCache返回Cache而不是CouchbaseCache来解决此问题。然后,在编译时,Arrays.asList将期待一个Cache数组而不是CouchbaseCache数组,并且由于返回的代理将实现Cache,因此一切正常。
或者,您可以告诉Spring不要使用JDK动态代理。参见Spring documentation on how it generates proxies。我认为您想使用proxy-target-class = true,它将告诉Spring使用CGLIB生成实际的子类CouchbaseCache。