Search code examples
javaspringspring-mvcaspectjspring-annotations

How to get @PathVariable value for custom annotation?


I have a controller:

@Authorised(id = "{personId}")
@RequestMapping(value = {"{personId}"}, method = GET)
public void test(@PathVariable PersonId personId) {
    System.out.println(personId); //gets personId
}

Annotation:

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Authorised {
    String id() default "";
}

Pointcut:

@Pointcut("@annotation(Authorised)")
private void AuthorisedMethod() {}

And the method that has to get {personId} value not string "{personId}":

@Before("AuthorisedMethod()")
public void checkIfIsCurrentlyAuthenticated(JoinPoint joinPoint) throws NoSuchMethodException {
    MethodSignature signature = (MethodSignature) joinPoint.getSignature();
    String methodName = signature.getMethod().getName();
    Class<?>[] parameterTypes = signature.getMethod().getParameterTypes();
    Parameter[] parameters = signature.getMethod().getParameters();
    Authorised annotations = joinPoint.getTarget().getClass().getMethod(methodName, parameterTypes).getAnnotation(Authorised.class);
    String id = annotations.id();
    System.out.println(id); // prints: "{personId}"
    // do the chekcing
    throw new UnauthenticatedUserException();
}

Can it be achieved and how?

UPDATE: But what if method argument parameter number don't match with the pointcut args()? I mean that what if specific method has parameter @PathVariable PersonId personId and few more, but poincut needs to know only PersonId personId?

Like @statut said you have to write args() like that: args(personId,..)


Solution

  • You can modify @Before() annotation to have PersonId value and pass this value to aspect, for example

    @Before("AuthorisedMethod() && args(personId)")
    public void checkIfIsCurrentlyAuthenticated(JoinPoint joinPoint, PersonId personId) throws NoSuchMethodException {}
    

    To test it I had the following Aspect:

    @Aspect
    @Component
    public class SomeAspect {
    
        @Pointcut("@annotation(Authorised)")
        private void AuthorisedMethod() {
        }
    
        @Before("AuthorisedMethod() && args(personId)")
        public void checkIfIsCurrentlyAuthenticated(JoinPoint joinPoint, PersonId personId) throws NoSuchMethodException {
            System.out.println("aspect " + personId.getId());
        }
    
    }
    

    Configuration class:

    @Configuration
    @ComponentScan(basePackages = {"test"})
    @EnableAspectJAutoProxy(proxyTargetClass = true)
    public class Config {
    }
    

    Test component:

    @Component
    public class Test {
    
        @Authorised(id = "{personId}")
        public void test(PersonId personId) {
            System.out.println("component " + personId.getId()); //gets personId
        }
    }
    

    And testNG's runner:

    @ContextConfiguration(classes = Config.class)
    public class TestRunner extends AbstractTestNGSpringContextTests {
    
        @Autowired
        test.Test test;
    
        @Test
        public void testName() {
            test.test(new PersonId("id"));
        }
    
    }
    

    When I run it, I get printed "aspect id" from aspect and "component id" from invoked method.