标注在接口上的注解无效,无法使用aop拦截,即使声明了@Inherited

博客讲述将注解标注在接口方法上,用AOP无法拦截,即便使用@Inherited元注解也不行。阅读源码可知,标注@Inherited的自定义注解在接口中不生效,在类中生效需@Target为ElementType.TYPE,即类级别,还举例说明生效情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

通常我们会通过实现一个接口来编写一个类,为了省事,我将注解直接标注在了接口的方法上,实验发现通过aop并没有拦截到该注解,即使在定义注解时使用了@Inherited元注解。
通过阅读@Inherited注解的源码,可以发现:

package java.lang.annotation;

/**
 * Indicates that an annotation type is automatically inherited.  If
 * an Inherited meta-annotation is present on an annotation type
 * declaration, and the user queries the annotation type on a class
 * declaration, and the class declaration has no annotation for this type,
 * then the class's superclass will automatically be queried for the
 * annotation type.  This process will be repeated until an annotation for this
 * type is found, or the top of the class hierarchy (Object)
 * is reached.  If no superclass has an annotation for this type, then
 * the query will indicate that the class in question has no such annotation.
 *
 * <p>Note that this meta-annotation type has no effect if the annotated
 * type is used to annotate anything other than a class.  Note also
 * that this meta-annotation only causes annotations to be inherited
 * from superclasses; annotations on implemented interfaces have no
 * effect.
 *
 * @author  Joshua Bloch
 * @since 1.5
 * @jls 9.6.3.3 @Inherited
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Inherited {
}

标注了@Inherited元注解的自定义注解在接口中是不生效的,在类中生效也分情况:

  • 该注解的@Target必须是@Target(ElementType.TYPE),即该注解的使用范围必须是类级别。方法级别等是无效的。

举例说明,下面这种情况生效:
自定义注解:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Inherited
public @interface NeedBye {
}

父类:

//注意该注解的使用位置
@NeedBye
abstract public class Waiter {
    public void greetTo(String name){};
    abstract public void serveTo(String name);
}

子类:

public class NaiveWaiter extends  Waiter {
    @Override
    public void greetTo(String clientName) {
        System.out.println("greeting to "+ clientName +"...");
    }
    @Override
    public void serveTo(String clientName) {
        System.out.println("serving to "+ clientName +"...");
    }
}

切面:

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;

@Aspect
public class AfterServingAspect {

    /*@After("execution(* serveTo(..))")
    public void afterServing(){
        System.out.println("Bye");
    }*/
//标注在类级别的注解,要使用@within,而不是@annotation
    @After("@within(com.smart.annotation.NeedBye)")
    public void afterServing(){
        System.out.println("Bye");
    }
}

运行结果:
这里写图片描述

具体运行参考:https://blog.youkuaiyun.com/my_wings/article/details/82018203

<think>嗯,用户想在Spring Boot中通过自定义注解@NoToken来跳过某些接口的Token校验。这个问题看起来是关于如何利用自定义注解拦截器或AOP来实现特定接口的放行。我需要先回忆一下相关的知识,然后结合提供的引用内容来组织答案。 首先,根据引用中的信息,用户提到使用拦截器来全局拦截请求,然后通过自定义注解来排除某些接口。比如引用1和引用3中的例子,他们创建了一个@NoToken注解,并在拦截器中检查是否存在该注解,如果存在则跳过Token校验。引用4还提到AOP拦截器的区别,这里可能更适合用拦截器,因为处理的是HTTP请求层面的拦截。 接下来,我需要整理实现步骤。首先需要创建自定义注解@NoToken,设置它的作用范围,可以是类或方法。然后,在拦截器中,当处理请求时,检查目标方法或其所属的类是否有@NoToken注解。如果有的话,就不进行Token校验,直接放行。否则,执行正常的校验流程。 需要注意的是,自定义注解注解,比如@Target和@Retention,要正确设置,确保注解在运行时可用,并且可以应用在类和方法上。拦截器的preHandle方法中,如何获取到处理程序对应的Method对象是关键。可能需要通过HandlerMethod来转换,然后获取方法或类上的注解。 另外,引用中的例子显示,拦截器需要注册到Spring的配置中,确保其生效。同时,用户可能需要排除一些静态资源路径,但这个问题中主要关注接口的放行,所以可能不需要处理这部分。 可能出现的问题包括:注解继承性,比如类上的注解是否会影响其所有方法;拦截器的执行顺序是否正确;如何正确处理控制器类和方法上的注解优先级。根据引用3的例子,类上的注解会影响所有方法,而方法上的注解会覆盖类的设置,所以需要先检查方法,再检查类。 最后,应该给出示例代码,展示如何创建注解和修改拦截器,以及如何在Controller中使用注解。同时,生成相关问题,帮助用户进一步理解,比如与AOP的区别,如何组合使用多个注解,以及处理继承性问题等。</think>### Spring Boot通过@NoToken跳过Token校验的实现方式 #### 一、实现步骤 1. **创建自定义注解@NoToken** 定义注解时需指定其作用于类或方法,并保留至运行时: ```java @Inherited @Documented @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface NoToken {} ``` - `@Inherited`:允许子类继承父类的注解[^3] - `@Target`:指定注解标注类或方法 - `@Retention`:确保注解在运行时可通过反射获取 2. **修改Token拦截器逻辑** 在拦截器的`preHandle`方法中,通过反射判断目标接口是否包含`@NoToken`注解: ```java public class TokenInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (handler instanceof HandlerMethod) { HandlerMethod handlerMethod = (HandlerMethod) handler; // 检查方法上的注解 Method method = handlerMethod.getMethod(); if (method.isAnnotationPresent(NoToken.class)) { return true; // 跳过校验 } // 检查类上的注解 Class<?> controllerClass = method.getDeclaringClass(); if (controllerClass.isAnnotationPresent(NoToken.class)) { return true; } } // 执行正常Token校验逻辑 String token = request.getHeader("Authorization"); // ...校验逻辑... return true; } } ``` 优先级:**方法级注解 > 类级注解**,即方法上的`@NoToken`会覆盖类上的配置[^3]。 3. **注册拦截器** 将拦截器添加到Spring Boot配置中: ```java @Configuration public class WebConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new TokenInterceptor()) .addPathPatterns("/**") .excludePathPatterns("/static/**"); } } ``` #### 二、使用示例 ```java @RestController @NoToken // 类级注解:所有接口免校验 public class DemoController { @GetMapping("/public") public String publicApi() { return "无需Token"; } @GetMapping("/secure") @Token // 方法级注解:需要校验(假设@Token为需校验标记) public String secureApi() { return "需校验Token"; } } ``` #### 三、实现原理 通过拦截器对所有请求进行拦截,利用反射机制检查目标接口注解信息。若存在`@NoToken`,则直接放行请求,否则执行Token校验流程。此方案相比AOP更适合处理HTTP请求层面的拦截需求[^4]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值