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 结果

完结!!!

相关推荐
猪猪拆迁队10 分钟前
虚拟工厂仿真引擎的架构设计:让一条产线可编程、可观测、可干预
后端·ai编程
字节跳动数据库34 分钟前
文章分享——相似函数处理方法
人工智能·后端·程序员
云技纵横34 分钟前
@Transactional 失效的 7 种场景:第 5 种最难排查
后端
用户6757049885021 小时前
你知道 Go 结构体和结构体指针调用的区别吗?一文带你彻底搞懂!
后端·go
程序员cxuan1 小时前
读懂 Claude Code 架构分析系列,第一篇,开始!
人工智能·后端·架构
用户6757049885021 小时前
面试官问“装饰器模式”,这样回答薪资多要 3000!
后端
tntxia1 小时前
Geo Scene域名修改引起的一些问题
后端
用户298698530141 小时前
Java 实现 Word 文档加密与权限解除
java·后端
vanuan2 小时前
给你的A2A-Agent加把锁-认证鉴权实战指南
后端
Yeats_Liao2 小时前
14:Servlet中的页面跳转-Java Web
java·后端·架构