2016-01-03 1 views
6

Je suis confronté à un problème tout en obtenant la valeur du cache.spring-boot-devtools provoquant l'exception ClassCastException à partir du cache.

java.lang.RuntimeException: java.lang.ClassCastException: com.mycom.admin.domain.User cannot be cast to com.mycom.admin.domain.User 

Configuration du cache

@Configuration 
@EnableCaching 
@AutoConfigureAfter(value = { MetricsConfiguration.class, DatabaseConfiguration.class }) 
@Profile("!" + Constants.SPRING_PROFILE_FAST) 
public class MemcachedCacheConfiguration extends CachingConfigurerSupport { 

    private final Logger log = LoggerFactory.getLogger(MemcachedCacheConfiguration.class); 

    @Override 
    @Bean 
    public CacheManager cacheManager() { 
     ExtendedSSMCacheManager cacheManager = new ExtendedSSMCacheManager(); 
     try { 
      List<SSMCache> list = new ArrayList<>(); 
      list.add(new SSMCache(defaultCache("apiCache"), 86400, false)); 
      cacheManager.setCaches(list); 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 
     return cacheManager; 
    } 


    @Override 
    public CacheResolver cacheResolver() { 
     return null; 
    } 

    @Override 
    public CacheErrorHandler errorHandler() { 
     return null; 
    } 

    private Cache defaultCache(String cacheName) throws Exception { 
     CacheFactory cacheFactory = new CacheFactory(); 
     cacheFactory.setCacheName(cacheName); 
     cacheFactory.setCacheClientFactory(new MemcacheClientFactoryImpl()); 
     String serverHost = "127.0.0.1:11211"; 
     cacheFactory.setAddressProvider(new DefaultAddressProvider(serverHost)); 
     cacheFactory.setConfiguration(cacheConfiguration()); 
     return cacheFactory.getObject(); 
    } 

    @Bean 
    public CacheConfiguration cacheConfiguration() { 
     CacheConfiguration cacheConfiguration = new CacheConfiguration(); 
     cacheConfiguration.setConsistentHashing(true); 
     return cacheConfiguration; 
    } 

} 

et annotés avec

@Cacheable(value = "apiCache#86400", key = "'User-'.concat(#login)") 

J'utilise com.google.code.simple-printemps-memcached 3.5.0

Valeur devient mis en cache mais lors de l'obtention de l'application renvoie erreur de distribution de classe. Quels seraient les problèmes possibles?

Full stack trace

+2

Avez-vous le même 'class' sur votre classpath deux fois? Ou l'avez-vous chargé à partir de plusieurs chargeurs de classe (ou exemple dans un environnement WebApp). La cause habituelle d'une classe ne peut pas être convertie en elle-même. Les problèmes sont que les classes sont chargées à partir d'endroits différents ... –

+1

A première vue, cela ressemble à un problème de ClassLoader. On dirait que vous avez deux classloaders différents qui ont chargé la même classe. – sisyphus

+0

@sisyphus j'utilise spring boot + devtools. J'ai lu certains où devtools maintient un chargeur de classe pour les pots statiques et un pour le code de l'application. Cela causerait-il problème? – titogeo

Répondre

7

C'est a known limitation of Devtools. Lorsque l'entrée de cache est désérialisée, l'objet n'est pas attaché au classloader approprié.

Il y a différentes façons dont vous pouvez résoudre ce problème:

  1. Désactivation du cache lorsque vous utilisez votre application dans le développement
  2. Utilisez un gestionnaire de cache différent (si vous utilisez Spring Boot 1.3, vous peut forcer un gestionnaire de cache simple en utilisant la propriété spring.cache.type dans application-dev.properties et activer le profil de développement dans votre IDE)
  3. Configurer memcached (et les éléments mis en cache) à run in the application classloader. Je ne recommanderais pas cette option puisque les deux premiers ci-dessus sont beaucoup plus faciles à implémenter
1

Eh bien j'ai eu la même erreur, mais la mise en cache n'était pas la raison. En fait j'utilisais la mise en cache, mais le commentaire de la mise en cache n'a pas aidé. Basé sur les indices ici et là, j'ai juste introduit la sérialisation/dérialisation supplémentaire de mon objet. C'est définitivement le meilleur moyen (le problème de performance), mais ça marche.

Alors, juste pour les autres j'ai changé mon code de:

@Cacheable("tests") 
public MyDTO loadData(String testID) { 
    // add file extension to match XML file 
    return (MyDTO) this.xmlMarshaller.loadXML(String.format("%s/%s.xml", xmlPath, testID)); 
} 

à:

@Cacheable("tests") 
public MyDTO loadData(String testID) { 
    // add file extension to match XML file 
    Object dtoObject = this.xmlMarshaller.loadXML(String.format("%s/%s.xml", xmlPath, testID)); 
    byte[] data = serializeDTO(dtoObject); 
    MyDTO dto = deserializeDTO(data); 
    return dto; 
} 

private MyDTO deserializeDTO(byte[] data) { 
    MyDTO dto = null; 
    try { 
     ByteArrayInputStream fileIn = new ByteArrayInputStream(data); 
     ObjectInputStream in = new ConfigurableObjectInputStream(fileIn, 
       Thread.currentThread().getContextClassLoader()); 
     dto = (MyDTO) in.readObject(); 
     in.close(); 
     fileIn.close(); 
    } catch (Exception e) { 
     String msg = "Deserialization of marshalled XML failed!"; 
     LOG.error(msg, e); 
     throw new RuntimeException(msg, e); 
    } 
    return dto; 
} 

private byte[] serializeDTO(Object dtoObject) { 
    byte[] result = null; 
    try { 
     ByteArrayOutputStream data = new ByteArrayOutputStream(); 
     ObjectOutputStream out = new ObjectOutputStream(data); 
     out.writeObject(dtoObject); 
     out.close(); 
     result = data.toByteArray(); 
     data.close(); 
    } catch (IOException e) { 
     String msg = "Serialization of marshalled XML failed!"; 
     LOG.error(msg, e); 
     throw new RuntimeException(msg, e); 
    } 

    return result; 
} 

Note: ce n'est pas une solution Sophistiqué, mais juste la pointe de la classe utilisation de ConfigurableObjectInputStream .