需求是這樣的,業務代碼須要使用到緩存功能以減小數據庫壓力,使用redis來實現,而且須要生成緩存的key由方法的傳參拼接而成(貌似也只能這樣才能保證一樣的select查詢能夠使用緩存),簡單的方式就是在須要緩存的方法內加上大概這樣的邏輯:查詢緩存--->沒有則查詢數據庫 --->查詢結果以key-value形式放入緩存,這樣對業務代碼形成了侵入,而且產生大量重複的代碼,很不優雅,因此決定本身封裝一個緩存模塊,在須要緩存的地方只須要加入一個註解便可。java
首先自定義一個註解redis
package com.example.test.aspect; import java.lang.annotation.*; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) @Inherited public @interface MyCache { String value() default ""; }
定義切面spring
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項目express
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",結果以下springboot