springboot AOP中,通过解析SpEL 表达式动态获取参数值

切面注解

bash 复制代码
import com.bn.document.constants.FmDeptCatalogueConstants;

import java.lang.annotation.*;

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

    /**
     * 权限类型
     */
    FmDeptCatalogueConstants value();

    /**
     * 目录id
     */
    String catalogueId() ;
}

切面类 (整个类) 重点代码在下面

bash 复制代码
package com.bn.document.aop;

import com.bn.document.constants.FmDeptCatalogueConstants;
import com.bn.document.po.FmDeptCataloguePo;
import com.bn.document.po.LoginUser;
import com.bn.document.service.FmDeptCatalogueService;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
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.reflect.Method;
import java.util.List;

@Aspect
@Component
public class MyAspect {

    private final ExpressionParser parser = new SpelExpressionParser();
    private final ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();

    @Autowired
    private FmDeptCatalogueService fmDeptCatalogueService;

    /**
     * 定义切入点:拦截带有 @MyCustomAnnotation 的方法
     */
    @Pointcut("@annotation(com.bn.document.aop.FmDeptCatalogueAopAnnotation)")
    public void cataloguePointcut() {}

    /**
     * 环绕增强:获取注解中的权限类型,检查用户权限
     */
    @Around("cataloguePointcut()")
    public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
        // 获取方法上的注解
        FmDeptCatalogueAopAnnotation annotation = ((MethodSignature) joinPoint.getSignature())
                .getMethod()
                .getAnnotation(FmDeptCatalogueAopAnnotation.class);

        if (annotation == null) {
            return joinPoint.proceed(); // 没有注解直接放行
        }

        // 获取权限类型
        FmDeptCatalogueConstants permissionType = annotation.value();

        // 获取方法参数值
        Object[] args = joinPoint.getArgs();
        LoginUser loginUser = null;

        // 遍历参数列表,提取 LoginUser 和 catalogueId
        for (Object arg : args) {
            if (arg instanceof LoginUser) {
                loginUser = (LoginUser) arg;
            }
        }

        // 开始解析  SpEL 表达式
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        // 获取方法参数名 + 参数值,用于构建 SpEL 上下文
        String[] paramNames = parameterNameDiscoverer.getParameterNames(method);

        // 构建 EvaluationContext,并绑定参数
        EvaluationContext context = new StandardEvaluationContext();
        if (paramNames != null && args.length > 0) {
            for (int i = 0; i < paramNames.length; i++) {
                context.setVariable(paramNames[i], args[i]); // 绑定参数
            }
        }
        // 解析表达式中的目录ID
        String catalogueIdExpr = annotation.catalogueId();
        Long catalogueId = parser.parseExpression(catalogueIdExpr).getValue(context, Long.class);

        // 判断参数是否为空
        if (loginUser == null || loginUser.getDeptId() == null) {
            throw new SecurityException("登录信息错误");
        }

        if (catalogueId == null || catalogueId <= 0) {
            throw new SecurityException("目录ID不能为空");
        }

        // 调用服务层查询权限
        List<FmDeptCataloguePo> result = fmDeptCatalogueService.getFmDeptCataloguePoWithDeptId(
                loginUser.getDeptId(),
                catalogueId,
                permissionType
        );

        // 如果权限不存在
        if (result == null || result.isEmpty()) {
            throw new SecurityException("您没有【" + permissionType.getDes() + "】权限");
        }

        // 否则放行
        return joinPoint.proceed();
    }


}

重点【开始解析 SpEL 表达式】

bash 复制代码
// 开始解析  SpEL 表达式
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        // 获取方法参数名 + 参数值,用于构建 SpEL 上下文
        String[] paramNames = parameterNameDiscoverer.getParameterNames(method);

        // 构建 EvaluationContext,并绑定参数
        EvaluationContext context = new StandardEvaluationContext();
        if (paramNames != null && args.length > 0) {
            for (int i = 0; i < paramNames.length; i++) {
                context.setVariable(paramNames[i], args[i]); // 绑定参数
            }
        }
        // 解析表达式中的目录ID
        String catalogueIdExpr = annotation.catalogueId();
        Long catalogueId = parser.parseExpression(catalogueIdExpr).getValue(context, Long.class);

使用

1.获取对象中的属性值

bash 复制代码
 @Override
    @Transactional(rollbackFor = Exception.class)
    @FmDeptCatalogueAopAnnotation(value = FmDeptCatalogueConstants.MODIFY_PERMISSION, catalogueId = "#sysFileCataloguePo.parentId")
    public CommonResponse insertSub(SysFileCataloguePo sysFileCataloguePo,LoginUser loginUser) {
}

1.1 结果

2.直接取值

bash 复制代码
 @Override
    @FmDeptCatalogueAopAnnotation(value = FmDeptCatalogueConstants.DETAILS_PERMISSION, catalogueId = "#id")
    public CommonResponse getSub(Long id,  LoginUser loginUser) {
       
    }

2.1 结果

完结!!!

相关推荐
tachibana231 分钟前
使用for range 的时候,它的地址会发生变化吗?
开发语言·后端·golang·go·语法
AIGC小尼32 分钟前
毕设实战|SpringBoot+Vue 实现多个系统项目
vue.js·spring boot·课程设计
蜗牛互联网37 分钟前
GPT-6 与 Intelligent UI:从回答到可审计操作的产品迁移
java·人工智能·后端
王中阳Go1 小时前
Go 后端转 AI:非科班怎么补?一张 3 列路线表(可保存)
开发语言·人工智能·后端·python·golang·大语言模型
IT_陈寒1 小时前
Vite热更新失效?你可能也踩了这个坑
前端·人工智能·后端
SL_staff1 小时前
运维救火实录:钉钉考勤API变更引发工资计算异常,我们用JVS-Logic可视化逻辑引擎实现分钟级热修复
java·mysql·全栈
SunnyDays10111 小时前
Java 查找并高亮 Word 文字:精确查找及正则表达式模糊查找
java·查找文字·word 文档·高亮文字·正则匹配查找·模糊查找
geats人山人海2 小时前
java 4继承
java·开发语言
行百里er2 小时前
Redis 模块——JSON、向量搜索与时序
redis·后端
光依旧2 小时前
发版就掉请求?Spring Boot优雅停机的四步
运维·spring boot·nginx·高可用·优雅停机·mcp server