在Spring 4中不使用XML使用EhCache

40

是否有一种方法可以在Spring 4或Spring Boot中不使用xml来初始化EhCache?

我注意到Spring Boot 1.0.0.RC3没有任何ehcache依赖项,但是 Spring 4.0GA release post 提到它已经改进了对EhCache的支持。此外,Spring 3中有类org.springframework.cache.ehcache.EhCacheCacheManager,但它不再是依赖项的一部分。

编辑: Spring 4确实支持EhCache。您必须添加以下依赖项:

<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>

编辑2: 我尝试了以下方法,我认为我接近成功了,但是我遇到了一个错误:

@Bean
@Override
public CacheManager cacheManager() {
    CacheConfiguration cacheConfiguration = new CacheConfiguration();
    cacheConfiguration.setName("primary");
    cacheConfiguration.setMemoryStoreEvictionPolicy("LRU");
    cacheConfiguration.setMaxEntriesLocalHeap(0);

    net.sf.ehcache.config.Configuration config = new net.sf.ehcache.config.Configuration();
    config.addCache(cacheConfiguration);

    net.sf.ehcache.CacheManager cacheManager = new net.sf.ehcache.CacheManager(config);
    cacheManager.setName("EhCache");

    return new EhCacheCacheManager(cacheManager);
}

@Bean
public EhCacheManagerFactoryBean factoryBean() {
    return new EhCacheManagerFactoryBean();
}

错误

Caused by: net.sf.ehcache.CacheException: Another unnamed CacheManager already exists in the same VM. Please provide unique names for each CacheManager in the config or do one of following:
1. Use one of the CacheManager.create() static factory methods to reuse same CacheManager with same name or create one if necessary
2. Shutdown the earlier cacheManager before creating new one with same name.
The source of the existing CacheManager is: [Programmatically configured]
    at net.sf.ehcache.CacheManager.assertNoCacheManagerExistsWithSameName(CacheManager.java:590)
    at net.sf.ehcache.CacheManager.init(CacheManager.java:384)
    at net.sf.ehcache.CacheManager.<init>(CacheManager.java:263)
    at org.springframework.cache.ehcache.EhCacheManagerFactoryBean.afterPropertiesSet(EhCacheManagerFactoryBean.java:166)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1612)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1549)
    ... 15 more

Spring4仍然有那个类,所以不确定你从哪里得到它不再工作的想法。它在spring-context-support jar中。请参考https://github.com/spring-projects/spring-framework/tree/master/spring-context-support/src/main/java/org/springframework/cache/ehcache。 - M. Deinum
2个回答

52

Spring中无需XML配置EhCache

@Configuration
@EnableCaching
public class CachingConfig implements CachingConfigurer {
    @Bean(destroyMethod="shutdown")
    public net.sf.ehcache.CacheManager ehCacheManager() {
        CacheConfiguration cacheConfiguration = new CacheConfiguration();
        cacheConfiguration.setName("myCacheName");
        cacheConfiguration.setMemoryStoreEvictionPolicy("LRU");
        cacheConfiguration.setMaxEntriesLocalHeap(1000);

        net.sf.ehcache.config.Configuration config = new net.sf.ehcache.config.Configuration();
        config.addCache(cacheConfiguration);

        return net.sf.ehcache.CacheManager.newInstance(config);
    }

    @Bean
    @Override
    public CacheManager cacheManager() {
        return new EhCacheCacheManager(ehCacheManager());
    }

    @Bean
    @Override
    public KeyGenerator keyGenerator() {
        return new SimpleKeyGenerator();
    }

    @Bean
    @Override
    public CacheResolver cacheResolver()    {
        return new SimpleCacheResolver();
    }

    @Bean
    @Override
    public CacheErrorHandler errorHandler() {
         return new SimpleCacheErrorHandler();
    }
}

如果需要进行测试,您可以使用以下简单的ConcurrentMapCache(不使用XML)。

@Configuration
@EnableCaching
public class CachingConfig implements CachingConfigurer {
    @Bean
    @Override
    public CacheManager cacheManager() {
        SimpleCacheManager cacheManager = new SimpleCacheManager();

        List<Cache> caches = new ArrayList<Cache>();
        caches.add(new ConcurrentMapCache("myCacheName"));
        cacheManager.setCaches(caches);

        return cacheManager;
    }

    @Bean
    @Override
    public KeyGenerator keyGenerator() {
        return new SimpleKeyGenerator();
    }

    @Bean
    @Override
    public CacheResolver cacheResolver()    {
        return new SimpleCacheResolver();
    }

    @Bean
    @Override
    public CacheErrorHandler errorHandler() {
         return new SimpleCacheErrorHandler();
    }
}

编辑:更新底层缓存的关闭方法。

编辑:添加了错误处理程序和缓存解析器的配置。

编辑:更改SimpleCacheResolver上的构造函数调用,解决了CacheManager must not be null问题(Spring v5.1+)。

@Configuration
@EnableCaching
public class CachingConfig implements CachingConfigurer {

    public static final String USER_CACHE_INSTANCE = "my-spring-ehcache";
    private final CacheManager cacheManager;
    private final net.sf.ehcache.CacheManager ehCacheManager;


    public CachingConfig() {
        CacheConfiguration cacheConfiguration = new CacheConfiguration();
        cacheConfiguration.setName(USER_CACHE_INSTANCE);
        cacheConfiguration.setMemoryStoreEvictionPolicy("LRU");
        cacheConfiguration.setMaxEntriesLocalHeap(1000);
        net.sf.ehcache.config.Configuration config
                = new net.sf.ehcache.config.Configuration();
        config.addCache(cacheConfiguration);
        this.ehCacheManager = net.sf.ehcache.CacheManager.newInstance(config);
        this.cacheManager = new EhCacheCacheManager(ehCacheManager);
    }

    @Bean(destroyMethod="shutdown")
    public net.sf.ehcache.CacheManager ehCacheManager() {
        return ehCacheManager;
    }

    @Bean
    @Override
    public CacheManager cacheManager() {
        return cacheManager;
    }

    @Bean
    @Override
    public KeyGenerator keyGenerator() {
        return new SimpleKeyGenerator();
    }

    @Bean
    @Override
    public CacheResolver cacheResolver() {
        return new SimpleCacheResolver(cacheManager);
    }

    @Bean
    @Override
    public CacheErrorHandler errorHandler() {
        return new SimpleCacheErrorHandler();
    }
}

1
@KevinM,你也可以使用Spring Profile来实现这个功能。使用它将根据活动配置文件启动其中一个。http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/annotation/Profile.html - Erich
1
@KevinM 我会将它们拆分为两个单独的配置类,并对每个进行注释。 - Erich
1
Spring 4.2.5 org.springframework.cache.annotation.CachingConfigurer 需要实现两个额外的方法才能与 EHCache 3.x 兼容: cacheResolvererrorHandler。如果能在答案中涵盖这些方法就更好了 :) - J.Olufsen
4
@Hadi 遇到了同样的问题。我通过将 CachingConfig 类中的代码从 return new SimpleCacheResolver(); 改为 return new SimpleCacheResolver(cacheManager()); 来解决该问题。 - Santiago Tórtora
1
@SantiagoTórtora 当你调用 cacheManager() 时,每次都会实例化一个新的对象,例如 Spring 在创建 CacheManager bean 时会调用一次以填充 IoC bean 上下文,然后在创建 SimpleCacheResolver bean 时再次调用。我将在构造函数中创建 cacheManager 并将其保留为 CacheConfig 的成员变量,这在 Spring 创建它作为单例时是可以的。 - Adam
显示剩余5条评论

23

我会在两个抽象级别上进行操作,即每种技术一个配置文件(例如Ehcache、Redis等)和一个通用配置文件。

以下是Ehcache的配置文件示例(Redis类似):

@Configuration
public class EhCacheConfiguration {

    @Bean
    public EhCacheCacheManager ehCacheCacheManager() {

        return new EhCacheCacheManager(ehCacheManagerFactoryBean().getObject());
    }


    @Bean
    public EhCacheManagerFactoryBean ehCacheManagerFactoryBean() {

        EhCacheManagerFactoryBean cacheManagerFactoryBean = new EhCacheManagerFactoryBean();

        cacheManagerFactoryBean.setConfigLocation(new ClassPathResource("ehcache.xml"));
        cacheManagerFactoryBean.setShared(true);

        return cacheManagerFactoryBean;
    }
}

这里是通用的代码(包括Redis钩子):

@Configuration
@EnableCaching
public class CachingConfiguration implements CachingConfigurer {

    @Qualifier("ehCacheCacheManager")
    @Autowired(required = false)
    private CacheManager ehCacheCacheManager;

    @Qualifier("redisCacheManager")
    @Autowired(required = false)
    private CacheManager redisCacheManager;


    @Bean
    @Override
    public CacheManager cacheManager() {

        List<CacheManager> cacheManagers = Lists.newArrayList();

        if (this.ehCacheCacheManager != null) {
            cacheManagers.add(this.ehCacheCacheManager);
        }

        if (this.redisCacheManager != null) {
            cacheManagers.add(this.redisCacheManager);
        }

        CompositeCacheManager cacheManager = new CompositeCacheManager();

        cacheManager.setCacheManagers(cacheManagers);
        cacheManager.setFallbackToNoOpCache(false);

        return cacheManager;
    }


    @Bean
    @Override
    public KeyGenerator keyGenerator() {

        return new DefaultKeyGenerator();
    }
}

1
+1 喜欢抽象化的想法。会去尝试。 - Erich
我需要将这两个缓存管理器结合起来;虽然对于您的需求来说可能有些过度,但是将复合体移除应该很简单。 - Emerson Farrugia
我也喜欢这个想法,但是我在 org.springframework.cache.CacheManager 和 net.sf.ehcache.CacheManager 之间感到困惑。你如何将这两个实例放在同一个列表中? - Gabriel
@Gabriel https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/cache/ehcache/EhCacheCacheManager.html,实现了org.springframework.cache.CacheManager。 - Emerson Farrugia

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接