spel简单使用&基于注解的AOP使用SPEL获取参数的值

  简单的场景是基于接口层面对数据实现权限校验。

1. Spel 简单使用

package com.example.demo;

import lombok.Builder;
import lombok.Data;
import org.assertj.core.util.Lists;
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 java.util.Date;
import java.util.List;

/**
 * https://docs.spring.io/spring-framework/docs/4.2.x/spring-framework-reference/html/expressions.html
 */
public class SpelTest {

    public static void main(String[] args) {
        UserWrapper zs = UserWrapper.builder()
                .age(22).birthDay(new Date()).username("zs")
                .likes(Lists.newArrayList("篮球", "羽毛球"))
                .build();
        System.out.println(zs);

        // 1. 创建解析器
        ExpressionParser parser = new SpelExpressionParser();
        // 2. 设置上下文
        EvaluationContext context = new StandardEvaluationContext();
        context.setVariable("zs", zs);
        context.setVariable("zs2", null);
        // 3. 创建表达式并且解析
        // 3.1 解析username
        System.out.println("========0");
        Expression expression = parser.parseExpression("#zs.username");
        String name = (String) expression.getValue(context);
        System.out.println(name);
        String value = parser.parseExpression("#zs.username + '111222'").getValue(context, String.class);
        System.out.println(value);
        // 3.2 解析likes 爱好
        System.out.println("========1");
        System.out.println(parser.parseExpression("#zs.likes").getValue(context));
        System.out.println(parser.parseExpression("#zs.likes").getValueType(context));
        // spel 调用方法
        System.out.println(parser.parseExpression("#zs.likes.size()").getValue(context));
        System.out.println(parser.parseExpression("#zs.likes.size() == 2").getValue(context));
        System.out.println(parser.parseExpression("#zs.likes.get(1)").getValue(context));
        // spel 调用方法添加元素
        System.out.println(parser.parseExpression("#zs.likes.add('新爱好')").getValue(context));
        System.out.println(zs.getLikes());
        // 3. 解析age 年龄
        System.out.println("========1");
        Integer age = parser.parseExpression("#zs.age").getValue(context, int.class);
        System.out.println(age);
        // 4. 解析在上下文环境中,值为null的获取的值为null; 不存在上下文中、或者解析值为null的对象的属性会报 SpelEvaluationException 错误
//        Object value1 = parser.parseExpression("#zs2.age").getValue(context);
//        Object value2 = parser.parseExpression("#zs.age2").getValue(context);
//        System.out.println(value1);

        /**
         * UserWrapper(username=zs, age=22, birthDay=Fri Feb 10 11:01:56 CST 2023, likes=[篮球, 羽毛球])
         * ========0
         * zs
         * zs111222
         * ========1
         * [篮球, 羽毛球]
         * class java.util.ArrayList
         * 2
         * true
         * 羽毛球
         * true
         * [篮球, 羽毛球, 新爱好]
         * ========1
         * 22
         */
    }
}

@Data
@Builder
class UserWrapper {

    private String username;

    private int age;

    private Date birthDay;

    private List<String> likes;
} 

2. AOP基于Spel 获取参数值

1. 注解

package com.example.demo.aop;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AopAnnotation {

    /**
     * id 表达式
     * @return
     */
    String idSpel() default "";

    /**
     * 值
     * @return
     */
    String value();
}

2. 切面Aspect

package com.example.demo.aop;

import cn.hutool.core.util.ArrayUtil;
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.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Arrays;

@Component
@Aspect
public class AopAspect {

    // 定义一个空方法,借用其注解抽取切点表达式
    @Pointcut("@annotation(com.example.demo.aop.AopAnnotation)")
    public void pc() {
    }

    // 环绕通知
    @Around("AopAspect.pc()")
    public Object around(ProceedingJoinPoint pjp) {
        System.out.println("----------------环绕通知之前 的部分(相当于前置通知)----------------");
        // 获取到类名
        String targetName = pjp.getTarget().getClass().getName();
        System.out.println("代理的类是:" + targetName);
        // 获取到参数
        Object[] parameter = pjp.getArgs();
        System.out.println("传入的参数是:" + Arrays.toString(parameter));
        // 获取到方法签名,进而获得方法
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        Method method = signature.getMethod();
        System.out.println("增强的方法名字是:" + method.getName());

        // 处理一些业务逻辑
        // 模拟简单的获取参数
        AopAnnotation annotation = method.getAnnotation(AopAnnotation.class);
        String value = annotation.value();
        System.out.println("注解参数值: value\t" + value);
        // spel 解析参数
        Parameter[] parameters = method.getParameters();
        String idSpel = annotation.idSpel();
        if (ArrayUtil.isNotEmpty(parameters)) {
            ExpressionParser parser = new SpelExpressionParser();
            EvaluationContext context = new StandardEvaluationContext();
            for (int index = 0, length_1 = parameters.length; index < length_1; index++) {
                String paramterName = parameters[index].getName();
                Object paramterValue = parameter[index];
                context.setVariable(paramterName, paramterValue);
            }
            String value1 = parser.parseExpression(idSpel).getValue(context, String.class);
            System.out.println(String.format("idSpel: %s, value: %s", idSpel, value1));
        } else {
            System.out.println("idSpel 参数获取不到上下文环境, idSpel: " + idSpel);
        }

        // 让方法执行
        Object proceed = null;
        try {
            proceed = pjp.proceed();
            // 环绕通知之后的业务逻辑部分
            System.out.println("----------------环绕通知之后的部分(相当于后置通知AfterReturning)-----------------");
        } catch (Throwable e) {
            System.out.println("-------------环绕通知的异常部分(相当于异常通知AfterThrowing)--------------------------");
            e.printStackTrace();
        } finally {
            System.out.println("-------------环绕通知的最终部分部分(相当于最终通知After)--------------------------");
        }

        return proceed;
    }

}

3. 测试Controller

package com.example.demo.aop;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/aop")
public class AopTestController {

    @GetMapping("/")
    @AopAnnotation(idSpel = "#id", value = "test")
    public String test(String id) {
        return "111222";
    }
}

4. 访问: http://localhost:8088/aop/?id=3

5. 控制台

----------------环绕通知之前 的部分(相当于前置通知)----------------
代理的类是:com.example.demo.aop.AopTestController
传入的参数是:[3]
增强的方法名字是:test
注解参数值: value    test
idSpel: #id, value: 3
----------------环绕通知之后的部分(相当于后置通知AfterReturning)-----------------
-------------环绕通知的最终部分部分(相当于最终通知After)--------------------------

 

posted @ 2023-02-13 22:40  QiaoZhi  阅读(1039)  评论(0编辑  收藏  举报