1. 程式人生 > 實用技巧 >springboot自定義註解

springboot自定義註解

springboot自定義註解

註解(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,後面發現了再補充。

總結

好了,今天的內容就到這裡,核心內容就是學會自定義註解,然後可以應用自定義註解解決問題。今天提供的思路就是通過自定義註解實現攔截器解耦,即新增方法後只需要在方法上增加鑑權註解即可,無需修改攔截器配置。某個方法不想鑑權,僅需要去掉方法上的鑑權註解即可。好了,大家週末愉快哦!!!