需求是這樣的,業務代碼需要使用到緩存功能以減少數據庫壓力,使用redis來實現,並且需要生成緩存的key由方法的傳參拼接而成(貌似也只能這樣才能保證同樣的select查詢可以使用緩存),簡單的方式就是在需要緩存的方法內加上大概這樣的邏輯:查詢緩存--->沒有則查詢數據庫 --->查詢結果以key-value形式放入緩存,這樣對業務代碼造成了侵入,並且產生大量重復的代碼,很不優雅,所以決定自己封裝一個緩存模塊,在需要緩存的地方只需要加入一個注解即可。
首先自定義一個注解
package com.example.test.aspect; import java.lang.annotation.*; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) @Inherited public @interface MyCache { String value() default ""; }
定義切面
package com.example.test.aspect; 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.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 java.lang.reflect.Method; @Component @Aspect public class MyAspect { //切入點,帶有Myche注解並且是UserService類下的方法 @Pointcut("@annotation(com.example.test.aspect.MyCache)&&execution(* com.example.test.service.UserService.*(..))") public void pointCut(){}; @Around("pointCut()") public Object cache(ProceedingJoinPoint joinPoint)throws Throwable{ //獲取方法簽名 MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); //獲取目標方法 Method method = methodSignature.getMethod(); // Method method2 = joinPoint.getTarget().getClass().getMethod(methodSignature.getName(),methodSignature.getParameterTypes()); //獲取方法上的注解 MyCache myCache = method.getDeclaredAnnotation(MyCache.class); //得到el表達式 String el = myCache.value(); //解析el表達式,將#id等替換為參數值 ExpressionParser expressionParser = new SpelExpressionParser(); Expression expression = expressionParser.parseExpression(el); EvaluationContext context = new StandardEvaluationContext(); String[] parameterNames = methodSignature.getParameterNames(); Object[] args = joinPoint.getArgs(); for (int i = 0; i <parameterNames.length ; i++) { context.setVariable(parameterNames[i],args[i]); } String key = expression.getValue(context).toString(); System.out.println(key); //根據key從緩存中拿數據,這里省略 //如果緩存中沒有則執行目標方法 Object o = joinPoint.proceed(); //將結果放入緩存,這里省略 return o; } }
測試的service
package com.example.test.service; import com.example.test.aspect.MyCache; import org.springframework.stereotype.Service; @Service public class UserService implements UserServiceInterface{ @MyCache("'asd_' + #id") @Override public void addUser(String id,String name){ System.out.println(id + "----" + name); } }
測試類,這里是springboot項目
package com.example.test; import com.example.test.service.UserService; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest public class TestApplicationTests { @Autowired private UserService userService; @Test public void contextLoads() { System.out.println(userService.getClass().getName()); userService.addUser("12345" ,"zhagnsan"); } }
執行結果如下:
修改UserService上的注解為:"'asd_' + #id + '_' + #name",結果如下