【问题标题】:How to get @PathVariable value for custom annotation?如何获取自定义注释的@PathVariable 值?
【发布时间】:2018-07-31 17:44:09
【问题描述】:

我有一个控制器:

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

注释:

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

切入点:

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

必须获取 {personId} 值的方法不是字符串“{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();
}

能否实现以及如何实现?

更新:但是如果方法参数参数编号与切入点 args() 不匹配怎么办?我的意思是,如果特定方法有参数@PathVariable PersonId personId 等等,但 poincut 只需要知道 PersonId personId 怎么办?

就像@statut 说的你必须这样写 args():args(personId,..)

【问题讨论】:

    标签: java spring spring-mvc aspectj spring-annotations


    【解决方案1】:

    例如,您可以修改@Before() 注释以具有PersonId 值并将此值传递给方面

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

    为了测试它,我有以下方面:

    @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
    @ComponentScan(basePackages = {"test"})
    @EnableAspectJAutoProxy(proxyTargetClass = true)
    public class Config {
    }
    

    测试组件:

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

    还有 testNG 的跑步者:

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

    当我运行它时,我会从方面打印"aspect id",从调用的方法打印"component id"

    【讨论】:

    • 如果我按照您建议的方式更改注释和方法签名,则根本不会调用 checkIfIsCurrentlyAuthenticated 方法。你知道为什么吗? @statut
    • 你能分享你的代码吗?我自己试过了,效果很好。
    • 对不起,我不能,因为它是在一个更大的项目中实现的。所以这里只是它的简化版本。但是,问题仍然存在:) @statut
    • @BrainDead 我在答案中添加了一些测试代码,请参阅
    • 我唯一没有的是@EnableAspectJAutoProxy(proxyTargetClass = true)。我添加后没有任何变化。
    【解决方案2】:

    如果可能的话,您还可以使用 HandlerInterceptor 获取 RequestMapping URL 中 PathVariable 的值。

    编写一个Handler类来拦截这个Request。

    public class AuthorisedHandler extends HandlerInterceptorAdapter {
    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        if (!isResourceHandler(handler) && (handler instanceof HandlerMethod)) {
    
            HandlerMethod hm = (HandlerMethod) handler;
            Method method = hm.getMethod();
    
            Authorised authAnnotation = method.getAnnotation(Authorised.class);
            if (authAnnotation != null) {
                String personId = getPersonId(request);
                //Do all your validations Here
            }
        }
        return true;
    }
    
    @SuppressWarnings("rawtypes")
    private String getPersonId(HttpServletRequest request) throws IOException {
        String personId = request.getParameter("personId");
        if(personId == null || personId.equals("")){
            Map pathVariables = (Map) request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
            personId = (String) pathVariables.get("personId");
        }
        return personId;
    }
    
    
    private boolean isResourceHandler(Object handler) {
        return handler instanceof ResourceHttpRequestHandler;
    }
    
    }
    

    而且你必须在 spring config xml 或 Spring Java Config 中配置这个 Handler bean。

    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <bean class="com.**.AuthorisedHandler" />
        </mvc:interceptor>
    </mvc:interceptors>
    

    现在,所有的请求都将通过这个拦截器。只有带有@Authorised 注释的才会通过。

    【讨论】:

    • 是的,这可能是解决这个问题的一种方法,但这不是我问题的答案:)
    猜你喜欢
    • 2021-07-05
    • 1970-01-01
    • 2021-12-28
    • 1970-01-01
    • 1970-01-01
    • 2014-12-10
    • 1970-01-01
    • 1970-01-01
    • 2019-01-06
    相关资源
    最近更新 更多