Spel + AOP动态赋值

1、注解Annotation

/**
 * created by guanjian on 2020/11/30 19:58
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Spel {

    /**
     * 参数列表  --- 解析map类型
     */
    String variables() default "";

    /**
     * 业务ID  --- 解析字符串类型
     */
    String bizId() default "";

    /**
     * 操作类型  --- 枚举类型
     */
    Operation operation();
}

2、切面Aspect

/**
 * created by guanjian on 2020/11/30 20:00
 */
@Aspect
@Component("spelAspect")
public class SpelAspect {

    /**
     * spEl parser
     */
    private final static ExpressionParser parser = new SpelExpressionParser();
    /**
     * Param discover
     */
    private final static ParameterNameDiscoverer discoverer = new LocalVariableTableParameterNameDiscoverer();

    @Around("@annotation(com.github.java.learning.spring.spel.annotation.Spel)")
    public Object around(ProceedingJoinPoint pjp) {

        /**
         * 解析MAP类型
         */
        String variables = getAnnotation(pjp).variables();
        System.out.println("variables=  " + variables);
        Map<String, Object> map = (Map) parser.parseExpression(variables).getValue();
        //1、解析map {name:'#req.name',age:'#req.age'}
        System.out.println("map= " + map);
        //2、获取到方法形参
        String[] params = discoverer.getParameterNames(getMethod(pjp));
        System.out.println("形参param= " + JSON.toJSONString(params));
        //3、获取到方法实参
        Object[] args = getArgs(pjp);
        System.out.println("实参args= " + JSON.toJSONString(args));
        //4、构建spel的context
        EvaluationContext context = new StandardEvaluationContext();

        for (int index = 0; index < params.length; index++) {
            //匹配形参、实参的对应关系注入到context环境中
            context.setVariable(params[index], args[index]);
        }
        //5、遍历map={name=#req.name, age=#req.age},把对应的value的形参赋值成实参值
        map.forEach((k, spel) -> {
            String value = parser.parseExpression(String.valueOf(spel)).getValue(context, String.class);
            System.out.format("key=%s,spel=%s,value=%s\n", k, spel, value);
        });

        /**
         * 解析String类型
         */
        String bizId = getAnnotation(pjp).bizId();
        System.out.println("bizId=  " + parser.parseExpression(bizId).getValue(context, String.class));

        //target method execute...
        Object result = null;
        try {
            result = pjp.proceed();
        } catch (Throwable e) {
            e.getStackTrace();
        } finally {
            return result;
        }
    }

    protected Method getMethod(ProceedingJoinPoint pjp) {
        Method method = null;
        try {
            MethodSignature ms = (MethodSignature) pjp.getSignature();
            method = pjp.getTarget()
                    .getClass()
                    .getMethod(ms.getName(), ms.getParameterTypes());
        } catch (NoSuchMethodException e) {
            //ignore
        }
        return method;
    }

    protected Object[] getArgs(ProceedingJoinPoint pjp) {
        return pjp.getArgs();
    }

    protected static Spel getAnnotation(ProceedingJoinPoint pjp) {
        Annotation annotation = null;
        try {
            MethodSignature ms = (MethodSignature) pjp.getSignature();
            annotation = pjp.getTarget()
                    .getClass()
                    .getMethod(ms.getName(), ms.getParameterTypes())
                    .getAnnotation(Spel.class);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return (Spel) annotation;
    }
}

3、业务类Service

/**
 * created by guanjian on 2020/11/30 20:13
 */
@Component("spelService")
public class SpelService {

    @Spel(
            variables = "{name:'#req.name',age:'#req.age'}",
            bizId = "#req.bizId",
            operation = Operation.START

    )
    public Object test(User req) {
        return new Object();
    }
}

4、测试类Test

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/spring-config.xml")
public class SpelTest{

    @Autowired
    private SpelService spelService;

    @Test
    public void test() {
        User user = new User("zhangsan");
        user.setAge(10);
        user.setBizId("12312212521512");
        spelService.test(user);
    }
}

控制台打印输出

variables=  {name:'#req.name',age:'#req.age'}
map= {name=#req.name, age=#req.age}
形参param= ["req"]
实参args= [{"age":10,"bizId":"12312212521512","name":"zhangsan"}]
key=name,spel=#req.name,value=zhangsan
key=age,spel=#req.age,value=10
bizId=  12312212521512

5、用法总结

Aspect不支持Map、List等常用的数据结构做传参,但实际业务场景中需要使用的话需要做些变通。基于Spel提供的强大解析功能能够非常方便的拆解Map、List等,通过ParameterNameDiscoverer和Aop进行形参、实参解析和匹配,根据配置的Spel表达式进行解析,最终达到动态赋值的目的。

6、参考

https://blog.csdn.net/shichen2010/article/details/96504008

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大摩羯先生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值