使用方法拦截机制在不修改原逻辑基础上为 spring MVC 工程添加 Redis 缓存
首先,相关文件:链接: https://pan.baidu.com/s/1H-D2M4RfXWnKzNLmsbqiQQ 密码: 5dzk
文件说明:
redis-2.4.5-win32-win64.zip --windows程序包,无需安装,直接执行redis-server.exe 启动服务,执行redis-cli.exe启动客户端
redis-3.2.9.tar.gz --linux程序包,需要make安装一下,然后执行src/redis-server 注意启动时指定配置文件为redis.conf
RedisUtil.java --redis工具类
spring-redis-caching-example-master --spring-redis示例
一、redis 安装
1、windows 安装与运行
直接使用下载的程序包,解压后获得文件夹(假定为redis_home),进入redis_home/32bit(或者redis_home/64bit)目录,运行redis-server.exe即可运行redis服务。通过查看运行窗口的日志。
运行时指定配置和日志输出文件:
cmd窗口运行redis-server.exe ./redis.conf > ./redis.log
服务方式运行:
使用redis-server.exe 运行需要在命令行窗口中,关闭窗口后程序也会跟着关闭,如果想要后台一直默默的运行,可以从官网下载msi版的安装文件,安装后会生成一个redis的服务,启动服务即可
2、Linux 下安装与运行
假定安装到 ..../program下:
将redis-3.2.9.tar.gz安装包cp到program/redis/目录下 ----> 解压 tar -xzf redis-3.2.9.tar.gz --->进入解压后的目录 cd redis-3.2.9 ----> make(安装命令);
运行(假定redis根目录为redisHome):
cd redisHome/src
nohup redis-server ../redis.conf > ../../../logs/redis-log.txt & ---- nohup 方式,指定配置文件 , 指定日志文件路径
3、运行后验证系统是否正常运行
进入程序目录(和redis-server一个目录),运行redis-client程序验证:
redis-cli -p 127.0.0.1 -a eas@1234 -- -p:指定redis服务host,-a指定密码(如果有)
set 'test' 'HelloWord!' --存入数据
get 'test' --取出数据
flushAll --清空所有数据
4、通过ip地址无法连接问题
redis-cli 客户端验证时,可能出现用ip地址无法连接用localhost可以连接的情况,这是因为没有配置允许通过ip外网访问。
打开redis.conf配置文件,做如下修改:
注释 bind 行配置;
修改密码 (去掉注释 requirepass foobared并修改为requirepass eas@1234);
修改protected-mode no;
5、redis desktop Manager软件
可使用Redis DeskTop Manager可视化软件工具查看redis数据
二、redis缓存在项目中的使用
1、使用拦截器方式让redis介入原有的程序逻辑
开发环境:Maven、spring MVC 架构的web工程
需求: 当前数据库因为多种因素查询效率比较低下,需要使用redis缓存提交效率,但是之前的代码开发已经比较多了不方便大规模修改代码,希望尽可能不修改之前的代码。
1.1 maven pom配置:
<!--引用spring-redis相关jar包-->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>1.6.1.RELEASE</version>
</dependency>
<!--引用jedis相关jar包-->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.7.3</version>
</dependency>
<!--spring aop相关包-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.0</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.0</version>
</dependency>
1.2 spring bean配置
<beans
......
xmlns:aop="http://www.springframework.org/schema/aop"
......
xsi:schemaLocation="
......
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
......
">
<!-- 配置connectionFactory,提供redis服务器相关连接参数 -->
<bean id="connectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" >
<property name="poolConfig" ref="poolConfig" />
<property name="port" value="6379" />
<property name="hostName" value="10.80.13.10" />
<property name="password" value="eas@1234" />
<property name="timeout" value="100" />
</bean >
<!-- 配置spring的redisTemplate,注入connectionFactory,指定key和value的序列化类 -->
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate" >
<property name="connectionFactory" ref="connectionFactory" />
<property name="keySerializer" >
<bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
</property>
<property name="valueSerializer" >
<bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" />
</property>
</bean >
<!-- 配置redis工具类,注入redisTemplate -->
<bean id="redisUtil" class="com.sunline.common.utils.RedisUtil" > <!--根据实际包名修改-->
<property name="redisTemplate" ref="redisTemplate" />
</bean >
RedisUtil.java代码:
package com.sunline.common.utils; import java.io.Serializable;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations; /**
* redis cache 工具类
*/
public final class RedisUtil {
/*private Logger logger = Logger.getLogger(RedisUtil.class); */
private RedisTemplate<Serializable, Object> redisTemplate; /**
* 批量删除对应的value
* @param keys
*/
public void remove(final String... keys) {
for (String key : keys) {
remove(key);
}
} /**
* 批量删除key
* @param pattern
*/
public void removePattern(final String pattern) {
Set<Serializable> keys = redisTemplate.keys(pattern);
if (keys.size() > 0)
redisTemplate.delete(keys);
} /**
* 删除对应的value
* @param key
*/
public void remove(final String key) {
if (exists(key)) {
redisTemplate.delete(key);
}
} /**
* 判断缓存中是否有对应的value
* @param key
* @return
*/
public boolean exists(final String key) {
return redisTemplate.hasKey(key);
} /**
* 读取缓存
* @param key
* @return
*/
public Object get(final String key) {
Object result = null;
ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
result = operations.get(key);
return result;
} /**
* 写入缓存
* @param key
* @param value
* @return
*/
public boolean set(final String key, Object value) {
boolean result = false;
try {
ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
operations.set(key, value);
result = true;
} catch (Exception e) {
e.printStackTrace();
}
return result;
} /**
* 写入缓存
* @param key
* @param value
* @return
*/
public boolean set(final String key, Object value, Long expireTime) {
boolean result = false;
try {
ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
operations.set(key, value);
redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);
result = true;
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
public RedisTemplate<Serializable, Object> getRedisTemplate() {
return this.redisTemplate;
}
public void setRedisTemplate(RedisTemplate<Serializable, Object> redisTemplate) {
this.redisTemplate = redisTemplate;
}
}
以上2步为项目添加了redis相关功能,java代码中可以使用redisUtil的相关方法操作缓存了。
3、实现不修改原有代码的前提下给现有的项目添加缓存逻辑
其实单纯的spring MVC工程要实现缓存可以使用Spring 的缓存注解相关逻辑既可,简单方便。
但是现在要求不修改原有代码的情况下给现有项目添加缓存机制。考虑使用spring的方法拦截机制,对相关已有的访问数据库的方法进行拦截,拦截后判断,对符合要求的方法进行缓存,调用方法时先判断缓存是否存在,如存在直接取缓存,如不存在调用原方法逻辑并把结果存入缓存。
spring 添加配置:
<!--必须,否则方法拦截无效-->
<aop:aspectj-autoproxy proxy-target-class="true"/>
<!--方法拦截类配置-->
<bean id="methodCacheInterceptor" class="com.sunline.common.utils.MethodCacheInterceptor" >
<property name="redisUtil" ref="redisUtil" />
</bean >
<!-- 需要加入缓存的类或方法 proxy-target-class="true"-->
<bean id="methodCachePointCut" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice" ref="methodCacheInterceptor" />
<property name="patterns" >
<list>
<value>org\.springframework\.jdbc\.core\.JdbcTemplate\.*queryFor.*</value >
</list>
</property>
</bean >
以上配置了一个方法拦截类和一个方法拦截策略bean,只有符合list中的规则的方法才会被拦截然后送到methodCacheInterceptor类中进行拦截后的缓存处理。
MethodCacheInterceptor 代码:
package com.sunline.common.utils; import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties; import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.log4j.Logger; import javassist.expr.Instanceof; public class MethodCacheInterceptor implements MethodInterceptor {
private Logger logger = Logger.getLogger(MethodCacheInterceptor.class);
private RedisUtil redisUtil;
private Long defaultCacheExpireTime = 1000*60*60*12L; // 缓存默认的过期时间 一天 @Override
public Object invoke(MethodInvocation invocation) throws Throwable {
Object value = null;
long s = System.currentTimeMillis();//start time
String targetName = invocation.getThis().getClass().getName();
String methodName = invocation.getMethod().getName(); Object[] arguments = invocation.getArguments();
String key = getCacheKey(targetName, methodName, arguments);
System.out.println(targetName + "." + methodName + "-----SQL-----"+arguments[0].toString());
try {
// 判断是否有缓存
if (redisUtil.exists(key)) {
value = redisUtil.get(key);
System.out.println("----获取缓存---key="+key + "----耗时:"+(System.currentTimeMillis()-s)+"ms");
return value;
}
// 写入缓存
value = invocation.proceed();
if (value != null) {
final String tkey = key;
final Object tvalue = value;
new Thread(new Runnable() {
@Override
public void run() {
redisUtil.set(tkey, tvalue, defaultCacheExpireTime);
}
}).start();
}
} catch (Exception e) {
e.printStackTrace();
if (value == null) {
return invocation.proceed();
}
}
System.out.println("----数据库查询----耗时:"+(System.currentTimeMillis()-s)+"ms"); return value;
} /**
* 是否加入缓存
*
* @return
*/
private boolean isAddCache(String targetName, String methodName) {
boolean flag = true;
if (targetNamesList.contains(targetName)
|| methodNamesList.contains(methodName)) {
flag = false;
}
return flag;
} /**
* 创建缓存key
*
* @param targetName
* @param methodName
* @param arguments
*/
private String getCacheKey(String targetName, String methodName,
Object[] arguments) {
StringBuffer sbu = new StringBuffer();
if(targetName.contains("JdbcTemplate") && methodName.contains("query") && arguments.length == 1 ){
if(arguments[0] instanceof String){
return MD5Utils.encryptMD5((String)arguments[0]);
}
}
sbu.append(targetName).append("_").append(methodName);
if ((arguments != null) && (arguments.length != 0)) {
for (int i = 0; i < arguments.length; i++) {
sbu.append("_").append(arguments[i]);
}
}
return sbu.toString();
} public void setRedisUtil(RedisUtil redisUtil) {
this.redisUtil = redisUtil;
}
}
方法需要实现接口MethodInterceptor,在方法invoke中编写拦截方法后判断是否有缓存有则取缓存没有就调用原逻辑并加入缓存的逻辑。
三、其他相关问题及报错问题及解决办法
在实际工程中多多少少会有一些在按照本办法修改后仍然报错或拦截不起作用的情况,此处仅列出小弟在使用中遇到的情况,如有其他情况欢迎留言共同交流。
1、<aop:aspectj-autoproxy proxy-target-class="true"/>配置编译错误,提示元素“aop:aspectj-autoproxy”的前缀“aop”未绑定
解决办法:检查spring配置文件顶部的beans属性,添加如下配置:
ps:同类的其他如"前缀xxx未绑定"同理应该都可以用改办法解决
<beans
......
xmlns:aop="http://www.springframework.org/schema/aop"
......
xsi:schemaLocation="
......
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
......
">
2、java.lang.ClassNotFoundException: org.aspectj.lang.annotation.Around 启动报错
解决办法:缺spring相关jar包,pom文件添加如下配置,也可手动引入相关包
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.0</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.0</version>
</dependency>
使用方法拦截机制在不修改原逻辑基础上为 spring MVC 工程添加 Redis 缓存的更多相关文章
- $.ajax()方法详解 ajax之async属性 【原创】详细案例解剖——浅谈Redis缓存的常用5种方式(String,Hash,List,set,SetSorted )
$.ajax()方法详解 jquery中的ajax方法参数总是记不住,这里记录一下. 1.url: 要求为String类型的参数,(默认为当前页地址)发送请求的地址. 2.type: 要求为Str ...
- Spring MVC工程 无法拦截到url请求
一直没有办法拦截到url的请求,tomcat启动也没有看到Springmvc容器启动的任何说明.所以就建立了一个普通的servlet工程,可以访问url.再重新发布springmvc项目,访问url, ...
- Spring MVC温故而知新 – 参数绑定、转发与重定向、异常处理、拦截器
请求参数绑定 当用户发送请求时,根据Spring MVC的请求处理流程,前端控制器会请求处理器映射器返回一个处理器,然后请求处理器适配器之心相应的处理器,此时处理器映射器会调用Spring Mvc 提 ...
- spring mvc拦截器
Java里的拦截器是动态拦截Action调用的对象.它提供了一种机制可以使开发者可以定义在一个action执行的前后执行的代码,也可以在一个action执行前阻止其执行,同时也提供了一种可以提取act ...
- Spring MVC拦截器浅析
Spring MVC拦截器 重点:Spring MVC的拦截器只会拦截控制器的请求,如果是jsp.js.image.html则会放行. 什么是拦截器 运行在服务器的程序,先于Servlet或JSP之前 ...
- 玩转spring MVC(七)----拦截器
继续在前边的基础上来学习spring MVC中拦截器的使用,下面通过一个例子来实现(完整项目在这里下载:http://download.csdn.net/detail/u012116457/84334 ...
- Spring MVC-学习笔记(5)spring MVC的文件上传、下载、拦截器
1.文件上传. spring MVC为文件上传提供了直接的支持,这种支持是即插即用的MultipartResolver(多部分解析器)实现的.spring MVC使用Apache Commo ...
- Java CAS同步机制 原理详解(为什么并发环境下的COUNT自增操作不安全): Atomic原子类底层用的不是传统意义的锁机制,而是无锁化的CAS机制,通过CAS机制保证多线程修改一个数值的安全性。
精彩理解: https://www.jianshu.com/p/21be831e851e ; https://blog.csdn.net/heyutao007/article/details/19 ...
- Android开发,缺少权限导致无法修改原文件,获取所有文件访问权限的方法
在Android 11开发中,app会遇到使用绝对路径无法打开某文件的情况(文件存在根目录下,获取到的路径为:/storage/emulated/0/XXX.txt),而使用相对路径打开文件后(获取到 ...
随机推荐
- 基于JDK1.8的String源码学习笔记
String,可能是学习Java一上来就学习的,经常用,但是却往往只是一知半解,甚至API有时也得现查.所以还是老规矩,倒腾源码. 一.java doc 这次首先关注String的doc,因为其实作为 ...
- CH1809匹配统计【KMP】
1809 匹配统计 0x18「基本数据结构」练习 描述 阿轩在纸上写了两个字符串,分别记为A和B.利用在数据结构与算法课上学到的知识,他很容易地求出了“字符串A从任意位置开始的后缀子串”与“字符串B” ...
- Oracle体系结构之Oracle分区
目录 Oracle分区 0 一.Oracle分区理论知识 1 二.分区表的实现方式 1 1.范围分区(range partition table) 1 2.列表分区(list partitioning ...
- 洛谷P4799 世界冰球锦标赛 CEOI2015 Day2 meet-in-the-middle
正解:折半搜索 解题报告: 先放个传送门QAQ 想先说下部分分?因为包含了搜索背包两个方面就觉得顺便复习下?QwQ 第一档部分分 爆搜 就最最普通的爆搜鸭,dfs(第几场,钱),然后每次可以看可以不看 ...
- python os模块 os.chmod
os.chmod() 方法用于更改文件或目录的权限. os.chmod(path, mode) 参数 path -- 文件名路径或目录路径. flags -- 可用以下选项按位或操作生成, 目录的读权 ...
- vue基础篇(一)
1.简介 Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架.与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用.Vue 的核心库只关注视图层,不仅易于上手 ...
- cocos代码研究(20)Widget子类LoadingBar学习笔记
理论基础 在一些操作中可视化指示进度条.显示给用户一个条表示操作已经完成了多少,继承自 Widget. 代码实践 static LoadingBar * create ()创建一个空的LoadingB ...
- Spring整合Mybatis 之分页插件使用
[分页插件项目中的正式代码一共有个5个Java文件,这5个文件的说明如下] Page<E>[必须]:分页参数类,该类继承ArrayList,虽然分页查询返回的结果实际类型是Page< ...
- EF Code First 学习笔记:表映射(转)
多个实体映射到一张表 Code First允许将多个实体映射到同一张表上,实体必须遵循如下规则: 实体必须是一对一关系 实体必须共享一个公共键 观察下面两个实体: public class Per ...
- 逆向及BOF基础实践
逆向及BOF基础实践 20145316 许心远 一.缓冲区溢出基础知识 缓冲区溢出是一种非常普遍.非常危险的漏洞,在各种操作系统.应用软件中广泛存在.利用缓冲区溢出攻击,可以导致程序运行失败.系统宕机 ...