今天实现的是一个缓存管理器的代替,也是我自己的一个小尝试,有不足的地方需要大家提出,思路大部分都是按照Redis的缓存管理器的想法来的(文章会很长)
spring boot 版本 2.1.6.RELEASE
引入redis
org.springframework.boot
spring-boot-starter-data-redis
引入AOP
org.springframework.boot
spring-boot-starter-aop
引入FastJson
com.alibaba
fastjson
1.2.61
统一解说一下
注解上的参数代表意义
key代表的缓存前缀例 user:info:
keys代表前缀后面拼接的参数值,例 keys = “phone”,传递方法中phone的参数是1234,则写入缓存的key是:user:info:1234,这个可以写EL表达式,用来解析对象参数的某一个值,例参数值为User user,keys="#user.phone",则解析后为user:info:
涉及有三个注解,三个AOP切面的实现类,一个EL表达式解析类
RedisSetString 写入和更新
RedisGetString 单纯获取
RedisDelString 删除
今天先来kotlin的
首先写三个注解 RedisSetString、RedisGetString、RedisDelString
注解RedisSetString代码如下:
@Target(AnnotationTarget.FUNCTION) //表明适用于方法上
@Retention(AnnotationRetention.RUNTIME) //表明运行时
@MustBeDocumented
annotation class RedisSetString(val key: String = "", val keys: String = "", val expire: Long = 0L, val timeUnit: TimeUnit = TimeUnit.SECONDS)
参数解析:
key -- 缓存前缀
keys -- 获取方法相同参数名的值用于拼接完整的key,
expire -- 缓存有效时间,默认为0L
timeUnit -- 缓存有效时间的单位,默认是秒
注解RedisGetString代码如下:
@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
@MustBeDocumented
annotation class RedisGetString(val key: String = "", val keys: String = "")
key -- 缓存前缀
keys -- 获取方法相同参数名的值用于拼接完整的key,
注解RedisDelString代码如下
@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
@MustBeDocumented
annotation class RedisDelString(val key: String = "", val keys: String = "")
上面就是三个注解的代码,接下来是一个Keys值得获取与解析类,支持EL表达式,创建AspectExpression 类
import com.alibaba.fastjson.JSON
import com.alibaba.fastjson.JSONObject
import com.bedding.core.exception.NotFoundException
import org.aspectj.lang.ProceedingJoinPoint
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.core.DefaultParameterNameDiscoverer
import org.springframework.expression.EvaluationContext
import org.springframework.expression.Expression
import org.springframework.expression.spel.standard.SpelExpressionParser
import org.springframework.expression.spel.support.StandardEvaluationContext
import java.lang.Exception
import java.lang.StringBuilder
import java.lang.reflect.Method
object AspectExpression {
/**
* 用于SpEL表达式解析.
*/
private val parser = SpelExpressionParser()
/**
* 用于获取方法参数定义名字.
*/
private val nameDiscoverer = DefaultParameterNameDiscoverer()
private val logger: Logger = LoggerFactory.getLogger(AspectExpression::class.java)
fun getKey(keys: String, method: Method, joinPoint: ProceedingJoinPoint): String {
// 使用spring的DefaultParameterNameDiscoverer获取方法形参名数组
val paramNames = nameDiscoverer.getParameterNames(method)
// 通过joinPoint获取被注解方法的形参
val args = joinPoint.args
return if (!paramNames.isNullOrEmpty()) {
//当没有指定keys时,把所有参数的值拼接成一个key,对象的使用字典排序
if (keys == "") {
val value = StringBuffer()
args.forEach {
val str = getStr(it)
if (str.first().toString() == ":") {
value.append(str)
} else {
value.append(":").append(str)
}
}
value.toString()
}
//指定的keys
else {
//当keys中不存在#即没有使用el表达式,
if (!keys.contains("#")) {
getStr(args[paramNames.toList().indexOf(keys)])
}
//存在#符号,则是支持EL表达式
else {
// 解析过后的Spring表达式对象
val expression: Expression = parser.parseExpression(keys)
// spring的表达式上下文对象
val context: EvaluationContext = StandardEvaluationContext()
// 给上下文赋值
for (i in args.indices) {
context.setVariable(paramNames[i], args[i])
}
expression.getValue(context).toString()
}
}
} else {
throw NotFoundException("没有找到对应的参数")
}
}
//判断是否支持序列化
private fun getStr(any: Any): String {
return try {
val data = JSON.parseObject(JSON.toJSONString(any), JSONObject()::class.java)
//固定排序后开始拼接
val keySet = data.keys.sorted()
val value = StringBuffer()
keySet.forEach {
if (data[it] != null) {
value.append(":").append(data[it])
}
}
value.toString()
} catch (e: Exception) {
any.toString()
}
}
}
只要把keys 传入即可返回出解析后的值
然后创建RedisSetString 的 AOP切面类RedisSetStringAspect需要引入StringRedisTemplate
类RedisSetString的代码如下:
import com.alibaba.fastjson.JSON
import com.bedding.web.core.annotations.RedisSetString
import org.aspectj.lang.ProceedingJoinPoint
import org.aspectj.lang.annotation.*
import org.aspectj.lang.reflect.MethodSignature
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.data.redis.core.StringRedisTemplate
import org.springframework.stereotype.Component*
@Aspect
@Component
class RedisSetStringAspect(private val redisTemplate: StringRedisTemplate) {
private val logger: Logger = LoggerFactory.getLogger(RedisSetStringAspect::class.java)
@Pointcut("@annotation(com.bedding.web.core.annotations.RedisSetString)")
fun pointcutRedisSetString() {
}
//包围切面
//该注解表示拦截添加了@RedisSetString注解的方法体
@Around("pointcutRedisSetString()")
fun redisSetStringBefore(joinPoint: ProceedingJoinPoint): Any {
val sign: MethodSignature = joinPoint.signature as MethodSignature
val method = sign.method
//获取方法上的注解
val annotation = method.getAnnotation(RedisSetString::class.java)
//keys代表的值
val parameter = AspectExpression.getKey(annotation.keys, method, joinPoint)
//判断自己个字符是不是":",然后进行拼接
val key = if (parameter.first().toString() == ":"){
"${annotation.key}${parameter}"
}else{
"${annotation.key}:${parameter}"
}
//调用方法内的所有内容获取返回值
val result = joinPoint.proceed()
//序列化采用的是FastJson
if (annotation.expire != 0L) {
//如果对应的时间是默认值0L则是永久写入
redisTemplate.opsForValue().set(key, JSON.toJSONString(result))
} else {
//如果写入了则是按传递的来写入
redisTemplate.opsForValue().set(key, JSON.toJSONString(result), annotation.expire, annotation.timeUnit)
}
return result
}
}
然后创建RedisGetString 的 AOP切面类RedisGetStringAspect需要引入StringRedisTemplate
类RedisGetString的代码如下
import com.alibaba.fastjson.JSON
import com.bedding.web.core.annotations.RedisGetString
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.springframework.data.redis.core.StringRedisTemplate
import org.springframework.stereotype.Component
@Aspect
@Component
class RedisGetStringAspect(private val redisTemplate: StringRedisTemplate) {
private val logger: Logger = LoggerFactory.getLogger(RedisGetStringAspect::class.java)
@Pointcut("@annotation(com.bedding.web.core.annotations.RedisGetString)")
fun pointcutRedisGetString() {
}
//该注解表示拦截添加了@RedisGetString注解的方法体
@Around(value = "pointcutRedisGetString()")
fun redisGetStringBefore(joinPoint: ProceedingJoinPoint): Any {
val sign: MethodSignature = joinPoint.signature as MethodSignature
val method = sign.method
//获取方法上的注解
val annotation = method.getAnnotation(RedisGetString::class.java)
val parameter = AspectExpression.getKey(annotation.keys, method, joinPoint)
val key = if (parameter.first().toString() == ":"){
"${annotation.key}${parameter}"
}else{
"${annotation.key}:${parameter}"
}
//实现判断这个存不存在,然后再去获取
return if (redisTemplate.hasKey(key)) {
//采用FastJson的一个序列化方式,获取值然后获取方法的返回参数类型再来序列化
JSON.parseObject(redisTemplate.opsForValue().get(key), method.returnType)
} else {
//不存在则去执行方法,然后返回
joinPoint.proceed()
}
}
}
然后创建RedisDelString 的 AOP切面类RedisDelStringAspect需要引入StringRedisTemplate
类RedisDelString的代码如下
import com.bedding.core.exception.NotFoundException
import com.bedding.web.core.annotations.RedisDelString
import org.aspectj.lang.ProceedingJoinPoint
import org.aspectj.lang.annotation.*
import org.aspectj.lang.reflect.MethodSignature
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.data.redis.core.StringRedisTemplate
import org.springframework.stereotype.Component
@Aspect
@Component
class RedisDelStringAspect(private val redisTemplate: StringRedisTemplate) {
private val logger: Logger = LoggerFactory.getLogger(RedisDelStringAspect::class.java)
@Pointcut("@annotation(com.bedding.web.core.annotations.RedisDelString)")
fun pointcutRedisDelString() {
}
//该注解表示拦截添加了@RedisDelString注解的方法体
@Around("pointcutRedisDelString()")
fun redisDelStringBefore(joinPoint: ProceedingJoinPoint): Any {
val sign: MethodSignature = joinPoint.signature as MethodSignature
val method = sign.method
//获取方法上的注解
val annotation = method.getAnnotation(RedisDelString::class.java)
val parameter = AspectExpression.getKey(annotation.keys, method, joinPoint)
val key = if (parameter.first().toString() == ":"){
"${annotation.key}${parameter}"
}else{
"${annotation.key}:${parameter}"
}
//事先判断判断存不存在
return if (redisTemplate.hasKey(key)) {
//存在则去删除
redisTemplate.delete(key)
} else {
//此处是我的自定义异常,需要完整使用这需要改称自己的
throw NotFoundException("缓存中没有记录")
}
}
}
以上就是Kotlin的一个自定义注解代替Redis缓存管理器的实现方案.这个还不是很完善,如果大家有好的思路可以提供到我,我可以整改,接下来是JAVA的实现方式,注解和类命名一致
同样写三个注解 RedisSetString、RedisGetString、RedisDelString
注解RedisSetString代码如下:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RedisSetString {
public String key() default "";
public String keys() default "";
public long expire() default 0L;
public TimeUnit timeUnit() default TimeUnit.SECONDS;
}
参数解析:
key -- 缓存前缀
keys -- 获取方法相同参数名的值用于拼接完整的key,
expire -- 缓存有效时间,默认为0L
timeUnit -- 缓存有效时间的单位,默认是秒
注解RedisGetString代码如下:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RedisGetString {
public String key() default "";
public String keys() default "";
}
key -- 缓存前缀
keys -- 获取方法相同参数名的值用于拼接完整的key,
注解RedisDelString代码如下
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RedisDelString {
public String key() default "";
public String keys() default "";
}
上面就是三个注解的代码,接下来是一个Keys值得获取与解析类,支持EL表达式,创建AspectExpression 类
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.springcloud.common.core.exception.NotFoundException;
import org.aspectj.lang.ProceedingJoinPoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
public class AspectExpression {
private Logger logger = LoggerFactory.getLogger(AspectExpression.class);
/**
* 用于SpEL表达式解析.
*/
;
private static SpelExpressionParser parser = new SpelExpressionParser();
/**
* 用于获取方法参数定义名字.
*/
private static DefaultParameterNameDiscoverer nameDiscoverer = new DefaultParameterNameDiscoverer();
public static String getKey(String keys, Method method, ProceedingJoinPoint joinPoint) {
// 使用spring的DefaultParameterNameDiscoverer获取方法形参名数组
String[] paramNames = nameDiscoverer.getParameterNames(method);
// 通过joinPoint获取被注解方法的形参
Object[] args = joinPoint.getArgs();
//当没有指定keys时,把所有参数的值拼接成一个key,对象的使用字典排序
if (paramNames != null) {
if ("".equals(keys)) {
StringBuffer value = new StringBuffer();
Arrays.asList(args).forEach(it -> {
String str = getStr(it);
if (str.subSequence(0, 1) == ":") {
value.append(str);
} else {
value.append(":").append(str);
}
});
return value.toString();
}
//指定的keys
else {
//当keys中不存在#即没有使用el表达式,
if (!keys.contains("#")) {
return getStr(args[Arrays.asList(paramNames).indexOf(keys)]);
}
//存在#符号,则是支持EL表达式
else {
// 解析过后的Spring表达式对象
Expression expression = parser.parseExpression(keys);
// spring的表达式上下文对象
EvaluationContext context =new StandardEvaluationContext();
// 给上下文赋值
for (int i = 0; i < args.length; i++) {
context.setVariable(paramNames[i], args[i]);
}
return Objects.requireNonNull(expression.getValue(context)).toString();
}
}
} else {
throw new NotFoundException("没有找到对应的参数");
}
}
//判断是否支持序列化
private static String getStr(Object any) {
try {
JSONObject data = JSON.parseObject(JSON.toJSONString(any), JSONObject.class);
//固定排序后开始拼接
List keySet = data.keySet().stream().sorted().collect(Collectors.toList());
StringBuffer value = new StringBuffer();
keySet.forEach(it -> {
if (data.get(it) != null) {
value.append(":").append(data.get(it));
}
});
return value.toString();
} catch (Exception e) {
return any.toString();
}
}
}
只要把keys 传入即可返回出解析后的值
然后创建RedisSetString 的 AOP切面类RedisSetStringAspect需要引入StringRedisTemplate
类RedisSetString的代码如下:
import com.alibaba.fastjson.JSON;
import com.springcloud.userserver.core.annotations.RedisSetString;
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.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
@Aspect
@Component
public class RedisSetStringAspect {
@Autowired
private StringRedisTemplate redisTemplate;
@Pointcut("@annotation(com.springcloud.userserver.core.annotations.RedisSetString)")
public void pointcutRedisSetString() {
}
//该注解表示拦截添加了@RedisSetString注解的方法体
@Around("pointcutRedisSetString()")
public Object redisSetStringBefore(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature sign = (MethodSignature) joinPoint.getSignature();
Method method = sign.getMethod();
//获取方法上的注解
RedisSetString annotation = method.getAnnotation(RedisSetString.class);
String parameter = AspectExpression.getKey(annotation.keys(), method, joinPoint);
String key;
if (parameter.subSequence(0, 1) == ":") {
key = annotation.key() + parameter;
} else {
key = annotation.key() + ":" + parameter;
}
Object result = joinPoint.proceed();
if (annotation.expire() != 0L) {
redisTemplate.opsForValue().set(key, JSON.toJSONString(result));
} else {
redisTemplate.opsForValue().set(key, JSON.toJSONString(result), annotation.expire(), annotation.timeUnit());
}
return result;
}
}
然后创建RedisGetString 的 AOP切面类RedisGetStringAspect需要引入StringRedisTemplate
类RedisGetString的代码如下
import com.alibaba.fastjson.JSON;
import com.springcloud.userserver.core.annotations.RedisGetString;
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.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
@Aspect
@Component
public class RedisGetStringAspect {
@Autowired
public StringRedisTemplate redisTemplate;
@Pointcut("@annotation(com.springcloud.userserver.core.annotations.RedisGetString)")
public void pointcutRedisGetString() {
}
@Around(value = "pointcutRedisGetString()") //该注解表示拦截添加了@RedisSetString注解的方法体
public Object redisGetStringBefore(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature sign = (MethodSignature) joinPoint.getSignature();
Method method = sign.getMethod();
//获取方法上的注解
RedisGetString annotation = method.getAnnotation(RedisGetString.class);
String parameter = AspectExpression.getKey(annotation.keys(), method, joinPoint);
String key;
if (parameter.subSequence(0, 1) == ":") {
key = annotation.key() + parameter;
} else {
key = annotation.key() + ":" + parameter;
}
Boolean hasKey = redisTemplate.hasKey(key);
if (hasKey != null && hasKey) {
return JSON.parseObject(redisTemplate.opsForValue().get(key), method.getReturnType());
} else {
return joinPoint.proceed();
}
}
}
然后创建RedisDelString 的 AOP切面类RedisDelStringAspect需要引入StringRedisTemplate
类RedisDelString的代码如下
import com.springcloud.common.core.exception.NotFoundException;
import com.springcloud.userserver.core.annotations.RedisDelString;
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.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
@Aspect
@Component
public class RedisDelStringAspect {
@Autowired
private StringRedisTemplate redisTemplate;
private Logger logger = LoggerFactory.getLogger(RedisDelStringAspect.class);
@Pointcut("@annotation(com.springcloud.userserver.core.annotations.RedisDelString)")
public void pointcutRedisDelString() {
}
//该注解表示拦截添加了@RedisDelString注解的方法体
@Around("pointcutRedisDelString()")
public Object redisDelStringBefore(ProceedingJoinPoint joinPoint) {
MethodSignature sign = (MethodSignature) joinPoint.getSignature();
Method method = sign.getMethod();
//获取方法上的注解
RedisDelString annotation = method.getAnnotation(RedisDelString.class);
String parameter = AspectExpression.getKey(annotation.keys(), method, joinPoint);
String key;
if (parameter.subSequence(0, 1) == ":") {
key = annotation.key() + parameter;
} else {
key = annotation.key() + ":" + parameter;
}
Boolean hasKey = redisTemplate.hasKey(key);
if (hasKey != null && hasKey) {
return redisTemplate.delete(key);
} else {
throw new NotFoundException("缓存中没有记录");
}
}
}
关联代码已经上传:
下载链接:https://download.csdn.net/download/qq_40466900/12737503
所需积分:0
以上就是JAVA的自定注解以及AOP切面实现Redis的写入,更新查看以及删除。
如果有更好的实现方式可以一起聊天,然后更加完善