自定义Redis分布式缓存starter 并使用@Enablexx 控制缓存功能是否启用
1.创建maven项目,添加依赖
项目结构
pom.xml
4.0.0
com.sgg
aopCache-spring-boot-start
0.0.1-SNAPSHOT
aopCache-spring-boot-start
aopCache-spring-boot-start
1.8
UTF-8
UTF-8
2.3.7.RELEASE
org.springframework.boot
spring-boot-starter-data-redis
org.apache.commons
commons-pool2
2.6.0
org.redisson
redisson
3.15.3
org.springframework.boot
spring-boot-starter-aop
org.springframework.boot
spring-boot-starter
org.springframework.boot
spring-boot-configuration-processor
true
org.projectlombok
lombok
true
org.springframework.boot
spring-boot-starter-test
test
org.junit.vintage
junit-vintage-engine
org.springframework.boot
spring-boot-dependencies
${spring-boot.version}
pom
import
2.自定义注解
1.@EnableGmalllCache 启动功能注解
package com.sgg.aopcache.cache.anno;
import com.sgg.aopcache.cache.selector.AopCacheSelector;
import org.springframework.context.annotation.Import;
import java.lang.annotation.*;
@Target(ElementType.TYPE)
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Import(AopCacheSelector.class)
public @interface EnableGmalllCache {
}
2.@GmalllCache 目标方法缓存注解
@Target({ ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface GmalllCache {
String cacheKey() default ""; //支持写表达式
String bloomName() default "";//默认是不用布隆
String bloomValue() default "";//布隆需要判断哪个值的表达式
String lockKey() default ""; // 分布式锁值
}
3.配置类
1.RedissonProperties 配置信息绑定类
/**
* redisson配置信息
*/
@Data
@ConfigurationProperties("spring.redis")
public class RedissonProperties {
private String host = "localhost";
private String addresses = "";
private String password = "";
private String port = "6379";
private int timeout = 3000;
private int connectionPoolSize = 64;
private int connectionMinimumIdleSize=10;
private int pingConnectionInterval = 60000;
public static String ADDRESS_PREFIX = "redis://";
}
2. AopCacheConf 组件配置类
public class AopCacheConf {
//需要 redisTemplate redissonClient
@Bean
public CacheService cacheService(StringRedisTemplate stringRedisTemplate,RedissonClient redissonClient)
{
if (StringUtils.isEmpty(redissonClient)){
throw new RuntimeException("请往容器中添加RedissonClient组件");
}
System.out.println("cacheService方法执行了,往容器中添加CacheService组件");
return new CacheServiceImpl(stringRedisTemplate,redissonClient);
}
@Bean
public GmallCacheAspect gmallCacheAspect(CacheService cacheService)
{
System.out.println("GmallCacheAspect方法执行了,往容器中添加GmallCacheAspect组件");
return new GmallCacheAspect(cacheService);
}
}
3.RedissonAotoConfiguration 自动装配类
@EnableConfigurationProperties(RedissonProperties.class)
public class RedissonAotoConfiguration {
/**
* 自动装配
*
*/
@Bean
RedissonClient redissonClient(RedissonProperties redissonConfig) {
Config config = new Config();
if(StringUtils.isEmpty(redissonConfig.getHost())){
throw new RuntimeException("host is empty");
}
SingleServerConfig serverConfig = config.useSingleServer()
.setAddress(RedissonProperties.ADDRESS_PREFIX + redissonConfig.getHost() + ":" +redissonConfig.getPort())
.setTimeout(redissonConfig.getTimeout())
.setPingConnectionInterval(redissonConfig.getPingConnectionInterval())
.setConnectionPoolSize(redissonConfig.getConnectionPoolSize())
.setConnectionMinimumIdleSize(redissonConfig.getConnectionMinimumIdleSize())
;
if(!StringUtils.isEmpty(redissonConfig.getPassword())) {
serverConfig.setPassword(redissonConfig.getPassword());
}
// RedissonClient redisson = Redisson.create(config);
System.out.println("RedissonClient组件创建完成");
return Redisson.create(config);
}
}
4.Selector
/**
* @author sz
* @DATE 2022/3/13 17:54
*/
public class AopCacheSelector implements ImportSelector {
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
//要添加的组件的全限定类名
System.out.println("selectImports方法执行了,往容器中添加AopCacheConf组件");
return new String[]{"com.sgg.aopcache.cache.conf.AopCacheConf"};
}
}
5.切面类
@Slf4j
@Aspect
public class GmallCacheAspect {
//没抢到默认的等待时间
private AtomicLong atomicLong = new AtomicLong(1000);
private CacheService cacheService;
public GmallCacheAspect(CacheService cacheService) {
this.cacheService = cacheService;
}
public GmallCacheAspect() {
}
@Around("@annotation(com.sgg.aopcache.cache.anno.GmalllCache)")
public Object around(ProceedingJoinPoint joinPoint) throws Exception {
//先拿到目标方法执行的参数
Object[] args = joinPoint.getArgs();
//拿到方法声明
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
//拿到方法声明上注解
Method method = signature.getMethod();
//获取方法方法的返回值类型
final Type returnType = method.getGenericReturnType();
GmalllCache cacheAnnotation = method.getAnnotation(GmalllCache.class);
//拿到注解上的值 缓存数据的key
String cacheKeyExpr = cacheAnnotation.cacheKey();
String bloomValueExpr = cacheAnnotation.bloomValue();
//Sp EL表达式 解析
String cacheKey = parseSpel(cacheKeyExpr, joinPoint,"cacheKey");
Long bloomValue=null;
if (!StringUtils.isEmpty(bloomValueExpr)){
String bloomVlaueStr = parseSpel(bloomValueExpr, joinPoint,"bloomValue");
bloomValue = Long.valueOf(bloomVlaueStr);
}
while (true){
//从缓冲中拿数据 并根据方法的返回值类型封装数据
Object dataFromCache = cacheService.getDataFromCache(cacheKey, new TypeReference() {
@Override
public Type getType() {
//返回方法对应的返回类型
return returnType;
}
});
if (StringUtils.isEmpty(dataFromCache)) {
//缓存中没有数据 回源 经过布隆过滤器和锁
boolean contains = true;
if (!StringUtils.isEmpty(bloomValue)){
RBloomFilter bloomFilter = cacheService.getBloomFilter(cacheAnnotation.bloomName());
//布隆过滤器判断是否有值
contains = bloomFilter.contains(bloomValue);
}
if (contains) {
//如果布隆过滤器中有这个对象
//获取注解的分布式锁 lockKey的值
String lockKeyExpr = cacheAnnotation.lockKey();
String lockKey = (String) parseSpel(lockKeyExpr, joinPoint,"lockKey");
//连接点方法的返回值对象
Object detail = null;
//如果锁的值为空 不使用分布式锁 直接查数据库
if (StringUtils.isEmpty(lockKey)) {
detail = getProceed(joinPoint);
} else {
//拿到锁对象
RLock rLock = cacheService.getLock(lockKey);
boolean tryLockFlag = false;
try {
//尝试加锁
tryLockFlag = rLock.tryLock();
//如果加锁成功
if (tryLockFlag) {
long curr = System.currentTimeMillis();
//查询数据库 也就是执行目标方法
detail = getProceed(joinPoint);
atomicLong.set(System.currentTimeMillis() - curr);
//
//将切入点方法查询到的数据存入缓存
cacheService.saveCacheData(cacheKey, detail, returnType);
return detail;
} else {
//如果加锁失败 等待一秒后重新查询
TimeUnit.MINUTES.sleep(atomicLong.get());
}
} catch (InterruptedException e) {
log.error("加锁出现了异常");
throw new RuntimeException(e);
} finally {
try {
//加锁成功后解锁
if (tryLockFlag) {
rLock.unlock();
}
} catch (Exception e) {
log.error("解错了锁");
throw new RuntimeException(e);
}
}
}
} else {
//如果布隆过滤器中没有,返回空对象
return null;
}
} else {
//缓存中有数据 返回缓存中数据
return dataFromCache;
}
}
}
/**
* 执行目标方法 获取返回数据
*
* @param joinPoint
* @return
*/
private Object getProceed(ProceedingJoinPoint joinPoint) {
Object proceed = null;
try {
//前置通知
Object[] args = joinPoint.getArgs();
proceed = joinPoint.proceed(args);
//返回后通知
} catch (Throwable e) {
//异常通知
throw new RuntimeException(e);
} finally {
//后置通知
}
return proceed;
}
/**
* 解析spel表达式的值
*
* @param cacheKey
* @return
*/
private String parseSpel(String cacheKey, ProceedingJoinPoint joinPoint,String type) {
//拿到方法的参数值
Object[] args = joinPoint.getArgs();
//拿到方法对象
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
if (args.length==0){
if (type.contains("lockKey")){
return signature.getMethod().getName()+":lockKey:";
}
if (type.contains("cacheKey")){
return signature.getMethod().getName()+":cacheKey:";
}
}
ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext evaluationContext = new StandardEvaluationContext();
evaluationContext.setVariable("args", args);
evaluationContext.setVariable("method", signature);
String value = parser.parseExpression(cacheKey, new TemplateParserContext())
.getValue(evaluationContext, String.class);
return value;
}
}
6.CacheService
CacheService接口
public interface CacheService {
/**
* 从缓冲中获取数据
* @param spelValue
* @param typeReference
* @return
*/
T getDataFromCache(String spelValue, TypeReference typeReference) throws Exception;
/**
* 获取布隆过滤器名
* @param bloomName
*/
RBloomFilter getBloomFilter(String bloomName);
/**
* 获取一把分布式锁
* @param lockKey
* @return
*/
RLock getLock(String lockKey);
/**
* 将数据存入缓存
* @param cacheKey
* @param detail
* @param returnType
*/
void saveCacheData(String cacheKey, Object detail, Type returnType) throws Exception;
}
CacheServiceImpl 实现类
/**
* @author sz
* @DATE 2022/3/13 13:29
*/
public class CacheServiceImpl implements CacheService {
StringRedisTemplate redisTemplate; //操作redis
RedissonClient redissonClient; //使用redisson
ObjectMapper objectMapper = new ObjectMapper();
public CacheServiceImpl(StringRedisTemplate redisTemplate, RedissonClient redissonClient) {
this.redisTemplate = redisTemplate;
this.redissonClient = redissonClient;
}
/**
* 从缓冲获取数据
*
* @param spelValue
* @param returnType
* @return
*/
public T getDataFromCache(String spelValue, TypeReference returnType) throws Exception {
String json = redisTemplate.opsForValue().get(spelValue);
if (StringUtils.isEmpty(json)) {
return null;
}
T value = objectMapper.readValue(json, returnType);
return value;
}
/**
* 获取布隆过滤器
*
* @param bloomName
*/
public RBloomFilter getBloomFilter(String bloomName) {
RBloomFilter bloomFilter = redissonClient.getBloomFilter(bloomName);
return bloomFilter;
}
/**
* 获取分布式锁
*
* @param lockKey
* @return
*/
public RLock getLock(String lockKey) {
return redissonClient.getLock(lockKey);
}
/**
* 将数据存入缓存
* @param cacheKey
* @param detail
* @param returnType
*/
public void saveCacheData(String cacheKey, Object detail, Type returnType) throws Exception {
//要缓存的数据
Object target = detail;
//缓存空值
if (StringUtils.isEmpty(target)) {
//如果从数据库中查询的数据时null
redisTemplate.opsForValue().set(cacheKey,"n",30, TimeUnit.MINUTES);
}else {
String writeValueAsString = new ObjectMapper().writeValueAsString(target);
redisTemplate.opsForValue().set(cacheKey, writeValueAsString,3, TimeUnit.DAYS);
}
}
}