com.sun.proxy.$Proxy219 无法使用 Java 配置进行投射,但它与 xml 一起工作正常

com.sun.proxy.$Proxy219 cannot be cast using Java Config but it's working fine with xml

我正在将 Couchbase 缓存管理器配置从我们的遗留 xml 迁移到 Java 配置。

但我得到一个 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配置文件

@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" />

注意使用 local 而不是 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[=17=](<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 配置失败但 XML 配置正常?

有人能知道这里发生了什么吗?

谢谢!

Spring 用一些额外的字节代码增强了您的 CacheConfiguration class。这可能是通过创建一个作为 CacheConfiguration 的子 class 的代理来完成的。您可以在堆栈跟踪中看到它作为 class CacheConfiguration$$EnhancerBySpringCGLIB$$dd4e20b8.

当Spring实例化CacheManager时,它首先通过它在subclass中生成的cacheManager方法向下调用,然后调用你原来的cacheManager方法,它调用mainCouchbaseCache方法

这里开始变得有趣了。您的 cacheManager 方法调用的不是您的 mainCouchbaseCache 方法,而是调用 subclass 中生成的方法。生成的方法调用您的 mainCouchbaseCache,您的方法生成 CouchbaseCache 对象并 returns 它。在你的方法 returns 之后,将 returns 控制到生成的子 class,然后 将 returned CouchbaseCache 包装在生成的代理中 .

我不确定为什么 Spring 正在生成代理,但出于某种原因决定它需要拦截对 CouchbaseCache 方法的调用。

问题是因为CouchbaseCache实现了一些接口,Spring创建了一个JDK动态代理,而JDK动态代理的一个限制是只能实现接口,不能扩展 classes。 (它们扩展 java.lang.reflect.Proxy。)因此,您的 cacheManager 方法从 mainCouchbaseCache 方法中看到 return 的值不是 CouchbaseCache 的实例,而是 [=31 的某些子 class 的实例=] 实现 Cache 和 CouchbaseClientManagement 接口。

这适用于 XML bean 文件,因为没有 Spring-generated 代理干扰 mainCouchbaseCache 方法的 return 值。

我认为您可以通过制作 mainCouchbaseCache return 缓存而不是 CouchbaseCache 来解决这个问题。然后在编译时 Arrays.asList 将期望缓存数组而不是 CouchbaseCache 数组,并且由于 returned 代理将实现缓存,所以一切都会正常工作。

或者您可以告诉 Spring 不要使用 JDK 动态代理;见Spring documentation on how it generates proxies。我认为您想使用 proxy-target-class=true,这将告诉 Spring 使用 CGLIB 生成实际的子 class CouchbaseCache。