You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring AOP切面未生效:IDE提示‘该通知未匹配任何方法’排查

Spring AOP切面未生效排查:@ManageRequestRate注解匹配失败

我正在完成入门测试任务,用Spring AOP实现请求限流功能,但切面完全未生效。IDE提示「This advice advises no methods」,集成测试也看不到切面执行的日志(比如requestRateAdvice里的打印语句没有输出)。我的需求是匹配org.example包下所有标注@ManageRequestRate注解的方法(当前仅CrptAPI的createDocument方法,后续会扩展),求问错误原因及解决办法。

相关代码

切面类 RequestRateAspect

package org.example.aspects;

import lombok.RequiredArgsConstructor;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.example.requestRateManagers.RequestRateManager;
import org.springframework.stereotype.Component;

@Component
@Aspect
@RequiredArgsConstructor
public class RequestRateAspect {
    private final RequestRateManager requestRateManager;

    @Pointcut("within(org.example..*) && @annotation(org.example.annotations.ManageRequestRate)")
    public void annotationManageRequests() {
    }

    @Before("annotationManageRequests()")
    public void requestRateAdvice() {
        System.out.println("Running requestRateAdvice()..."); // 用于验证切面是否执行的日志
        requestRateManager.ensureRequestRate();
    }
}

注解类 ManageRequestRate

package org.example.annotations;

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

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

请求限流管理器接口 RequestRateManager

package org.example.requestRateManagers;

public interface RequestRateManager {
    void ensureRequestRate();
}

请求限流管理器实现类 RequestRateManagerImpl

package org.example.requestRateManagers;

import org.example.exceptions.CrptException;

import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

public class RequestRateManagerImpl implements RequestRateManager {
    private final TimeUnit timeUnit;
    private final int requestsPerTimeUnit;
    private final Semaphore semaphore;

    public RequestRateManagerImpl(TimeUnit timeUnit, int requestsPerTimeUnit) {
        this.timeUnit = timeUnit;
        this.requestsPerTimeUnit = requestsPerTimeUnit;
        this.semaphore = new Semaphore(requestsPerTimeUnit, true);
        startScheduledExecutor();
    }

    private void startScheduledExecutor() {
        Executors.newSingleThreadScheduledExecutor()
                .scheduleAtFixedRate(this::releasePermits, 0, 1, timeUnit);
    }

    @Override
    public void ensureRequestRate() {
        try {
            semaphore.acquire();
        } catch (InterruptedException e) {
            throw new CrptException(e);
        } finally {
            semaphore.release();
        }
    }

    private void releasePermits() {
        int numberOfPermitsToRelease = requestsPerTimeUnit - semaphore.availablePermits();
        semaphore.release(numberOfPermitsToRelease);
    }
}

目标类 CrptAPI

package org.example;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import org.example.annotations.ManageRequestRate;
import org.example.exceptions.CrptException;
import org.example.models.Item;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;

@Component
@RequiredArgsConstructor
public class CrptAPI {
    private final ObjectMapper objectMapper;

    @ManageRequestRate
    public String createDocument(Item item, String signature) {
        Map<String, Object> documentMap = getDocumentMap(item, signature);
        String document;
        try {
            document = objectMapper.writeValueAsString(documentMap);
        } catch (JsonProcessingException e) {
            throw new CrptException(e);
        }
        return document;
    }

    private Map<String, Object> getDocumentMap(Item item, String signature) {
        Map<String, Object> documentMap = new HashMap<>();
        documentMap.put(item.getClass().getSimpleName().toLowerCase(), item);
        documentMap.put("signature", signature);
        return documentMap;
    }
}

集成测试类 CrptAPIIntegrationTest

public class CrptAPIIntegrationTest {
    private final ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);

    @Test
    public void testCreateDocument() throws JSONException {
        Item item = new Item(UUID.randomUUID(), "test product name",
                "test brand", "1234-5678", "test agent name");
        UUID id = item.getId();
        CrptAPI crptAPI = context.getBean(CrptAPI.class);
        String documentJson = crptAPI.createDocument(item, "test signature");
        String expectedJson = """
                {
                "item":
                {
                "id":""" + id + "," + """
                "productName":"test product name",
                "brand":"test brand",
                "agentTaxIdNumber":"1234-5678",
                "agentName":"test agent name"
                },
                "signature":"test signature"}
                """;
        JSONAssert.assertEquals(expectedJson, documentJson, JSONCompareMode.STRICT); // 测试通过,但无切面日志输出
    }
}

IDE提示

IntelliJ IDEA提示“This advice advises no methods”


错误原因及修复方案

1. 核心问题:未开启Spring AOP自动代理

Spring默认不会自动启用AOP代理,必须在配置类上添加@EnableAspectJAutoProxy注解,否则Spring无法识别@Aspect类,也不会将切面逻辑织入到目标Bean中。

2. 次要问题:RequestRateManagerImpl未被Spring托管

当前RequestRateManagerImpl是普通Java类,没有被Spring容器管理,会导致切面无法注入RequestRateManager依赖,进而切面无法正常初始化。

具体修复步骤

步骤1:修改Spring配置类,开启AOP并注册Bean

@Configuration
@ComponentScan(basePackages = "org.example") // 确保扫描所有org.example下的组件
@EnableAspectJAutoProxy // 开启Spring AOP自动代理
public class SpringConfig {
    // 注册RequestRateManagerImpl为Spring Bean
    @Bean
    public RequestRateManager requestRateManager() {
        return new RequestRateManagerImpl(TimeUnit.SECONDS, 5); // 根据需求调整限流参数
    }

    // 注册ObjectMapper(如果之前未注册)
    @Bean
    public ObjectMapper objectMapper() {
        return new ObjectMapper();
    }
}

步骤2:修正RequestRateManagerImpl的限流逻辑(可选但关键)

当前ensureRequestRate方法中,acquire后立即在finally中release,相当于没有限流效果,应移除finally块的release,由定时任务统一释放许可:

@Override
public void ensureRequestRate() {
    try {
        // 可添加超时时间,避免请求无限阻塞
        if (!semaphore.tryAcquire(1, timeUnit)) {
            throw new CrptException("请求过于频繁,超过限流阈值");
        }
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt(); // 恢复中断状态
        throw new CrptException(e);
    }
}

步骤3:确认组件扫描范围

确保@ComponentScan覆盖了org.example下的所有子包,包括aspects、requestRateManagers、annotations等,这样Spring才能扫描到切面类、目标类等组件。


内容的提问来源于stack exchange,提问作者Sergey Zolotarev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 21:17:02