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集成缓存的更多相关文章

  1. 从零开始学 Java - Spring 集成 Memcached 缓存配置(二)

    Memcached 客户端选择 上一篇文章 从零开始学 Java - Spring 集成 Memcached 缓存配置(一)中我们讲到这篇要谈客户端的选择,在 Java 中一般常用的有三个: Memc ...

  2. 从零开始学 Java - Spring 集成 Memcached 缓存配置(一)

    硬盘和内存的作用是什么 硬盘的作用毫无疑问我们大家都清楚,不就是用来存储数据文件的么?如照片.视频.各种文档或等等,肯定也有你喜欢的某位岛国老师的动作片,这个时候无论我们电脑是否关机重启它们永远在那里 ...

  3. spring集成ehcache本地缓存

    1.maven依赖 <!-- ehcache 相关依赖 --> <dependency> <groupId>net.sf.ehcache</groupId&g ...

  4. (转)为Spring集成的Hibernate配置二级缓存

    http://blog.csdn.net/yerenyuan_pku/article/details/52896195 前面我们已经集成了Spring4.2.5+Hibernate4.3.11+Str ...

  5. Spring集成GuavaCache实现本地缓存

    Spring集成GuavaCache实现本地缓存: 一.SimpleCacheManager集成GuavaCache 1 package com.bwdz.sp.comm.util.test; 2 3 ...

  6. 从零开始学 Java - Spring 集成 ActiveMQ 配置(二)

    从上一篇开始说起 上一篇从零开始学 Java - Spring 集成 ActiveMQ 配置(一)文章中讲了我关于消息队列的思考过程,现在这一篇会讲到 ActivMQ 与 Spring 框架的整合配置 ...

  7. spring集成常用技术的配置

    使用spring集成其他技术,最基本的配置都是模板化的,比如配置视图模板引擎.数据库连接池.orm框架.缓存服务.邮件服务.rpc调用等,以spring的xml配置为例,我将这些配置过程整理出来,并不 ...

  8. 注释驱动的 Spring cache 缓存介绍

    概述 Spring 3.1 引入了激动人心的基于注释(annotation)的缓存(cache)技术,它本质上不是一个具体的缓存实现方案(例如 EHCache 或者 OSCache),而是一个对缓存使 ...

  9. [转]注释驱动的 Spring cache 缓存介绍

    原文:http://www.ibm.com/developerworks/cn/opensource/os-cn-spring-cache/ 概述 Spring 3.1 引入了激动人心的基于注释(an ...

随机推荐

  1. Highcharts 3D柱形图;Highcharts 堆叠3D柱形图;Highcharts 3D饼图;Highcharts 3D圆环图

    Highcharts 3D柱形图 配置 chart.options3d 配置 以下列出了 3D 图的基本配置,设置 chart 的 type 属性为 column,options3d 选项可设置三维效 ...

  2. VS2015常用快捷键总结(转)

    生成解决方案 F6,生成项目Shift+F6 调试执行F5,终止调试执行Shift+F5 执行调试Ctrl+F5 查找下一个F3,查找上一个Shift+F3 附加到进程Ctrl+Alt+P,逐过程F1 ...

  3. LeetCode OJ:Min Stack(最小栈问题)

    Design a stack that supports push, pop, top, and retrieving the minimum element in constant time. pu ...

  4. 简单的cookie盗取

    此文需要有一定的javascript\html\php方面的只是作为基础 直接上代码: #用于抓取盗窃来的cookie的php文件,此文件需置于攻击者的服务器上,这里包含了两种写法:Method1和M ...

  5. three.js入门系列之材质

    一.基础网孔材料 MeshBasicMaterial 图示(光源是(0,1,0)处的点光源): 二.深度网孔材料 MeshDepthMaterial (由于只是改了材料名,代码将不重复贴出) 在这里, ...

  6. [置顶] AI大行其道,你准备好了吗?—谨送给徘徊于转行AI的程序员

    前言 近年来,随着 Google 的 AlphaGo 打败韩国围棋棋手李世乭之后,机器学习尤其是深度学习的热潮席卷了整个IT界.所有的互联网公司,尤其是 Google 微软,百度,腾讯等巨头,无不在布 ...

  7. Android深入理解JNI(二)类型转换、方法签名和JNIEnv

    相关文章 Android深入理解JNI系列 前言 上一篇文章介绍了JNI的基本原理和注册,这一篇接着带领大家来学习JNI的数据类型转换.方法签名和JNIEnv. 1.数据类型的转换 首先给出上一篇文章 ...

  8. GCD 深入理解(二)

    转自@nixzhu的GitHub主页(译者:Riven.@nixzhu),原文<Grand Central Dispatch In-Depth: Part 2/2> 欢迎来到GCD深入理解 ...

  9. Django(一):从socket到MVC

    一.socket的http套路 web应用本质上是一个socket服务端,用户的浏览器是一个socket客户端.socket处在应用层与传输层之间,是操作系统中I/O系统的延伸部分(接口),负责系统进 ...

  10. Balanced Substring

    You are given a string s consisting only of characters 0 and 1. A substring [l, r] of s is a string ...