自定義註解實戰

  • 2019 年 10 月 3 日
  • 筆記

自定義註解實戰

Java註解又稱Java標註,是Java語言5.0版本開始支援加入源程式碼的特殊語法元數據
Java註解為我們在程式碼中添加資訊提供了一種形式化的方法,使我們可以在稍後某個時刻非常方便的使用這些數據。
Java語言中的類、方法、變數、參數和包等都可以被標註。和Javadoc不同,Java標註可以通過反射獲取註解內容。在編譯器生成類文件時,註解可以被嵌入到位元組碼中。Java虛擬機可以保留註解內容,在運行時可以獲取到註解內容。

Java註解概述

jdk1.5版本內置了三種標準的註解:

@Override,表示當前的方法定義將覆蓋超類中的方法。
@Deprecated,表示註解的程式碼是不贊成使用的程式碼(即將被棄用的程式碼)
@SuppressWarnings,告訴編譯器忽略指定的警告,不用在編譯完成後出現警告資訊

同時Java還提供了4種註解用於新註解的創建:

@Target:用於說明註解修飾的範圍。

可以在以下範圍內選擇一個。

ElementType.METHOD:方法聲明
ElementType.TYPE:類、介面(包括註解類型)或enum聲明
ElementType.CONSTRUCTOR:構造器的聲明
ElementType.FIELD:域聲明(包括enum實例)
ElementType.LOCAL_VARIABLE:局部變數聲明
ElementType.PACKAGE:包聲明
ElementType.PARAMETER:參數聲明

@Retention:表示需要在什麼級別保存該註解資訊

RetentionPolicy.RUNTIME:運行期間保留註解,可以通過反射機制讀取註解資訊
RetentionPolicy.SOURCE:註解將被編譯器丟棄
RetentionPolicy.CLASS:註解在class文件中可用,但會被VM丟棄

@Document:表示將註解包含在Javadoc中
@Inherited:允許子類繼承父類中的註解,默認不能被子類繼承


如何自定義註解

自定義註解很簡單,只需要將上面元註解使用好即可。
以我們這次項目中的自定義註解為例:

@Target(ElementType.PARAMETER)  @Retention(RetentionPolicy.RUNTIME)  public @interface CurrentUser {  }

分析一下這個註解。
@Target(ElementType.PARAMETER) 表示該註解是使用範圍是參數
@Retention(RetentionPolicy.RUNTIME) 表示運行期間保留註解,並且可以通過反射機制讀取註解資訊

這兩個註解加在一起表示,@CurrentUser 這個註解是用來標註參數的,它在運行時還能獲取到註解資訊。


自定義註解實戰

如果只是單純地寫一個註解,這個註解就沒有任何實際價值。我們需要通過處理這個註解來達到我們想要的結果,
項目需求是希望通過@CurrentUser註解獲取當前登錄用戶的userId。
我們會怎麼做呢?下面是我們處理的邏輯圖:
自定义注解原理图

按照步驟拆分自定義註解的使用:

第一步,自定義註解。
第二步,繼承HandlerMethodArgumentResolver,實現數據綁定。

spring調用HandlerMethodArgumentResolver實現Controller的參數裝配。HandlerMethodArgumentResolver實現類中會調用DataBinder,Converter等。常用的該介面實現類有:
ServletModelAttributeMethodProcessor: 實體類的組裝用它實現。
RequestParamMethodArgumentResolver: 基本數據類型如String用它實現。

RequestParamMethodArgumentResolver介面定義了實現類必須實現supportsParameter和resolveArgument這兩個方法。
第一個方法表示這個方法的參數是否支援這個解析器,第二個方法將方法參數解析為來自給定請求的參數值。

自定義解析器都需要實現這個介面,下面是我們在項目中的實現類:

public class UserModelResolver<T> implements HandlerMethodArgumentResolver {      public static final String REQUEST_USERID = "x-erp-auth-id";        @Override    public boolean supportsParameter(MethodParameter parameter) {      //如果有CurrentUser註解則支援      return parameter.hasParameterAnnotation(CurrentUser.class);    }      @Override    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,        NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {        Integer userId = 0;      //取出鑒權網關存入的登錄用戶Id,內部服務調顯示賦值userId      String headerData = webRequest.getHeader(REQUEST_USERID);      if (!Strings.isNullOrEmpty(headerData)) {        try {          userId = Integer.parseInt((String) headerData);        } catch (NumberFormatException nfe) {          userId = 0;        }      }      return userId;    }  }
第三步,在方法中使用,程式碼如下
  @GetMapping("/listDevices")      public BaseReturnVO listShopDevice(@CurrentUser Integer userId, @Valid @NotNull(message = "shopPublicId為空") Integer shopPublicId){          ...      }  

是不是很簡單實用?


The End

Tags: