springboot拦截器解耦——自定义注解

云中志

共 6516字,需浏览 14分钟

 ·

2021-04-27 19:11

注解(Annotation)对做java开发的小伙伴肯定不陌生,不能说熟悉,但一定在学习或者做项目的过程中有所耳闻,特别是随着springboot框架的大火,“约定大于配置”的开发理念被越来越多的人所喜爱。当然使用注解的最重要的好处就是减少代码的侵入,降低系统耦合性,当然就这一个理由就足够了。
在没有详细了解注解,没有自己定义注解,没有应用自定义注解之前,我对注解的认知仅仅停留在它是一种标记,特别是在springboot中,通过注解我们可以免去繁琐的配置过程,简化开发流程,但现在我发现自定义注解如果真的用的好,可以解决很多实际开发过程中的痛点、难点,让我们可以提出更多更合理的非侵入式解决方案,比如方法的鉴权、多数据源的数据源选择。好了,这里我们先不多说了,开始正文。

什么是注解

这里要多说些,介绍下注解的一些情况。注解(Annotation)相当于一种标记,在程序中加入注解就等于为程序打上某种标记。标记可以加在包、类、属性、方法、方法的参数以及局部变量上。通过反射可以拿到类、方法、变量上的注解。我们随便打开一个注解,比如springboot里面的Configuration注解:
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface Configuration {     @AliasFor(         annotation = Component.class    )     String value() default "";      boolean proxyBeanMethods() default true; }

根据上面的代码,我们可以得出以下结论:
  • 创建注解的关键字是@interface
  • 注解上也可以加注解
  • 注解可以有属性
但注解上加的都是什么注解,都有什么含义呢?这里要引入注解的一个概念——元注解。所谓元注解就是声明在注解上的注解,元注解是注解的一种声明,元注解分别有以下五种:
  • @Retention:保留期,声明注解的存活时间
    • RetentionPolicy.SOURCE:仅源代码时保留,在编译时会被丢弃忽略
    • RetentionPolicy.CLASS:编译为class时保留,但不会被加载到jvm中
    • RetentionPolicy.RUNTIME:运行环境保留,会被加载到jvm中
  • @Documented:保留本类中的注解并能够被javadoc识别
  • @Target:指定注解的添加位置(类/方法/变量等)
    • ElementType.TYPE:类注解
    • ElementType.FIELD:字段注解
    • ElementType.METHOD:方法注解
    • ElementType.PARAMETER:方法内的参数注解
    • ElementType.CONSTRUCTOR:构造方法注解
    • ElementType.LOCAL_VARIABLE:局部变量注解
    • ElementType.ANNOTATION_TYPE:注解注解
    • ElementType.PACKAGE:包注解
    • ElementType.TYPE_PARAMETER
    • ElementType.TYPE_USE
  • @Inherited:注解是否能够被子类继承
注解的介绍就到这里,我们继续往下看。

创建springboot项目

这里其实我不想介绍太多,因为我觉得这些都是很基础的东西,应该是每个小伙伴都会的,但考虑到还有一些小伙伴处在初学阶段,所以我还是会贴出我的项目结构,方便这些小伙伴参考:
首先是项目结构

pom.xml文件
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">     <modelVersion>4.0.0</modelVersion>     <groupId>io.github.syske</groupId>     <artifactId>custom-annotation-demo</artifactId>     <version>0.0.1-SNAPSHOT</version>     <name>custom-annotation-demo</name>     <description>Demo project for Spring Boot</description>      <properties>         <java.version>1.8</java.version>         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>         <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>         <spring-boot.version>2.3.0.RELEASE</spring-boot.version>     </properties>      <dependencies>         <dependency>             <groupId>org.springframework.boot</groupId>             <artifactId>spring-boot-starter-web</artifactId>         </dependency>          <dependency>             <groupId>org.springframework.boot</groupId>             <artifactId>spring-boot-starter-test</artifactId>             <scope>test</scope>             <exclusions>                 <exclusion>                     <groupId>org.junit.vintage</groupId>                     <artifactId>junit-vintage-engine</artifactId>                 </exclusion>             </exclusions>         </dependency>          <dependency>             <groupId>com.alibaba</groupId>             <artifactId>fastjson</artifactId>             <version>1.2.61</version>         </dependency>     </dependencies>      <dependencyManagement>         <dependencies>             <dependency>                 <groupId>org.springframework.boot</groupId>                 <artifactId>spring-boot-dependencies</artifactId>                 <version>${spring-boot.version}</version>                 <type>pom</type>                 <scope>import</scope>             </dependency>         </dependencies>     </dependencyManagement>      <build>         <plugins>             <plugin>                 <groupId>org.apache.maven.plugins</groupId>                 <artifactId>maven-compiler-plugin</artifactId>                 <configuration>                     <source>1.8</source>                     <target>1.8</target>                     <encoding>UTF-8</encoding>                 </configuration>             </plugin>             <plugin>                 <groupId>org.springframework.boot</groupId>                 <artifactId>spring-boot-maven-plugin</artifactId>             </plugin>         </plugins>     </build>  </project>

自定义注解

根据我们注解的介绍,我们在声明注解的时候必须用到元注解,否则这个注解是没有任何意义的。开始定义我们的第一个注解:
 import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;  @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface CheckAuth { }

这样我们的第一个注解就定义好了,包含了两个元注解,一个指明注解的类型,一个指明注解的生存时间,是不是很简单,接下来,我们要开始使用我们的注解。

自定义注解应用

我先说下本次示例注解的应用思路:我刚定义的注解是为了方法鉴权操作,所以我把刚定义的注解加在需要进行鉴权操作的方法上,然后定义一个拦截器,拦截器的拦截规则设置为拦截所有,然后在拦截器内进行判断和校验,如果方法有鉴权注解,则进行鉴权操作,否则跳过,具体如下:
拦截器
 public class AuthenticationInterceptor implements HandlerInterceptor {     @Override     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {         if (handler instanceof HandlerMethod) {             HandlerMethod handlerMethod = (HandlerMethod) handler;             if (handlerMethod.hasMethodAnnotation(CheckAuth.class)) {                 System.out.println("有CheckAuth注解");                 String token = request.getParameter("token");                 if (!"ABCDEF12345".equals(token)) {                     throw new AuthException();                }            }        }         return true;    } }

拦截器配置:
  // 鉴权拦截器 registry.addInterceptor(new AuthenticationInterceptor()).addPathPatterns("/**");

这里最核心的操作是handlerMethod.hasMethodAnnotation(CheckAuth.class),即判断当前方法是否有CheckAuth.class注解。
当然从handler中获取方法也是很重要的一个操作,我们只有从handler中拿到方法,才能判断该方法是否有我们自定义的注解。我们先来看下handler都有哪些类型:
  • HandlerMethod:方法
  • ResourceHttpRequestHandler:静态资源
根据目前掌握的资料,我还没发现其他类型的handler,后面发现了再补充。

总结

好了,今天的内容就到这里,核心内容就是学会自定义注解,然后可以应用自定义注解解决问题。今天提供的思路就是通过自定义注解实现拦截器解耦,即新增方法后只需要在方法上增加鉴权注解即可,无需修改拦截器配置。某个方法不想鉴权,仅需要去掉方法上的鉴权注解即可。

浏览 43
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报