Search code examples
javaspringaop

"This advice advises no methods"


I am trying to complete a simple (but, tbh, badly formulated) entry test. I used an aspect to incorporate the request limit feature, but the chances are it doesn't work. The IDE says "This advice advises no methods". I wrote and ran an integration test, and it seems the IDE is correct. What is my mistake, why doesn't my advice get applied? I need to aspect all methods within the org.example package that have the @ManageRequestRate annotation (it applies only to one method, but let's imagine I can include more methods like that in the future)

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()..."); // a simple log to see whether it works
        requestRateManager.ensureRequestRate();
    }
}
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 {
}
package org.example.requestRateManagers;

public interface RequestRateManager {
    void ensureRequestRate();
}
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);
    }
}
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;
    }
}
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); // passes, but no log is printed
    }
}

IntelliJ IDEA with the message "This advice advices no methods"

This questions asks a whole nother thing. I have no clue why it was suggested as a potential duplicate


Solution

  • Oh boy, oh boy... Rookie mistake... I forgot to include the @EnableAspectJAutoProxy annotation

    package org.example.configurations;
    
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
    import org.example.requestRateManagers.RequestRateManager;
    import org.example.requestRateManagers.RequestRateManagerImpl;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.EnableAspectJAutoProxy;
    
    import java.util.concurrent.TimeUnit;
    
    @Configuration
    @ComponentScan(basePackages = "org.example")
    @EnableAspectJAutoProxy // this is important
    public class SpringConfig {
        @Bean
        public RequestRateManager requestRateManager() {
            return new RequestRateManagerImpl(TimeUnit.MINUTES, 100);
        }
    
        @Bean
        public ObjectMapper objectMapper() {
            return new ObjectMapper().registerModule(new ParameterNamesModule());
        }
    }
    

    However, the IDE is still not convinced

    enter image description here