2017-02-18 36 views
0

Я переношу конфигурацию менеджера кэшей 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.

Это иерархия классов java для клиента Couchbase.

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-конфигурации?

Может ли кто-нибудь узнать, что здесь происходит?

Спасибо!

ответ

2

Весна улучшила класс 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-компонента, потому что нет никакого прокси-сервера, созданного Spring, с возвращаемым значением метода mainCouchbaseCache.

Я думаю, вы можете исправить это, просто сделав основной кэш CatchbaseCache вместо CouchbaseCache. Затем во время компиляции Arrays.asList ожидает массив Cache вместо массива CouchbaseCache, и поскольку возвращаемый прокси-сервер будет реализовывать Cache, все будет работать.

Или вы можете сказать, что Spring не использует динамический прокси JDK; см. Spring documentation on how it generates proxies. Я думаю, вы хотите использовать proxy-target-class = true, который расскажет Spring использовать CGLIB для создания реального подкласса CouchbaseCache.

+0

Да, смена возвращаемого типа на работу кеша. Большое спасибо за разъяснение причины, я не знал, что динамические прокси-серверы JDK могут реализовывать только интерфейсы, но не расширять классы. Но я не понимаю, почему этот прокси не требуется, когда я использую конфигурацию xml. –

+0

Я думаю, что это как-то связано с аннотациями кэширования, которые вы добавили в класс CacheConfiguration. Я не думаю, что вам нужно включить кеширование в CacheConfiguration, поскольку Spring управляет жизненным циклом компонентов. –

+0

AFAIK EnableCaching аннотации активирует аннотацию Cacheable –