Spring-AOP根据spel获取方法参数值、Bean对象属性值
Spring-AOP根据spel获取方法参数值、Bean对象属性值,动态的获取属性值,可以用来做注解式分布式锁、注解式获取属性值等等。
第一步:自定义注解,代码如下所示
package com.example.springbootstudy.interfaces; import java.lang.annotation.*; @Target(ElementType.METHOD) @Inherited @Documented @Retention(RetentionPolicy.RUNTIME) /** * 自定义注解,在注解上的方法将执行代理流程 */ public @interface MyAnnotation { String key(); }
第二步:方法上添加注解,代码如下所示:
package com.example.springbootstudy.service; import com.example.springbootstudy.config.BizException; import com.example.springbootstudy.config.CommonEnum; import com.example.springbootstudy.entity.Person; import com.example.springbootstudy.interfaces.MyAnnotation; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @Slf4j @Service public class TestService { public void print() { throw new BizException(CommonEnum.SERVER_BUSY); } /** * 添加自定义aop注解 #id为el表达式,需要和被替换的参数名称相同 * 获取参数值 * * @param id * @param person */ @MyAnnotation(key = "'param id is ' + #id") public void doSome(String id, Person person) { System.out.println("方法执行中"); } /** * 添加自定义aop注解 #id为el表达式,需要和被替换的参数名称相同 * 获取bean对象属性值 * * @param id * @param person */ @MyAnnotation(key = "'param id is ' + #person.name") public void doSome1(String id, Person person) { System.out.println("方法执行中"); } }
第三步:添加AOP,代码如下所示:
package com.example.springbootstudy.interceptors; import com.example.springbootstudy.interfaces.MyAnnotation; 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.DefaultParameterNameDiscoverer; import org.springframework.core.LocalVariableTableParameterNameDiscoverer; import org.springframework.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; /** * @Description TODO * @Author Jelly * @Date 2023/10/8 15:56 */ @Component @Aspect public class MyAspect { @Pointcut("@annotation(com.example.springbootstudy.interfaces.MyAnnotation)") public void pointCut() { } @Around("pointCut()") public Object dosome(ProceedingJoinPoint joinPoint) throws Throwable { //获取方法签名 MethodSignature signature = (MethodSignature) joinPoint.getSignature(); //获取切入方法的对象 Method method = signature.getMethod(); //获取方法上的Aop注解 MyAnnotation annotation = method.getAnnotation(MyAnnotation.class); //获取注解上的值如 : @MyAnnotation(key = "'param id is ' + #id") String keyEl = annotation.key(); //将注解的值中的El表达式部分进行替换 //创建解析器 SpelExpressionParser parser = new SpelExpressionParser(); //获取表达式 Expression expression = parser.parseExpression(keyEl); //设置解析上下文(有哪些占位符,以及每种占位符的值) EvaluationContext context = new StandardEvaluationContext(); //获取参数值 Object[] args = joinPoint.getArgs(); //获取运行时参数的名称 DefaultParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer(); String[] parameterNames = discoverer.getParameterNames(method); for (int i = 0; i < parameterNames.length; i++) { context.setVariable(parameterNames[i], args[i].toString()); } //解析,获取替换后的结果 try { String result = expression.getValue(context).toString(); System.out.println(result); } catch (EvaluationException e) { e.printStackTrace(); } test(joinPoint); return null; } /** * 获取bean对象属性值 * * @param point */ private void test(ProceedingJoinPoint point) { //获取方法签名 MethodSignature signature = (MethodSignature) point.getSignature(); //获取切入方法的对象 Method method = signature.getMethod(); //获取方法上的Aop注解 MyAnnotation annotation = method.getAnnotation(MyAnnotation.class); //获取注解上的值如 : @MyAnnotation(key = "'param id is ' + #id") String keyEl = annotation.key(); EvaluationContext context = getContext(point.getArgs(), signature.getMethod()); String value = getValue(context, keyEl, String.class); System.out.println(value); } /** * 获取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 IllegalArgumentException("参数列表不能为null"); } EvaluationContext context = new StandardEvaluationContext(); for (int i = 0; i < arguments.length; i++) { context.setVariable(parameterNames[i], arguments[i]); } return context; } }
总结:spring根据spel表达式动态的获取参数值,动态的获取bean属性值,使用场景主要是在注解式REDIS分布式锁种,注解式获取bean对象值等等场景。
郭慕荣博客园