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 ...
随机推荐
- WTH统计
SELECT t2.MasterName AS '类型',SUM(t1.DailyCount) AS '数量',(CASE T2.MasterName WHEN '电子阅读' THEN '篇' WHE ...
- js中Function的apply方法与call方法理解
最近在使用jQuery的$.each方法时很,突然想到$.each($('div'),function(index,entity){});中的这个index和entity是哪冒出来的,而且可有可无的, ...
- MDN搜索结果自动跳转中文地址
MDN社区(即Mozilla开发者社区)具有很多高质量中英文文档.它是我开发时遇到概念模糊的地方经常访问的网站.因为默认搜索一些代码,优先显示的都是英文.但是恰恰这些显示的英文文档是有中文的.每次都是 ...
- [Linux] 使用rename批量重命名文件
例如把所有png文件的后缀改为jpg $ rename 's/png/jpg/' *png
- SVN 安装配置详解,包含服务器和客户端,外带一个项目演示,提交,更改,下载历史版本,撤销
本次要介绍的是svn版本管理工具包含2个: 服务器端:visualsvn server 下载地址为:https://www.visualsvn.com/server/download/ 此处演示的 ...
- 如何只更新datetime类型字段中的日期
UPDATE [dbo].[Order] SET CreateDate = STUFF(CONVERT(VARCHAR(50),CreateDate,126) ,1, 10, ' ...
- learn go recursive
package main // 参考文章: // https://github.com/Unknwon/the-way-to-go_ZH_CN/blob/master/eBook/06.6.md im ...
- Vue之拦截与响应拦截
vue中有一个拦截方法,当我们发起请求或者请求回来的时候,我们需要做一定的数据过滤或者拦截 下面是在开发项目时进行的一个axios的请求封装: 拦截器:就是我们在请求之前进行的一个操作比如说,我们可以 ...
- 【sklearn】网格搜索 from sklearn.model_selection import GridSearchCV
GridSearchCV用于系统地遍历模型的多种参数组合,通过交叉验证确定最佳参数. 1.GridSearchCV参数 # 不常用的参数 pre_dispatch 没看懂 refit 默认为Tr ...
- C# Post发送数据返回页面结果
public string GetPage(string posturl, string postData) { Stream outstream = null; Stream instream = ...