AOP テクノロジーを使用してカスタム キャッシュ アノテーションを実装する
ビジネス開発に Java を使用する場合、キャッシングは不可欠なパフォーマンス向上テクノロジーです。キャッシュ ロジックがビジネス コードに追加される場合、いくつかの欠点があります。1) コードの冗長性。2) コード ロジックが非常に煩雑で、バグが入りやすい 3) 人によってコード スタイルが異なり、後のメンテナンスが困難になる。エントリにキャッシュ アノテーションを追加する方法があります. インターフェースが呼び出される前に, キャッシュが自動的に取得されます. キャッシュがなければ, インターフェース ロジックに従います. これはより明確でコードはよりクリーンです. 上記のアイデアを実現するにはステップ 1:次の
ように、アスペクト ロジックを記述し、アスペクト ファイルを作成します。
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.local.datashadow.annotion.LocalRedisCache;
import com.local.datashadow.utils.DateUtils;
import com.local.datashadow.utils.RedisUtil;
import lombok.extern.slf4j.Slf4j;
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.expression.EvaluationContext;
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 java.lang.reflect.Type;
import java.util.concurrent.TimeUnit;
/**
* @Author : test
* @create 2022/12/07 14:42
* description:
*/
@Slf4j
@Aspect
@Component
public class RedisCacheAspect {
private static ExpressionParser parser = new SpelExpressionParser();
@Pointcut("@annotation(com.local.datashadow.annotion.LocalRedisCache)")//注解在方法上
public void pointcut() {
System.out.println( "进入切面执行" );
}
/**
* 环绕通知
* 根据redisKey先查缓存、缓存没有就查数据库,然后将结果缓存
*/
@Around(value = "pointcut() && @annotation(localRedisCache)", argNames = "pjp,localRedisCache")
public Object AroundMethod(ProceedingJoinPoint pjp, LocalRedisCache localRedisCache) throws Throwable {
log.info("redis缓存切面开始");
String redisKey = localRedisCache.redisKey();
// 1.获取参数对象数组
Object[] args = pjp.getArgs();
// 2.获取参数名数组
MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
String[] argNames = methodSignature.getParameterNames();
// 3.将参数名和参数值数组放入context
EvaluationContext context = new StandardEvaluationContext();
for (int i = 0; i < args.length; i++) {
context.setVariable(argNames[i], args[i]);
}
//spring EL表达式,此处进行了key的组装,此处需要注意,如果key不需要带参数,需要把key放到注解的单引号里引起来,不然报错
redisKey = buildKey(redisKey,context);
String redisEventValue = RedisUtil.get( redisKey );
long expirationTime = localRedisCache.expirationTime();
if (expirationTime == 0 ){
log.info("获取的缓存时长=",expirationTime);
expirationTime = DateUtils.getExpirationTime();
}
Type genericReturnType = methodSignature.getMethod().getGenericReturnType();
if(redisEventValue == null){
log.info("redis key="+redisKey+"缓存不存在...");
Object result = pjp.proceed();
if (result != null){
JSONObject jsonObject = (JSONObject)JSON.toJSON(result);
if (("00000".equals(jsonObject.getString("rspCode")) ||
"00000".equals(jsonObject.getString("code")))
&& (jsonObject.getInteger("resultDataStatus") == 1)){
log.info("将 key="+redisKey+" 缓存到redis里面...");
RedisUtil.set(redisKey, JSON.toJSONString(result), expirationTime, TimeUnit.MILLISECONDS);
}
} else {
log.info("查询结果不存在不缓存到redis里面...");
}
return result;
}else {
log.info(" key="+redisKey+"的结果是从缓存里面获取的");
Object returnObj = JSONObject.parseObject(redisEventValue, genericReturnType);
return returnObj;
}
}
protected static String buildKey(String key, EvaluationContext context) {
String combined = "";
if (key != null && key.length()>0) {
combined = parser.parseExpression(key).getValue(context).toString();
}
return combined;
}
}
パート 2: カスタム アノテーション インターフェースを作成する
package com.local.datashadow.annotion;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @Author : test
* @create 2022/12/07 14:28
* description:
* Redis 缓存设置
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LocalRedisCache {
String redisKey() default "";
/**
* 缓存默认时长毫秒
* @return
*/
long expirationTime() default 0;
}
インターフェースの属性は、必要に応じて追加されます. この属性は、アスペクトを介してインターセプトされ、対応する処理のためにアスペクトで取得されます.
ステップ 3: 方法 1 を使用する:
上記の作業が完了したら、使用を開始します. サービス実装クラスで、メソッドにアノテーションを追加します. たとえば、
次のように:
@LocalRedisCache(redisKey = "'dataShadow:kpi_month_user_active_summary'")
@Override
public BaseResponse getMonthUserActiveSummary() {
log.info("月活逻辑处理开始");
SummarySearchDTO searchDto = new SummarySearchDTO();
searchDto.setDate6Month();
String endMonth = searchDto.getEndDate().substring(0, 6);
String starMonth = searchDto.getStarDate().substring(0, 6);
searchDto.setEndDate(endMonth);
searchDto.setStarDate(starMonth);
注意上面使用的注解,就是自定义注解,注解括号里的参数就是自定义注解接口中定义的参数
ステップ 3: 方法 2 を使用:
一部の rediskey は、ユーザーに推奨されるコンテンツなどのパラメーターに関連付ける必要があります, なぜなら、異なるユーザー ID またはデバイス ID には異なる推奨事項があるためです. この時点で、rediskey はユーザー属性に関連付ける必要があります。つまり、rediskey は
次のようなパラメーターを持ってくる必要があります。
@Override
@LocalRedisCache(
redisKey = "'dataShadow:event_property_' + #carEventDimPo.eventCode",expirationTime = 86400000)
public BaseResponse<List> getCarEventProperties(CarEventDimPo carEventDimPo) {
try {
Integer eventNum = carEventDimPo.getEventNum();
List<String> eventCodeList = new ArrayList<>();
//查询公共属性
eventCodeList.add("all");
使用#号取参数,直接把请求参数中的这个值获取到,这个方式是使用了EL表达式,EL表达式自行学习。
キー内の文字列を単一引用符で囲んで文字列に処理する必要があることに注意してください。
上記の3ステップがredisの利用ですが、もちろんredis環境の設定など利用前の準備が必要です. 以下の設定ファイル
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
public class RedisConfig extends CachingConfigurerSupport {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
RedisSerializer<String> redisSerializer = new StringRedisSerializer();
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
template.setConnectionFactory(factory);
//key序列化方式
template.setKeySerializer(redisSerializer);
//value序列化
template.setValueSerializer(jackson2JsonRedisSerializer);
//value hashmap序列化
template.setHashKeySerializer(jackson2JsonRedisSerializer);
template.setHashValueSerializer(jackson2JsonRedisSerializer);
return template;
}
}
redis のプロパティ パラメータは、nacos 統合ファイルで設定されます。
spring:
main:
allow-bean-definition-overriding: true
redis:
# Redis服务器地址 ***.**.**.***
host: ***************.com
# Redis服务器连接端口
port: 6379
# Redis数据库索引(默认为0)
database: 2
# Redis服务器连接密码(默认为空)
password: ********
# 连接池最大连接数(使用负值表示没有限制)
max-active: 8
# 连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: -1
# 连接池中的最大空闲连接
max-idle: 8
# 连接池中的最小空闲连接
min-idle: 0
# 连接超时时间(毫秒)
#timeout: 30000
構成ファイルに加えて、redis のストレージや検索などのパブリック メソッド クラスがあります。