自定義註解實戰
- 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){ ... } 是不是很簡單實用?