简体   繁体   中英

How to correctly use Spring AOP to select the execution of a method annotated with a specific annotation?

I am studying Spring AOP and I have the following doubt related to a quetion found on my study material.

So consider the following pointcut: execution(@com.myapp.MyCustomAnnotation void *(..)) . What exactly means?

It give me the following answer that it seems to me a litle strange (using my knoledge on how AOP works). It say that:

This will select joint points representing voiud method that are annotated by @com.myapp.MyCustomAnnotation annotation.

Ok so what it means that using AOP I can specify when it is performed a specific method that is annotated with a specific annotation? Is it right or am I missing something?

So, if the previous assertion is correct, it means that (for example) can I also specify something like: "when it is performed a controller method annotated by @RequestMapping("/listAccounts") ? (that means, when the controller handle the HttpRequest towards the /listAccounts resource doing something like:

execution(@RequestMapping("/listAccounts") * *(..))

Can I do something like this or not?

Spring is using the AspectJ Pointcut Expression Language ( https://eclipse.org/aspectj/doc/next/adk15notebook/annotations-pointcuts-and-advice.html )

applying your pointcut expression means intercepting all method-calls with any name and parameter list having a 'void' return type as long as the method is annotated with @com.myapp.MyCustomAnnotation.

It is not possible to match Join-Points using Annotation-Parameters though, so your second Pointcut-Expression is invalid.

You can't explicitly specify the arguments to the annotation in the pointcut like that. Rather, you could set a pointcut to capture all methods with @RequestMapping annotation, then retrieve the annotation from the method and check that the value matches the endpoint. For example:

@PointCut("@annotation(org.springframework.web.bind.annotation.RequestMapping)")
public void requestMapping() {}

@Before("requestMapping()")
public void handleRequestMapping(JoinPoint joinPoint) {
    MethodSignature signature = (MethodSignature) joinPoint.getSignature();
    Method method = signature.getMethod();
    String mapping = method.getAnnotation(RequestMapping.class).value()[0];

    if (mapping.equals("/listAccounts") {
        // do something
    }
}

André R.'s answer is incorrect because it is possible to limit annotation matching to parameter values, but there is a limitation: It only works with simple types like strings, integers and classes, not arrays of strings or so. But in your specific example the Spring annotation @RequestMapping 's value type is String[] , see Javadoc . Let us assume you have this situation:

Annotation:

package de.scrum_master.app;

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

@Target(value={ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    int value();
    String name() default "";
    Class<?> type() default Object.class;
}

Driver application:

Here we have an application with several methods annotated by the Spring annotation @RequestMapping and by our home-made annotation @MyAnnotation :

package de.scrum_master.app;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

public class Application {
    @RequestMapping("/listAccounts")
    @MyAnnotation(11)
    public void doSomething() {}

    public void doSomethingElse() {}

    @RequestMapping(value = {"/listAccounts","/another/method"}, name = "Newton")
    @MyAnnotation(value = 22, type = String.class)
    public void foo() {}

    @RequestMapping(value = "/listAccounts", method = RequestMethod.POST, name = "Einstein")
    @MyAnnotation(value = 11, name = "John Doe", type = String.class)
    public void bar() {}

    @RequestMapping(value = "/listCustomers", method = RequestMethod.GET, name = "Einstein")
    @MyAnnotation(value = 22, name = "Jane Doe")
    public void zot() {}

    @RequestMapping(value = "/listAccounts", produces = {"application/json","application/xml"}, consumes = "text/html", name = "Newton")
    public void baz() {}

    public static void main(String[] args) {
        Application application = new Application();
        application.doSomething();
        application.doSomethingElse();
        application.foo();
        application.bar();
        application.zot();
        application.baz();
    }
}

Annotation matching aspect:

package de.scrum_master.aspect;

import java.util.Arrays;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.web.bind.annotation.RequestMapping;

import de.scrum_master.app.MyAnnotation;

@Aspect
public class AnnotationParameterMatcher {
    // Match *all* methods annotated by @RequestMapping
    @Before("execution(* *(..)) && @annotation(requestMapping)")
    public void logRequestMapping(JoinPoint thisJoinPoint, RequestMapping requestMapping) {
        // Print joinpoint and annotation
        System.out.println(thisJoinPoint + " -> " + requestMapping);
    }

    // Match *all* methods annotated by @RequestMapping
    @Before("execution(* *(..)) && @annotation(requestMapping)")
    public void logMatchingValue(JoinPoint thisJoinPoint, RequestMapping requestMapping) {
        // Only print if value array contains "/listAccounts"
        if (Arrays.asList(requestMapping.value()).contains("/listAccounts"))
            System.out.println("  value contains '/listAccounts'");
    }

    // Match *all* methods annotated by @RequestMapping and bind 'name' parameter
    @Before("execution(* *(..)) && @annotation(RequestMapping(name))")
    public void logName(JoinPoint thisJoinPoint, String name) {
        System.out.println("  name = '" + name + "'");
    }

    // Match methods annotated by @MyAnnotation with value=11
    @Before("execution(@MyAnnotation(value=11) * *(..))")
    public void logName(JoinPoint thisJoinPoint) {
        System.out.println("  @MyAnnotation(value=11) detected");
    }

    // Match methods annotated by @MyAnnotation with 3 specific parameter values 
    @Before("execution(@MyAnnotation(value=11, name=\"John Doe\", type=String.class) * *(..)) && @annotation(myAnnotation)")
    public void logName(JoinPoint thisJoinPoint, MyAnnotation myAnnotation) {
        System.out.println("  " + myAnnotation);
    }
}

Console output:

execution(void de.scrum_master.app.Application.doSomething()) -> @org.springframework.web.bind.annotation.RequestMapping(headers=[], method=[], name=, produces=[], params=[], value=[/listAccounts], consumes=[])
  value contains '/listAccounts'
  name = ''
  @MyAnnotation(value=11) detected
execution(void de.scrum_master.app.Application.foo()) -> @org.springframework.web.bind.annotation.RequestMapping(headers=[], method=[], name=Newton, produces=[], params=[], value=[/listAccounts, /another/method], consumes=[])
  value contains '/listAccounts'
  name = 'Newton'
execution(void de.scrum_master.app.Application.bar()) -> @org.springframework.web.bind.annotation.RequestMapping(headers=[], method=[POST], name=Einstein, produces=[], params=[], value=[/listAccounts], consumes=[])
  value contains '/listAccounts'
  name = 'Einstein'
  @MyAnnotation(value=11) detected
  @de.scrum_master.app.MyAnnotation(name=John Doe, type=class java.lang.String, value=11)
execution(void de.scrum_master.app.Application.zot()) -> @org.springframework.web.bind.annotation.RequestMapping(headers=[], method=[GET], name=Einstein, produces=[], params=[], value=[/listCustomers], consumes=[])
  name = 'Einstein'
execution(void de.scrum_master.app.Application.baz()) -> @org.springframework.web.bind.annotation.RequestMapping(headers=[], method=[], name=Newton, produces=[application/json, application/xml], params=[], value=[/listAccounts], consumes=[text/html])
  value contains '/listAccounts'
  name = 'Newton'

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM