前言
在Spring Aop中,我們可以拿到攔截方法的引數,如果能結合spel表示式,就能實現更加靈活的功能。典型的實現有Spring的快取註解:
@Cacheable(value = "user", key = "#id", condition = "#id lt 10")
public User conditionFindById(final Long id) {
}
複製程式碼
@Caching(put = {
@CachePut(value = "user", key = "#user.id"),
@CachePut(value = "user", key = "#user.username"),
@CachePut(value = "user", key = "#user.email")
})
public User save(User user) {
複製程式碼
本文介紹如何在aop程式設計中解析spel表示式,提供幾個通用的方法。
Spring使用自定義註解實現aop的方式這裡就不贅述,只著重介紹如何解析spel。
準備
實現非常簡單,Spring本身就提供了簡便的api,我們只需要獲取:
- 方法:
Method method
- 方法引數:
Object[] arguments
- spel表示式:
String spel
這些都能從aop入口方法的引數ProceedingJoinPoint
中得到。
spel表示式顯然就是從自定義註解中獲取了,而獲取方法和引數的方式如下:
獲取方法:
private Method getMethod(ProceedingJoinPoint joinPoint) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
if (method.getDeclaringClass().isInterface()) {
try {
method = joinPoint
.getTarget()
.getClass()
.getDeclaredMethod(joinPoint.getSignature().getName(),
method.getParameterTypes());
} catch (SecurityException | NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
return method;
}
複製程式碼
獲取方法引數值:
Object[] arguments = joinPoint.getArgs();
複製程式碼
解析
然後就是解析spel表示式,首先在aop類中定義兩個屬性:
private ExpressionParser parser = new SpelExpressionParser();
private LocalVariableTableParameterNameDiscoverer discoverer = new LocalVariableTableParameterNameDiscoverer();
複製程式碼
根據spel表示式解析引數,得到結果:
/**
* 解析 spel 表示式
*
* @param method 方法
* @param arguments 引數
* @param spel 表示式
* @param clazz 返回結果的型別
* @param defaultResult 預設結果
* @return 執行spel表示式後的結果
*/
private <T> T parseSpel(Method method, Object[] arguments, String spel, Class<T> clazz, T defaultResult) {
String[] params = discoverer.getParameterNames(method);
EvaluationContext context = new StandardEvaluationContext();
for (int len = 0; len < params.length; len++) {
context.setVariable(params[len], arguments[len]);
}
try {
Expression expression = parser.parseExpression(spel);
return expression.getValue(context, clazz);
} catch (Exception e) {
return defaultResult;
}
}
複製程式碼
總結
上述就是整個解析spel表示式的關鍵流程,整體來看,aop類的結構是這樣的:
@Aspect
public class SpelAspect {
private ExpressionParser parser = new SpelExpressionParser();
private LocalVariableTableParameterNameDiscoverer discoverer = new LocalVariableTableParameterNameDiscoverer();
@Around(value = "@annotation(自定義註解)")
public Object test(ProceedingJoinPoint point) throws Throwable {
Object obj;
// 獲取方法引數值
Object[] arguments = point.getArgs();
// 獲取方法
Method method = getMethod(point);
// 從註解中獲取spel字串,省略...
String spel = ...
// 解析spel表示式
Boolean result = parseSpel(method, arguments, spel, Boolean.class, Boolean.FALSE);
// 業務操作,省略...
...
return point.proceed();
}
}
複製程式碼
以上提供一個基本思路和幾個通用的方法(#getMethod
、#parseSpel
),接下來就是大家發揮想象力的時間啦!