spring-boot-devtools 从缓存获取时导致 ClassCastException。

问题描述 投票:0回答:4

我在从缓存获取价值时遇到问题。

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

缓存配置

@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;
    }

}

并标注有

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

我正在使用 com.google.code.simple-spring-memcached 3.5.0

值正在被缓存,但在获取应用程序时会抛出类转换错误。可能会出现什么问题。

完整堆栈跟踪

java spring-boot memcached jhipster spring-cache
4个回答
14
投票

这是Devtools 的一个已知限制。当缓存条目被反序列化时,该对象不会附加到正确的类加载器。

有多种方法可以解决此问题:

  1. 在开发中运行应用程序时禁用缓存
  2. 使用不同的缓存管理器(如果您使用的是 Spring Boot 1.3,您可以使用
    simple
    中的
    spring.cache.type
    属性强制使用
    application-dev.properties
    缓存管理器,并在 IDE 中启用开发配置文件)
  3. 将 memcached(以及缓存的内容)配置为 在应用程序类加载器中运行。我不会推荐这个选项,因为上面的两个更容易实现

1
投票

我也遇到了同样的错误,但缓存不是原因。实际上我正在使用缓存,但是注释缓存并没有帮助。

根据这里和那里的提示,我刚刚引入了对象的附加序列化/反序列化。这绝对是最好的方法(性能问题),但它确实有效。

因此,对于其他人,我更改了代码:

@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;
}

注意:这不是任何复杂的解决方案,而只是使用 ConfigurableObjectInputStream 类的提示。


0
投票

在启用了 STS 插件的 eclipse 中运行项目时,我遇到了同样的问题。即使我从项目中完全删除了 devtools 依赖项。它在 eclipse 中仍然启用。为了解决这个问题,我必须禁用开发工具。


0
投票

问题是,当您使用开发工具时,类加载器被设置为与您正在使用的缓存提供程序/框架不同的类加载器。

如果您使用 JCache 或 EhCache,您只需确保使用相同的类加载器即可初始化缓存管理器。

更多信息和背景可以在这里找到

@Configuration
@EnableCaching
@Slf4j
public class CachingConfiguration {

    @Bean
    @Primary
    public CacheManager myCacheManager() {
     
    // The trick is to use "Thread.currentThread().getContextClassLoader()"
    // which overwrites the default class loader set internally by the cache provider
    // with this you ensure both Class loaders are the same when you are using the devtools 

        CachingProvider provider = Caching.getCachingProvider();
        CacheManager cacheManager = provider.getCacheManager(provider.getDefaultURI(), Thread.currentThread().getContextClassLoader());
        return cacheManager;
    }


    // Optional - in the same class or separate class you can verify the 
    // reload which gets initiated by the devtools. So you can check if the 
    // Class loader of your cache Manager and the one currently used by the
    // Spring Framework is the same
    
    @Autowired
    @Lazy
    CacheManager cacheManager;

    @EventListener
    public void handleContextRefreshEvent(ContextRefreshedEvent ctxEvt) {           ClassLoader loader1 = Thread.currentThread().getContextClassLoader();
            ClassLoader loader2 = cacheManager.getClassLoader();
            log.debug("{} = {}", loader1, loader2);
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.