Spring AOP不生效的原因(切入点表达式错误)

本文讲述了项目中AOP模块不生效的案例,涉及接口权限检查,通过修正切入点表达式解决了问题,提供了解决此类问题的步骤和技巧。
摘要由CSDN通过智能技术生成

Spring AOP不生效的原因(切入点表达式错误)

背景

接手了一个写了一半的项目,发现其中的AOP模块不生效,着手开始修改

要实现的功能是在接口调用前判断该接口是否在时间线之内,不是的话则抛出异常从而不执行接口

代码

注解类

package com.neu.annotion;

import java.lang.annotation.*;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface TimeAvailableLimit {

    /**
     * 接口可用的阶段
     */
    int[] availableStage();
}

切面类

package com.neu.config.aop;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.neu.annotion.TimeAvailableLimit;
import com.neu.config.exception.BizException;
import com.neu.entity.Timeline;
import com.neu.mapper.TimelineMapper;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.lang.reflect.Method;

/**
 * @Description:
 * 附议,取消附议:1
 * 反馈:6
 * 合并,修改合并后的提案,拟立案:3
 * 创建提案,提交提案:0
 */

@Aspect
@Component
public class TimeAvailableAspect {

    @Resource
    TimelineMapper timelineMapper;

    private static final int NOT_STARTED = 0;
    private static final int ON_GOING = 1;
    private static final int FINISHED = 2;

    @Before("@annotation(timeAvailableLimit) && args(sessionId, ..)")
    public void checkTimeAvailable(JoinPoint joinPoint, TimeAvailableLimit timeAvailableLimit, String sessionId){
        int[] availableStage = timeAvailableLimit.availableStage();
        // 反射获取参数sessionId
        String[] parameterNames = getParameterNames(joinPoint);
        String sessionIdValue = findSessionId(joinPoint, parameterNames);
        if(sessionIdValue==null){
            throw new BizException("获取session参数失败");
        }
        Timeline timeline = timelineMapper.selectOne(new LambdaQueryWrapper<Timeline>().eq(Timeline::getSessionId, sessionIdValue));
        if(timeline==null){
            throw new BizException("当前届次时间线并未创建,请联系管理员创建时间线");
        }

        for (int oneAvailableStage : availableStage) {
            switch (oneAvailableStage){
                case 0:
                    if(timeline.getProposalStatus()!=ON_GOING){
                        throw new BizException("此功能当前阶段未开放");
                    }
                    break;
                case 1:
                    if(timeline.getApprovalStatus()!=ON_GOING){
                        throw new BizException("此功能当前阶段未开放");
                    }
                    break;
                case 2:
                    if(timeline.getInitialReviewStatus()!=ON_GOING){
                        throw new BizException("此功能当前阶段未开放");
                    }
                    break;
                case 3:
                    if(timeline.getProvisionalCaseStatus()!=ON_GOING){
                        throw new BizException("此功能当前阶段未开放");
                    }
                    break;
                case 4:
                    if(timeline.getFormalCaseStatus()!=ON_GOING){
                        throw new BizException("此功能当前阶段未开放");
                    }
                    break;
                case 5:
                    if(timeline.getUndertakeStatus()!=ON_GOING){
                        throw new BizException("此功能当前阶段未开放");
                    }
                    break;
                case 6:
                    if(timeline.getFeedbackStatus()!=ON_GOING){
                        throw new BizException("此功能当前阶段未开放");
                    }
                    break;
                default:
                    return;
            }
        }

        System.out.println("aop调用成功");
    }

    private String[] getParameterNames(JoinPoint joinPoint) {
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        LocalVariableTableParameterNameDiscoverer discoverer = new LocalVariableTableParameterNameDiscoverer();
        return discoverer.getParameterNames(method);
    }

    private String findSessionId(JoinPoint joinPoint, String[] parameterNames) {
        Object[] args = joinPoint.getArgs();
        for (int i = 0; i < args.length; i++) {
            if ("sessionId".equals(parameterNames[i])) {
                return (String) args[i];
            }
        }
        return null;
    }

}

接口

这里我有两个接口,方法名一样,但是参数不一样

    @PostMapping("/createProposal")
    @ApiOperation(value = "创建并提交一个提案", notes = "")
    @TimeAvailableLimit(availableStage = 0)
    public Response createProposal(@Valid @NotEmpty @RequestBody CreateProposalDto createProposalDto,@Valid @NotEmpty @RequestParam String sessionId) {
        return proposalService.createProposal(createProposalDto);
    }

    @PostMapping("/submitProposal")
    @ApiOperation(value = "使得一个草稿提案成为正式提案", notes = "")
    @TimeAvailableLimit(availableStage = 0)
    public Response createProposal(@Valid @NotEmpty @RequestParam String proposalId,@Valid @NotEmpty @RequestParam String sessionId) {
        return proposalService.submitProposal(proposalId);
    }

问题

我调用两个接口,调用createProposalaop直接不发挥作用,没有运行,调用submitProposal时会抛出异常,但是接口还是正常执行了

网上的解决方式

  1. 检查pom.xml类中是否依赖。

  2. 检查启动类中是否有这两个注解

    @ComponentScan("com.neu.config.aop")
    @EnableAspectJAutoProxy(proxyTargetClass = true)
    
  3. 检查AOP类中除了@Aspect注解还应该有@Component注解。(这里@Component用于开启组件扫描,使得SpringBoot可以找到它。即交由SpringBoot管理。)

  4. 检查AOP版本是否过老。

  5. 检查AOP类是否和启动类在同一等级。如果不在,应该使用2的方法进行指定。(这个方法和2有点重复)

  6. idea版本问题【特殊情况】SpringBoot的@Aspect注解不起作用的问题_@aspect不生效-CSDN博客

以上方法和我的问题并不一样,小伙伴们可以参考

下面分析我的问题

问题分析

排查了半天,发现是aop类的切入点表达式写错了

@Before("@annotation(timeAvailableLimit) && args(sessionId, ..)")

这行代码中,args表达式是基于参数的类型进行匹配的,而不是参数的名字。因此,args(sessionId, ..)这样的表达式实际上不会尝试匹配名为sessionId的参数;而是匹配第一个参数类型和sessionId变量类型相同,且后面可以跟任意类型参数的方法。

即:参数名不是匹配依据

因此将&& args(sessionId, ..)删掉,或者改为args( .. , String)都可以解决

切入点表达式

最后在扩展一下切入点表达式的写法

  1. 执行方法的表达式:
    • execution(* com.example.service.*.*(..)): 匹配com.example.service包下所有类的所有方法。这里*表示匹配所有的返回类型,..表示匹配任意参数列表。
  2. 注解:
    • @annotation(com.example.MyAnnotation): 匹配所有被com.example.MyAnnotation注解标记的方法。
  3. 参数:
    • args(java.io.Serializable): 匹配任何接受一个实现了java.io.Serializable接口的参数的方法。
    • args(.., com.example.MyType): 匹配方法的最后一个参数是com.example.MyType类型的方法。
  4. 在Bean上:
    • bean(myBean): 匹配Spring上下文中名为myBean的Bean。
  5. 指定包或类型:
    • within(com.example.service.*): 匹配指定包下所有类的方法。
    • within(com.example.service..*): 匹配指定包及其子包下所有类的方法。
    • within(com.example.service.MyService+): 匹配MyService接口或类及其子类的所有方法。
  6. 代理对象的类型:
    • this(com.example.service.MyInterface): 匹配实现了MyInterface接口的Spring Bean的所有方法。
    • target(com.example.service.MyClass): 匹配目标类为MyClass的所有方法。
  7. 组合使用:
    • 你还可以使用&&||!来组合以上元素,实现更复杂的切入点表达式。
  • 14
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值