AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。利用AOP可以对业务逻辑的各个部分进行隔离。
为什么要使用AOP?
在日常开发过程中,我们经常会对业务代码进行扩展,例如增加日志,鉴权等等,如果按原有逻辑,我们需要改动原有的所有代码,这会增加开发量和降低可维护性,如果把新增的功能抽取出来,形成一个切面,我们可以将整个逻辑功能看成一个柱体的话,将这个面嵌入柱体的过程就叫织入,这是一个不会影响原有代码逻辑下对原有代码进行扩展的实现思路,可以减少代码量,增加可维护性。
下面,我们可以通过自定义注解的形式,来确定哪些方法需要用切面进行增加缓存功能
1.定义注解
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface LyQueryCacheEnabled {
String value() default "true";
}
2.定义Aspect
@Component
@Aspect
public class LyQueryCacheAspect {
@Resource
RedisManager redisManager;
@Pointcut("@annotation(xxx.java.crm.common.annotation.LyQueryCacheEnabled)")
public void annotationPointcut() {
}
private static Long getMillisToNextDayBegin() {
Calendar cal = Calendar.getInstance();
cal.add(Calendar.DAY_OF_YEAR, 1);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.MILLISECOND, 0);
return (cal.getTimeInMillis() - System.currentTimeMillis());
}
private static boolean isTimeToSetCache(long seconds){
//23:00-3:00之间不设置缓存
long middleNigntBefore = 1000*60*60*21;
long middleNigntAfter = 1000*60*60*1;
if(middleNigntAfter<seconds&&seconds<middleNigntBefore){
return true ;
}else{
return false ;
}
}
@Around("annotationPointcut()")
public Object aroundMethod(ProceedingJoinPoint point){
Object result = null ;
String callMethod = point.getSignature().getName();
Object[] args = point.getArgs();
String callArgs = JSON.toJSONString(point.getArgs());
String key = callMethod + callArgs;
Class<?>[] paramsCls = new Class<?>[args.length];
for (int i = 0; i < args.length; ++i) {
paramsCls[i] = args[i].getClass();
}
//获取方法
Method method = null;
try {
method = point.getTarget().getClass().getMethod(point.getSignature().getName(), paramsCls);
} catch (NoSuchMethodException e) {
SkyLoggerClient.error("redis缓存织入异常堆栈->未找到方法:",e);
throw new BusinessException("system error");
}
//获取返回值类型
Class<?> classs = method.getReturnType();
SkyLoggerClient.info("redis缓存织入",">>>>>>>>into redis fast call check>>>>>>>>>>>");
SkyLoggerClient.info("redis缓存织入","method " + callMethod + " is called on " + " with " +"args" +" " + callArgs);
SkyLoggerClient.info("redis缓存织入","返回值类型 "+ classs.toString());
if(redisManager.exist(key)){
SkyLoggerClient.info("redis缓存织入","缓存命中,返回redis缓存的值");
String val = redisManager.get(key);
result = JSON.parseObject(val,classs);
}else{
SkyLoggerClient.info("redis缓存织入","缓存未命中,执行业务代码查询数据库");
try {
result = point.proceed();
} catch (Throwable throwable) {
SkyLoggerClient.error("redis缓存织入异常堆栈:",throwable);
throw new BusinessException("system error");
}
if(result!=null){
String returnVals = JSON.toJSONString(result);
long timeToDayEnd = getSecondsToNextDayBegin();
if(isTimeToSetCache(timeToDayEnd)) {
String idxKey = "CRM_FREQ_"+key;
long limitFrequency = 2L ;
if(redisManager.increment(idxKey,timeToDayEnd)>=limitFrequency) {//增加频率限制,只有频率高的查询结果才会被缓存
redisManager.set(key, returnVals, timeToDayEnd);
SkyLoggerClient.info("redis未命中后缓存设置", "缓存设置,到零点" + timeToDayEnd / 60000 + "分钟,缓存设置成功");
}
}
}
}
SkyLoggerClient.info("redis缓存织入",">>>>>>>>>>>>>>>>>>>>>>>>>>>");
return result;
}
3.启用Aspect
@EnableAspectJAutoProxy
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
4.在需要使用查询缓存的业务方法中使用自定义注解
@LyQueryCacheEnabled
public CrmBusiAnalysisGrowthVo getGmv(CrmBusiAnalysisDto.BusiAnalysisCommonRequest request) {
.....
}