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 ...
随机推荐
- 进程上下文频繁切换导致load average过高
一.问题现象 现网有两台虚拟机主机95%的cpu处于idle状态,内存使用率也不是特别高,而主机的load average达到了40多. 二.问题分析 先在主机上通过top.free.ps.iosta ...
- Ansible 开发调试 之【模块调试】
本地调试 需要安装jinja2 库 yum -y install python-jinja2 使用官方提供的测试脚本调试 git clone git://github.com/ansible/ansi ...
- leetcode 849. Maximize Distance to Closest Person
In a row of seats, 1 represents a person sitting in that seat, and 0 represents that the seat is emp ...
- Java复习6异常处理
Java复习6.异常处理 20131005 前言: Java中的异常处理机制是非常强大的,相比C++ 来说,更加系统.但是我们开发人员没有很好的使用这一点.一些小的程序是没有什么问题的,但是对于大型项 ...
- 九、dbms_ddl(提供了在PL/SQL块中执行DDL语句的方法)
1.概述 作用:提供了在PL/SQL块中执行DDL语句的方法,并且也提供了一些DDL的特殊管理方法. 2.包的组成 1).alter_compile说明:用于重新编译过程.函数和包语法:dbms_dd ...
- Prism 4 文档 ---第4章 模块化应用程序开发
模块化应用程序是指将一个应用程序拆分成一系列的可以组合的功能单元.一个客户端模块封装了应用程序的一部分,并且通常是一系列相关的关注点.它可以包含一个相关的组件的集合,就像用户界面,应用程序功能,和一些 ...
- Mysqlde的权限操作,以及增加用户
增加用户及直接授权 ' 在这条命令里边all代表所有的权限,*.*代表所有的空间名.表名 sql的通配符 _ 代表任意的一个字符 % 代表任意的字符的任意长度 修改用户的密码 update 空间名.表 ...
- 在QT中使用静态对象
最近做教研室的项目,需要只能存在一个接收数据的线程,那么我就想把这个线程设置成一个静态对象.但是在connect信号与槽的时候出了一点问题,最后搞好了,现在这mark一下: 比如说一个声明了一个静态的 ...
- The capacitive screen technology - tadpole
- java集合运算:求交集,并集,集合差
今天突然想用Java实现如何用集合实现交集,并集和差集的运算了!主要是看Python语言的时候想起来的. 实现主要使用的Set集合,Set集合的特点是集合内的元素不可重复. 具体代码如何: packa ...