Springboot - AOP 中使用spel 表达式解析
1、什么是SpEL
参考:
2、引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
3、开启AOP ,注册组件
/**
* @author Created by 谭健 on 2019/11/4. 星期一. 10:39.
* © All Rights Reserved.
*/
@Configuration
@EnableAspectJAutoProxy
public class AopConfiguration {
}
@Aspect
@Slf4j
@Component
public class SpringElAop {
}
4、构建一个切入注解
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target(ElementType.METHOD)
public @interface SpelLog {
String value() default "";
}
5、编写切面
import com.web.bottom.exception.BusinessException;
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.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.concurrent.Executor;
/**
* @author Created by 谭健 on 2019/11/4. 星期一. 15:22.
* © All Rights Reserved.
*/
@Aspect
@Slf4j
@Component
public class SpringElAop {
private final Executor executor;
public SpringElAop(Executor executor) {
this.executor = executor;
}
@Pointcut("@annotation(com.web.framework.aop.SpelLog)")
public void springElAop() {
}
@Around("springElAop()")
public Object ar(ProceedingJoinPoint point) throws Throwable {
MethodSignature signature = (MethodSignature) point.getSignature();
Method signatureMethod = signature.getMethod();
SpelLog spelLog = signatureMethod.getAnnotation(SpelLog.class);
executor.execute(() -> {
if (spelLog != null) {
EvaluationContext context = getContext(point.getArgs(), signature.getMethod());
String value = getValue(context, spelLog.value(), String.class);
if (log.isInfoEnabled()) {
log.info(" SpelLog {}", value);
}
}
});
return point.proceed();
}
/**
* 获取spel 定义的参数值
*
* @param context 参数容器
* @param key key
* @param clazz 需要返回的类型
* @param <T> 返回泛型
* @return 参数值
*/
private <T> T getValue(EvaluationContext context, String key, Class<T> clazz) {
SpelExpressionParser spelExpressionParser = new SpelExpressionParser();
Expression expression = spelExpressionParser.parseExpression(key);
return expression.getValue(context, clazz);
}
/**
* 获取参数容器
*
* @param arguments 方法的参数列表
* @param signatureMethod 被执行的方法体
* @return 装载参数的容器
*/
private EvaluationContext getContext(Object[] arguments, Method signatureMethod) {
String[] parameterNames = new LocalVariableTableParameterNameDiscoverer().getParameterNames(signatureMethod);
if (parameterNames == null) {
throw new BusinessException("参数列表不能为null");
}
EvaluationContext context = new StandardEvaluationContext();
for (int i = 0; i < arguments.length; i++) {
context.setVariable(parameterNames[i], arguments[i]);
}
return context;
}
}
5、简单示例使用
@SpelLog("'会员'+#user.memberId+'从账本'+#user.accountId+'切换到'+#newAccountId")
public void changeDefaultAccount(Long newAccountId, User user) {
}
这样就可以愉快的使用参数值而不用写入到业务代码里面去了