首先我们结合spring 来看一下redis缓存注解的使用
首先我们定义一个配置类redisconfig
package com.cai;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.data.redis.cache.RedisCache;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
* cache connfig class
* @author caizl
*
*/
@EnableCaching
@Configuration
@Profile("sig1")
public class RedisConfig {
@Value("${redis.port}")
private int port;
@Value("${redis.hostname}")
private String hostName;
private StringRedisTemplate stringRedisTemplate;
/**
* 初始化redis factory
* @return
*/
public LettuceConnectionFactory redisConnectionFactory() {
System.err.println("进入reids factory constraction");
return new LettuceConnectionFactory(new RedisStandaloneConfiguration(hostName, port));
}
/**
* 创建redis template
* @param redisConnectionFactory
* @return
*/
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer());
return redisTemplate;
}
/**
* create redis cache
* @param redisConnectionFactory
* @return
*/
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory);
RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();
RedisCacheManager cacheManager = new RedisCacheManager(redisCacheWriter, redisCacheConfiguration);
return cacheManager;
}
}
//结合redis 缓存代码
package com.cai;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;
import com.cai.pojo.User;
@Service
@Profile("single")
public class SpringCacheAnnotationService {
/**
* springcache注解版本(官方大部分资料开始往springboot方向引导,实际上不用springboot,也是差不多的方式)
*
*/
// value~单独的缓存前缀
// key缓存key 可以用springEL表达式 redis 存储格式cache-1:123
@Cacheable(cacheManager = "cacheManager", value = "cache-1", key = "#userId")
public User findUserById(String userId) throws Exception {
// 读取数据库
User user = new User(userId, "张三");
System.out.println("从数据库中读取到数据:" + user);
return user;
}
@CacheEvict(cacheManager = "cacheManager", value = "cache-1", key = "#userId")
public void deleteUserById(String userId) throws Exception {
// 先数据库删除,成功后,删除Cache
// 先判断Cache里面是不是有?有则删除
System.out.println("用户从数据库删除成功,请检查缓存是否清除~~" + userId);
}
// 如果数据库更新成功,更新redis缓存
@CachePut(cacheManager = "cacheManager", value = "cache-1", key = "#user.userId", condition = "#result ne null")
public User updateUser(User user) throws Exception {
// 先更新数据库,更成功
// 更新缓存
// 读取数据库
System.out.println("数据库进行了更新,检查缓存是否一致");
return user; // 返回最新内容,代表更新成功
}
public boolean incr(String userId) {
return true;
}
}
//上述一些不重要的类省略。
根据上述代码我们可以进行简单测试,此处略
那么当遇到@Cacheable等注解时,怎么神奇的使用到了redis缓存呢?首先注意我们的配置类,进行redis cache 初始化,那么进行数据操作时就与我们的redis 进行关联。下面自己定义一个缓存注解进行模拟:
package com.cai.custom.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 自定义cache注解
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CoustomCache {
/**
* key的规则,可以使用springEL表达式,可以使用方法执行的一些参数
*/
String key();
}
创建我们的切面自动配置类,并开启aop
package com.cai.custom;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.context.annotation.Profile;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
@Profile("custom")
// 开启AOP
@EnableAspectJAutoProxy
class CustomCacheRedisAppConfig {
@Value("${redis_host}")
private String redisHost;
@Value("${redis_port}")
private int redisPort;
@Bean
public LettuceConnectionFactory redisConnectionFactory() {
System.out.println("使用自定义注解版本");
return new LettuceConnectionFactory(new RedisStandaloneConfiguration(redisHost, redisPort));
}
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate redisTemplate = new RedisTemplate();
redisTemplate.setConnectionFactory(redisConnectionFactory);
// 可以配置对象的转换规则,比如使用json格式对object进行存储。
// Object --> 序列化 --> 二进制流 --> redis-server存储
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer());
return redisTemplate;
}
}
//编写我们的redis 缓存通知类
package com.cai.custom.aop;
import java.lang.reflect.Method;
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.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import com.cai.custom.annotations.CoustomCache;
@Component
@Aspect
public class CoustomCacheAspect {
@Autowired
private RedisTemplate redisTemplate;
@Pointcut("@annotation(com.cai.custom.annotations.CoustomCache)")
public void cachePointcut() {
}
// 定义相应的事件
@Around("cachePointcut()")
public Object doCache(ProceedingJoinPoint joinPoint) {
Object value = null;
try {
// 0-1、 当前方法上注解的内容
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = joinPoint.getTarget().getClass().getMethod(signature.getName(), signature.getMethod().getParameterTypes());
CoustomCache cacheAnnotation = method.getAnnotation(CoustomCache.class);
String keyEl = cacheAnnotation.key();
// 0-2、 前提条件:拿到作为key的依据 - 解析springEL表达式
// 创建解析器
ExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression(keyEl);
EvaluationContext context = new StandardEvaluationContext(); // 参数
// 添加参数
Object[] args = joinPoint.getArgs();
DefaultParameterNameDiscoverer discover = new DefaultParameterNameDiscoverer();
String[] parameterNames = discover.getParameterNames(method);
for (int i = 0; i < parameterNames.length; i++) {
context.setVariable(parameterNames[i], args[i].toString());
}
// 解析
String key = expression.getValue(context).toString();
// 1、 判定缓存中是否存在
value = redisTemplate.opsForValue().get(key);
if (value != null) {
System.out.println("从缓存中读取到值:" + value);
return value;
}
// 2、不存在则执行方法
value = joinPoint.proceed();
// 3、 同步存储value到缓存。
redisTemplate.opsForValue().set(key, value);
} catch (Throwable throwable) {
throwable.printStackTrace();
}
return value;
}
}
那么简单的模拟实现就ok了,说白了在这里使用了el表达式及aop相关技术,不懂得话可以查阅相关资料,最后进行测试
package edu.dongnao.study.redis.apply.lesson1;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import edu.dongnao.study.redis.apply.lesson1.SpringCacheAnnotationService;
import edu.dongnao.study.redis.apply.pojo.User;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class SpringCacheAnnotationTests {
@Autowired
SpringCacheAnnotationService springCacheAnnotationService;
// ---------------spring cache注解演示
// get
@Test
public void springCacheTest() throws Exception {
User user = springCacheAnnotationService.findUserById("cai");
System.out.println(user);
}
// update
//@Test
public void springCacheTest2() throws Exception {
springCacheAnnotationService.updateUser(new User("hhhhhhh-2", "hash"));
User user = springCacheAnnotationService.findUserById("cai");
System.out.println(user);
}
// delete
//@Test
public void springCacheTest3() throws Exception {
springCacheAnnotationService.deleteUserById("cai");
}
}