Spring集成缓存
Want#
上一篇简单服务端缓存API设计设计并实现了一套缓存API,适应不同的缓存产品,本文重点是基于Spring框架集成应用开发。
缓存集成#
以普通Web应用开发常见的搭配Spring+Spring mvc+Mybatis为例,在与DB集成时通常会出现在Mybatis数据访问层做缓存,在之前的文章Mybatis缓存结构一文中有关于Mybatis缓存的简要概述。
随着业务的发展,缓存的范围远不止针对数据库,缓存的产品也会有多种,这种情形下,我们很希望能够使用相同的代码或者基于相同的结构去设计并实现缓存逻辑。
缓存集成示例#
最常规的情形:
- 定义一个基于方法的注解(Cache)
- 定义基于注解拦截业务逻辑的切面
- 所有被注解标注的业务逻辑处理结果,都可以被缓存
备注:Spring提供了丰富的切入点表达式逻辑,如果你认为注解会影响代码的动态部署,可以考虑全部采用xml文件配置的方式。
定义Cache注解
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Cache {
}
定义切面
package org.wit.ff.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.wit.ff.cache.CacheKey;
import org.wit.ff.cache.IAppCache;
import org.wit.ff.util.JsonUtil;
/**
* Created by F.Fang on 2015/9/15.
* Version :2015/9/15
*/
@Aspect
public class BusinessCacheAspect {
private static final Logger LOGGER = LoggerFactory.getLogger(BusinessCacheAspect.class);
/**
* 实际的数据缓存服务提供者.
*/
private IAppCache appCache;
@Pointcut("@annotation(org.wit.ff.cache.Cache)")
public void methodCachePointcut() {
}
@Around("methodCachePointcut()")
public Object record(ProceedingJoinPoint pjp) throws Throwable {
CacheKey cacheKey = buildCacheKey(pjp);
// 只要两个CacheKey对象的json值相等,就认为一致.
// 数组是对象,内容相同的数组执行equals比较时并不想等.
// 如果先转换成json, 将json字符串转换成bytes数组,作为值比较更合理.
//appCache.get();
MethodSignature ms = (MethodSignature) pjp.getSignature();
// 获取方法返回类型
Class<?> returnType = ms.getMethod().getReturnType();
// 返回类型为空,不会应用缓存策略
if (Void.TYPE.equals(returnType)) {
// 实际上, 在你并不想改变业务模型的条件下, pjp.proceed()和pjp.proceed(params) 无差别.
return pjp.proceed();
}
// Json化可以避免掉许多的问题, 不必通过重写CacheKey的equals方法来比较, 因为实现会比较的复杂, 并且不见得能做好.
String key = JsonUtil.objectToJson(cacheKey);
// 查询缓存,即使缓存失败,也不能影响正常业务逻辑执行.
Object result = null;
try {
result = appCache.get(key, returnType);
} catch (Exception e) {
LOGGER.error("get cache catch exception!", e);
}
// 若缓存为空, 则处理实际业务.
if (result == null) {
// 正常业务处理不要做任何拦截.
result = pjp.proceed();
// 暂时不记录是否缓存成功,虽然有boolean返回.
try {
appCache.put(key, result);
}catch (Exception e){
LOGGER.error("put cache catch exception!",e);
}
}
return result;
}
private CacheKey buildCacheKey(ProceedingJoinPoint pjp) {
CacheKey key = new CacheKey();
key.setMethod(pjp.getSignature().getName());
if (pjp.getArgs() != null && pjp.getArgs().length > 0) {
key.setParams(pjp.getArgs());
}
return key;
}
public void setAppCache(IAppCache appCache) {
this.appCache = appCache;
}
}
业务服务
package org.wit.ff.business;
import org.springframework.stereotype.Service;
import org.wit.ff.cache.Cache;
import org.wit.ff.model.User;
import java.util.ArrayList;
import java.util.List;
/**
* Created by F.Fang on 2015/10/22.
* Version :2015/10/22
*/
@Service
public class UserBusiness {
@Cache
public List<User> getUser(int appId, List<Integer> userIds){
System.out.println("do business, getUser, appId="+appId);
User user1 = new User(1, "f.fang@adchina.com", "fangfan");
User user2 = new User(2,"mm@adchina.com","mm");
List<User> list = new ArrayList<>();
list.add(user1);
list.add(user2);
return list;
}
@Cache
public User findUnique(int appId, int id){
System.out.println("do business, findUnique, appId="+appId);
User user = new User(100, "am@gmail.com", "am");
return user;
}
@Cache
public void saveUser(int appId, User user){
System.out.println("do business, saveUser");
}
}
测试示例#
spring.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<import resource="classpath:spring-memcached.xml" />
<!-- Aspect扫描 Aspect配置的顺序决定了谁先执行.-->
<bean id="cacheAspect" class="org.wit.ff.aspect.BusinessCacheAspect" >
<property name="appCache" ref="xmemAppCache"/>
</bean>
<aop:aspectj-autoproxy proxy-target-class="true"/>
<!-- 启动service扫描 -->
<context:component-scan base-package="org.wit.ff.business"/>
</beans>
备注:spring-memcached.xml请参考上一篇简单服务端缓存API设计
package org.wit.ff.cache;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests;
import org.wit.ff.business.UserBusiness;
import org.wit.ff.model.User;
/**
* Created by F.Fang on 2015/10/26.
* Version :2015/10/26
*/
@ContextConfiguration("classpath:spring.xml")
public class CacheIntegrationTest extends AbstractJUnit4SpringContextTests {
@Autowired
private UserBusiness userBusiness;
@Test
public void demo(){
User user1 = userBusiness.findUnique(3,1000);
System.out.println(user1);
userBusiness.saveUser(1, new User());
User user2 = userBusiness.findUnique(1,1000);
System.out.println(user2);
userBusiness.saveUser(1, new User());
}
}
分析#
依据目前的简单业务情形分析,一套简单的缓存支持方案(包括对缓存产品的封装和无侵入式的应用接入)。
目前为止,个人认为如redis支持的集合操作,并不能作为通用的缓存处理场景,可考虑作为其它的抽象方案的具体实现。
QA#
有任何问题,请留言联系。
Spring集成缓存的更多相关文章
- 从零开始学 Java - Spring 集成 Memcached 缓存配置(二)
Memcached 客户端选择 上一篇文章 从零开始学 Java - Spring 集成 Memcached 缓存配置(一)中我们讲到这篇要谈客户端的选择,在 Java 中一般常用的有三个: Memc ...
- 从零开始学 Java - Spring 集成 Memcached 缓存配置(一)
硬盘和内存的作用是什么 硬盘的作用毫无疑问我们大家都清楚,不就是用来存储数据文件的么?如照片.视频.各种文档或等等,肯定也有你喜欢的某位岛国老师的动作片,这个时候无论我们电脑是否关机重启它们永远在那里 ...
- spring集成ehcache本地缓存
1.maven依赖 <!-- ehcache 相关依赖 --> <dependency> <groupId>net.sf.ehcache</groupId&g ...
- (转)为Spring集成的Hibernate配置二级缓存
http://blog.csdn.net/yerenyuan_pku/article/details/52896195 前面我们已经集成了Spring4.2.5+Hibernate4.3.11+Str ...
- Spring集成GuavaCache实现本地缓存
Spring集成GuavaCache实现本地缓存: 一.SimpleCacheManager集成GuavaCache 1 package com.bwdz.sp.comm.util.test; 2 3 ...
- 从零开始学 Java - Spring 集成 ActiveMQ 配置(二)
从上一篇开始说起 上一篇从零开始学 Java - Spring 集成 ActiveMQ 配置(一)文章中讲了我关于消息队列的思考过程,现在这一篇会讲到 ActivMQ 与 Spring 框架的整合配置 ...
- spring集成常用技术的配置
使用spring集成其他技术,最基本的配置都是模板化的,比如配置视图模板引擎.数据库连接池.orm框架.缓存服务.邮件服务.rpc调用等,以spring的xml配置为例,我将这些配置过程整理出来,并不 ...
- 注释驱动的 Spring cache 缓存介绍
概述 Spring 3.1 引入了激动人心的基于注释(annotation)的缓存(cache)技术,它本质上不是一个具体的缓存实现方案(例如 EHCache 或者 OSCache),而是一个对缓存使 ...
- [转]注释驱动的 Spring cache 缓存介绍
原文:http://www.ibm.com/developerworks/cn/opensource/os-cn-spring-cache/ 概述 Spring 3.1 引入了激动人心的基于注释(an ...
随机推荐
- day33 Python与金融量化分析(三)
第三部分 实现简单的量化框架 框架内容: 开始时间.结束时间.现金.持仓数据 获取历史数据 交易函数 计算并绘制收益曲线 回测主体框架 计算各项指标 用户待写代码:初始化.每日处理函数 第四部分 在线 ...
- css实现水平 垂直居中
css实现水平居中 <!DOCTYPE html> <html lang="en"> <head> <meta charset=" ...
- lombok --- 常用注解解析
@Data@Getter @Setter @ToString@Cleanup@NonNull@Builder@EqualsAndHashCode
- 【WebGL】3. 相机
相机的种类:WebGL中的相机有两种:正投影相机和透视相机 1. 正投影相机OrthographicCamera:类似于工程图纸中的视角,忽略远近距离,远近的物体比例不变,多用于科学研究,工程图纸的应 ...
- 第2课:jmeter总结、Charles抓包
1. tps(throughput):每秒钟处理的事务数(请求数),定义与qps类似(qps:每秒完成的请求个数.) 响应时间(average):每个请求的平均响应时间 2. jmeter实现下载 ...
- web服务器无法显示font-awesome字体图标
今天遇到了在本地运行网页 一切调用的额font的小图标都OK的,但是把网页发布到tomcat服务器上面就不行了 之后百度了下,找到了解决方法,遂记录下,方法如下: 在web.xml 文件中加上: &l ...
- JQ上传预览+存数据库
因为之前老师讲的方法有不少BUG 现在经过完善已经修复 之前老是讲的方法是每一张都会被传到后台文件夹里面去 导致在预览过程中如果刷新页面 那么预览的图片不能从后台文件夹中删除 这个方法实现在本地预览 ...
- Cannot forward after response has been committed 错误
出现该错误的原因是:页面的跳转控制不好,换句话就是说程序的逻辑控制不好,导致了程序顺序执行的时候多次跳转到同一页面,有的程序员建议用多次使用return语句来返回,但是个人认为最好的还是自己要先理清页 ...
- IOS开发 static关键字的作用
(1)函数体内 static 变量的作用范围为该函数体,不同于 auto 变量,该变量的内存只被分配一次, 因此其值在下次调用时仍维持上次的值: (2)在模块内的 static 全局变量可以被模块内所 ...
- VSS虚拟交换系统
下面介绍一下如何在CISCO交换机上配置VSS,具体配置如下: //在CISCO1 上配置vss域,两台设备都要在同一个域中 Cisco-(confgi)#switch virtual domain ...