自定义注解使用SPEL表达式来动态获取方法传参

1,082 阅读1分钟

要实现的是 动态入参,也就是在使用自定义注解的时候 ,写入的是字符串,并非一个传入参数值

1、方法是自定义注解传参

    @DataAuth(spaceName = "#space")
    @PostMapping("/list")
    @ApiOperation("卡片展示列表(图谱详情)")
    public R<List<DetailSpace>> detailSpace(@RequestBody GraphShowAttribute graphShowAttribute, @PathVariable String space) {
        return R.data(spaceService.detailSpace(graphShowAttribute));
    }

@DataAuth(spaceName = "#space")
这里的spaceName 并非一个固定字符串,而是一个路径参数,每次接口参数变化,我都想将这个参数传入自定义的注解中用以校验;


如果是单个参数,@DataAuth(spaceName = "#space")
如果是对象参数,@DataAuth(spaceName = "#graphShowAttribute.space")

自定义注解如下:

/**
 * @ClassName: DataAuth
 * 数据权限校验切面日志
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DataAuth {

    String spaceName();
}


注解切面实现:

使用 SpelExpressionParser 和DefaultParameterNameDiscoverer 来动态获取参数


import cn.dev33.satoken.stp.StpUtil;
import com.hotemasoft.knowledge.manage.exception.GraphExecuteException;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
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 org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * @ClassName: DataAuthAspect
 * 数据权限切面实现
 */
@Aspect
@Component
@Slf4j
public class DataAuthAspect implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @Pointcut("@annotation(com.hotemasoft.knowledge.manage.config.DataAuth)")
    public void dataAuthMethod() {

    }

    @SneakyThrows
    @Around(value = "dataAuthMethod()")
    public Object around(ProceedingJoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();
        log.info("拦截的方法:{}", method);
        DataAuth dataAuth = method.getAnnotation(DataAuth.class);
        log.info("入参:{}", dataAuth.spaceName());
        String value=  generateKeyBySpEL(dataAuth.spaceName(), joinPoint);
        log.info("解析入参:{}", value);
        if (!StpUtil.getRoleList().contains(value)) {
            throw new GraphExecuteException("无数据权限");
        }
        return joinPoint.proceed();
    }

    private SpelExpressionParser parserSpel = new SpelExpressionParser();
    private DefaultParameterNameDiscoverer parameterNameDiscoverer= new DefaultParameterNameDiscoverer();

    public String generateKeyBySpEL(String key, ProceedingJoinPoint pjp) {
        Expression expression = parserSpel .parseExpression(key);
        EvaluationContext context = new StandardEvaluationContext();
        MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
        Object[] args = pjp.getArgs();
        String[] paramNames = parameterNameDiscoverer.getParameterNames(methodSignature.getMethod());
        for(int i = 0 ; i < args.length ; i++) {
            context.setVariable(paramNames[i], args[i]);
        }
        return expression.getValue(context).toString();
    }

}