使用 Spring缓存抽象 支持 EhCache 和 Redis 混合部署

来源:互联网 发布:阿里云ecs搭建淘宝客 编辑:程序博客网 时间:2024/05/16 12:39

概述

在分布式 web 项目中,通常即需要本地缓存提高程序性能,也需要共享缓存在多机之间交换数据。本文介绍了使用Spring缓存抽象机制混合部署的方法。

Spring 3.1 引入了激动人心的基于注释(annotation)的缓存(cache)技术,使用Spring缓存抽象,程序员仅需要使用少量的注释就实现了对象的缓存,不必了解复杂的面向对象的编程(AOP)技术。

Spring 的缓存技术相当的灵活性,不仅能够使用 SpEL(Spring Expression Language)来定义缓存的 key 和各种 condition, 而且支持 Ehcache 2.x, Gemfire cache, Caffeine, Guava caches 以及 JSR-107 兼容的缓存 (例如 Ehcache 3.x)产品。

本文第一部分简单介绍注释驱动的缓存技术,第二部分介绍使用 Redis 做共享缓存,第三部分介绍混合部署。

第一部分:注释驱动的缓存技术简介

这里假设你对Java缓存有一些了解,这里主要介绍 Spring4,EhCache3 的配置方法。其他配置请参考 Spring官网(英文)文档:

1. 配置

配置类如下:

@Configuration@EnableCachingpublic class CacheConfig {// http://docs.spring.io/spring/docs/current/spring-framework-reference/html/cache.html#cache-store-configuration-jsr107//http://stackoverflow.com/questions/39386830/using-ehcache-3-with-spring-annotations-not-using-spring-boot@Beanpublic JCacheCacheManager jcacheCacheManager(){JCacheCacheManager cm = new JCacheCacheManager();cm.setCacheManager(jsr107cacheManager());return cm;}@Beanpublic CacheManager jsr107cacheManager(){//http://www.ehcache.org/documentation/3.1/107.htmlCachingProvider provider = Caching.getCachingProvider();CacheManager cacheManager = provider.getCacheManager();        MutableConfiguration<Long, String> configuration =        new MutableConfiguration<Long, String>()            // Cannot set type for store! this may be a bug in spring or ehCache        //.setTypes(Long.class, String.class)        .setStoreByValue(false)         .setExpiryPolicyFactory(CreatedExpiryPolicy.factoryOf(Duration.ONE_MINUTE));    cacheManager.createCache("foo", configuration);return cacheManager;}}

@Configuration 表示这是一个配置类

@EnableCaching 启动 Cache 注释

第一个 Bean 声明 Spring Cache 的 CacheManager 抽象管理接口对象。该对象是 JSR107 缓存规范的一个适配器(Adapter)

第二个 Bean 是 JSR107规范缓存的管理对象。Caching.getCachingProvider() 能正确找到注册的具体实现模块,如 EhCache3。然后创建了 “foo” 缓存区域。

2. 加载配置

在程序启动时:

public static void main(String[] args) {AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();ctx.register(PersistenceJDBCConfig.class);ctx.register(CacheConfig.class);ctx.register(RedisConfig.class);ctx.refresh();
如果你不了解 Spring 配置类的加载,建议阅读:使用 Java 配置进行 Spring bean 管理

3. Maven 依赖

<!-- spring cache support --><dependency>  <groupId>org.springframework</groupId>  <artifactId>spring-context-support</artifactId>  <version>${springframework.version}</version></dependency><!-- ehcache 3 & JSR107 --><dependency>  <groupId>org.ehcache</groupId>  <artifactId>ehcache</artifactId>  <version>3.1.3</version></dependency><dependency>  <groupId>javax.cache</groupId>  <artifactId>cache-api</artifactId>  <version>1.0.0</version></dependency>

注意:必须添加 ehcache3 让 JSR107 模块找到实现。

4.缓存应用

@Service@Transactionalpublic class CacheService {@Cacheable("foo")public Foo findBook(String name) {System.out.print("cache....or not?\n");return new Foo("jdbc test1");}}
假设你需要缓存结果,仅需要在@Service对象的方法上注释 @Cachable("foo") 即可实现该业务对象的缓存。

除 @Cacheable 外,常用的还有 @CachePut、@CacheEvict 注释,具体使用见:注释驱动的 Spring cache 缓存介绍 这些内容在新版本中没有变化。

注意:

(1)注意缓存与交易的关系。建议缓存在服务以上层使用,在 数据访问层(DAO)中谨慎使用!!!

(2)如果你喜欢JSR的注释,请参考Spring官方文档(英文)。


第二部分:使用 Redis 做共享缓存

Redis 的中文介绍:http://www.redis.cn/

1. 配置

@Configuration@EnableCachingpublic class RedisConfig extends CachingConfigurerSupport {@Beanpublic JedisConnectionFactory redisConnectionFactory() {JedisConnectionFactory redisConnectionFactory = new JedisConnectionFactory();// DefaultsredisConnectionFactory.setHostName("127.0.0.1");redisConnectionFactory.setPort(6379);return redisConnectionFactory;}@Beanpublic RedisTemplate<String, String> redisTemplate(RedisConnectionFactory rf) {RedisTemplate<String, String> redisTemplate = new RedisTemplate<String, String>();redisTemplate.setConnectionFactory(rf);return redisTemplate;}@Beanpublic RedisCacheManager redisCacheManager(RedisTemplate redisTemplate) {RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate);// Number of seconds before expiration. Defaults to unlimited (0)cacheManager.setDefaultExpiration(10); // Sets the default expire time (in seconds)return cacheManager;}}

注意:设置 Cache 过期时间要合适,太长就长期有效,太短你看不到测试结果。建议 10-30秒。

2. Maven 依赖

<!-- redis cache related.....start --><dependency><groupId>org.springframework.data</groupId><artifactId>spring-data-redis</artifactId><version>1.6.0.RELEASE</version></dependency><dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>2.7.3</version></dependency><!-- https://mvnrepository.com/artifact/org.apache.commons/commons-pool2 --><dependency>    <groupId>org.apache.commons</groupId>    <artifactId>commons-pool2</artifactId>    <version>2.4.2</version></dependency><!-- redis cache related.....end -->

其中,commons-pool2 是 访问 Redis 服务器的链接池需要的依赖。

具体代码见:Redis 缓存 + Spring 的集成示例 ,作者写的很不错。

第三部分:EhCache 与 Redis 混合部署支持

1. 原理与代码

通过前面的配置,我们发现 Spring 缓存抽象 就是在其他 Cache 管理程序基础上提出了自己的抽象体系,即利用适配器模式建立了通用的 Cache 管理体系。

找到 org.springframework.cache.CacheManager API文档,发现改接口相当简单。我们需要做的工作就是将两个 Manager 组合一下。 代码:

MixCacheManager 源代码:

public class MixCacheManager implements CacheManager {private CacheManager redisCacheManager;private CacheManager memCacheManager;private String redisPrefix = "redis-";   public Cache getCache(String arg0) {if (arg0.startsWith(redisPrefix))return redisCacheManager.getCache(arg0);else return memCacheManager.getCache(arg0); }public Collection<String> getCacheNames() {Collection<String> cacheNames = new ArrayList<String>();if (redisCacheManager != null) {cacheNames.addAll(redisCacheManager.getCacheNames());}if (memCacheManager != null) {cacheNames.addAll(memCacheManager.getCacheNames());}return cacheNames;}// getting & setting ...}

该类非常简单。注意:redisCacheManager 和 memCacheManager 都是实现了 org.springframework.cache.CacheManager 接口的类。MixCacheManager也实现了该接口。

getCacheNames 的任务就是拼接所以的 Cache 区域;

getCache 就是根据应用需要的 Cache 名字,返回合适的 Cache。这里指定“redis-”开头的名字,使用共享缓存。

2. 配置

修改 RedisConfig 类,添加

// Problem with Autowiring & No unique bean// http://stackoverflow.com/questions/2699608/problem-with-autowiring-no-unique-bean@Bean@Primarypublic CacheManager cacheManager(RedisCacheManager redisCacheManager,JCacheCacheManager jcacheCacheManager) {MixCacheManager cacheManager = new MixCacheManager();cacheManager.setRedisCacheManager(redisCacheManager);cacheManager.setMemCacheManager(jcacheCacheManager);return cacheManager;}
@Primary 是让 Spring Cache 知道,现在 org.springframework.cache.CacheManager 三个实现中,你该使用混合实现。

3.测试与使用

测试使用的服务代码:

@Service@Transactionalpublic class CacheService {@Cacheable("foo")public Foo findBook(String name) {System.out.print("cache....or not?\n");return new Foo("jdbc test1");}@Cacheable("redis-foo")public Foo findBookRedis(String name) {System.out.print("cache Redis....or not?\n");return new Foo("jdbc test1");}}

测试的使用代码:

public static void main(String[] args) {AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();ctx.register(PersistenceJDBCConfig.class);ctx.register(CacheConfig.class);ctx.register(RedisConfig.class);ctx.refresh();CacheService cs = ctx.getBean(CacheService.class);cs.findBook("naan1");cs.findBook("naan1");cs.findBook("naan1");cs.findBook("new1");cs.findBookRedis("redis");cs.findBookRedis("redis");cs.findBookRedis("redis");cs.findBookRedis("redis-new");


输出结果:

cache....or not?cache....or not?cache Redis....or not?cache Redis....or not?

在 Redis 控制台上输入 keys * 看到建立的 key。

总结:

(1)Redis 与 内存缓存 混合使用非常简单实用。

(2)设计模式就是 Java 的魂。了解设计模式,就能看代码猜出有趣的实现。


2 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 win7系统没有网上邻居怎么办 win10网络重置了怎么办 win7桌面没有网上邻居怎么办 win7电脑没有网上邻居怎么办 无线网连接受限怎么办 win7账户被锁定怎么办 贷款sdk授权失败怎么办 京东保价发票怎么办 淘宝购物出现质量问题怎么办 淘宝购物降价了怎么办 淘宝购物物流慢怎么办 在淘宝购物退货怎么办 淘宝购物未付款怎么办 淘宝购物余额不足怎么办 淘宝购物漏发货怎么办 京东618无货怎么办 iis默认文档无效怎么办? 购物卡没有磁性怎么办 墙面贴纸没有贴怎么办 车显示电池符号怎么办 遇上北京购物团怎么办 钱柜老是钱不见怎么办 写真顾客退单怎么办 电视不支持投屏怎么办 qq转错账的钱怎么办 qq关注不了别人怎么办 换货忘记要单号怎么办 快递提前签收了怎么办 签收了不明快递怎么办 京东签收了怎么办 快递被签收了怎么办 香港旅游团强制购物怎么办 爱奇艺京东会员领不了怎么办 买手机买到翻新机怎么办 买了翻新机怎么办 苹果四激活出错怎么办 电脑登陆密码忘记了怎么办 手机号不用微信怎么办 笔记本无限重启怎么办 键盘c键失灵怎么办 买到假避孕套用了怎么办