利用spring的AOP来实现Redis缓存
为什么使用Redis
数据查询时每次都需要从数据库查询数据,数据库压力很大,查询速度慢,因此设置缓存层,查询数据时先从redis中查询,如果查询不到,则到数据库中查询,然后将数据库中查询的数据放到redis中一份,下次查询时就能直接从redis中查到,不需要查询数据库了,减少数据库压力,同时redis读取数据快。
本文主要的参考地址(与参考文章不同的是重点利用xml来配置aop来实现redis缓存):
https://www.cnblogs.com/cmyxn/p/5990974.html https://blog.csdn.net/MassiveStars/article/details/53502288
这里是Redis 的相关配置
XML
.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
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.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--扫描redis配置文件-->
<context:property-placeholder ignore-unresolvable="true" location="classpath:redis.properties"/>
<!--设置链接属性-->
<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxIdle" value="${redis.maxIdle}"/>
<property name="maxTotal" value="${redis.maxTotal}" />
<property name="maxWaitMillis" value="${redis.maxWaitMillis}" />
<property name="testOnBorrow" value="${redis.testOnBorrow}" />
<property name="testOnReturn" value="${redis.testOnReturn}" />
</bean>
<!-- redis服务器中心 -->
<bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" >
<property name="hostName" value="${redis.host}"></property>
<property name="port" value="${redis.port}"></property>
<property name="password" value="${redis.password}"></property>
<property name="poolConfig" ref="poolConfig"></property>
</bean>
<!-- redis连接池 -->
<bean id="jedisPool" class="redis.clients.jedis.JedisPool" destroy-method="close">
<constructor-arg index="0" ref="poolConfig" />
<constructor-arg index="1" value="${redis.host}" />
<constructor-arg index="2" value="${redis.port}" />
<constructor-arg index="3" value="${redis.timeout}" />
<constructor-arg index="4" value="${redis.password}" />
</bean>
<bean id="stringRedisSerializer" class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
<!-- 配置RedisTemplate -->
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate" p:connection-factory-ref="jedisConnectionFactory" >
<!-- 以下针对各种数据进行序列化方式的选择 -->
<property name="keySerializer" ref="stringRedisSerializer" />
<property name="hashKeySerializer" ref="stringRedisSerializer" />
<property name="hashValueSerializer" ref="stringRedisSerializer" />
</bean>
<bean id="redisCache" class="com.web.redis.RedisCache" >
<property name="jedisPool" ref="jedisPool" />
</bean>
</beans>
redis.properties中的参数
redis.host=172.17.4.84
redis.port=6379
redis.password=handit8888
redis.maxIdle=300
redis.maxWaitMillis=1000
redis.maxTotal=600
redis.testOnBorrow=true
redis.testOnReturn=true
redis.projectNam=ssm_redis
redis.expiration=3000
redis.timeout=3000
system.cache.database=3
到此redis基本配置完成
xml中com.web.redis.RedisCache
import org.springframework.beans.factory.annotation.Autowired;
import com.njwangbo.util.SerializeUtil;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
public class RedisCache {
@Autowired
private JedisPool jedisPool = new JedisPool();
public JedisPool getJedisPool() {
return jedisPool;
}
public void setJedisPool(JedisPool jedisPool) {
this.jedisPool = jedisPool;
}
//从redis缓存中查询,反序列化
public Object getDataFromRedis(String redisKey){
//查询
Jedis jedis = jedisPool.getResource();
byte[] result = jedis.get(redisKey.getBytes());
//如果查询没有为空
if(null == result){
return null;
}
//查询到了,反序列化
return SerializeUtil.unSerialize(result);
}
//将数据库中查询到的数据放入redis
public void setDataToRedis(String redisKey, Object obj){
//序列化
byte[] bytes = SerializeUtil.serialize(obj);
//存入redis
Jedis jedis = jedisPool.getResource();
String success = jedis.set(redisKey.getBytes(), bytes);
if("OK".equals(success)){
System.out.println("数据成功保存到redis...");
}
}
}
代码中JedisPool 主要是利用set完成注入
配置AOP
在spring-mvc.xml中配置
<?xml version="1.0" encoding="UTF-8"?>
<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:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 配置注解映射器、适配器 -->
<mvc:annotation-driven/>
<!-- 设置能访问静态资源 -->
<mvc:default-servlet-handler/>
<!-- IOC使用注解方式 -->
<context:component-scan base-package="com.web"/>
<!--启动对@AspectJ注解的支持 , proxy-target-class设置为true表示通知spring使用cglib而不是jdk的来生成代理方法,这样AOP可以拦截到Controller -->
<aop:aspectj-autoproxy proxy-target-class="true" />
<bean id="getCache" class="com.web.redis.GetCacheAOP" ></bean>
<!-- redis配置 start -->
<!-- 声明切面类 -->
<aop:config>
<!-- 声明切面 -->
<aop:aspect ref="getCache">
<!--
1、method 指向的方法,是切面类中的方法,表示当程序触发pointcut指向的注解时,aop会启动method方法 -->
<aop:around method="beforeExec" pointcut="@annotation(com.web.redis.GetCache)"/>
</aop:aspect>
</aop:config>
<!-- redis配置 end -->
</beans>
com.web.redis.GetCacheAOP类
import java.lang.reflect.Method;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
/**
* 读取redis切点类
* @author chenting
*
*/
public class GetCacheAOP {
@Autowired
private RedisCache redisCache = new RedisCache();
public RedisCache getRedisCache() {
return redisCache;
}
public void setRedisCache(RedisCache redisCache) {
this.redisCache = redisCache;
}
/*
* 功能:读取redis
* 从redis中读取数据,如果没有就连接数据库读取数据,再放入redis中
* redisKey----用方法名作为key
*/
public Object beforeExec(ProceedingJoinPoint joinPoint){
//前置:到redis中查询缓存
System.out.println("调用从redis中查询的方法...");
//redis中key格式: id
String redisKey = getCacheKey(joinPoint);
//获取从redis中查询到的对象
Object objectFromRedis = redisCache.getDataFromRedis(redisKey);
//如果在redis中查询到了
if(null != objectFromRedis){
System.out.println("从redis中查询到了数据...不需要查询数据库");
return objectFromRedis;
}
System.out.println("没有从redis中查到数据...");
//没有在redis中查到,那么查询数据库
Object object = null;
try {
object = joinPoint.proceed();
} catch (Throwable e) {
e.printStackTrace();
}
System.out.println("从数据库中查询的数据...");
//后置:将数据库中查询的数据放到redis中
System.out.println("调用把数据库查询的数据存储到redis中的方法...");
redisCache.setDataToRedis(redisKey, object);
System.out.println("redis中的数据..."+object.toString());
//将查询到的数据返回
return object;
}
/**
* redisKey的来源
* 可自行修改
* (注释部分redisKey的来源,根据类名、方法名和参数值获取唯一的缓存键, @return 格式为 "包名.类名.方法名.参数类型.参数值",类似 "your.package.SomeService.getById(int).123")
*/
@SuppressWarnings("unused")
private String getCacheKey(ProceedingJoinPoint joinPoint) {
MethodSignature ms=(MethodSignature) joinPoint.getSignature();
Method method=ms.getMethod();
//String ActionName = method.getAnnotation(GetCache.class).name();
String ActionName=joinPoint.getSignature().getName();
/*String fieldList = method.getAnnotation(GetCache.class).value();
for (String field:fieldList.split(","))
ActionName +="."+field;
//先获取目标方法参数
String id = null;
Object[] args = joinPoint.getArgs();
if (args != null && args.length > 0) {
id = String.valueOf(args[0]);
}
ActionName += "="+id;
String redisKey = ms+"."+ActionName;*/
String redisKey=ActionName;
return redisKey;
}
}
自定义注解
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.lang.annotation.ElementType;
import java.lang.annotation.RetentionPolicy;
/**
* 自定义注解,对于查询使用缓存的方法加入该注解
* @author Chenth
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Documented
public @interface GetCache {
String name() default "";
String value() default "";
}
到这里基本改写的代码都写了,该配置的也都配置了
测试
我是在serviceImpl层添加注解
@GetCache(value="getUser")
public List getUser() throws Exception{
return userMapper.getUser();
}