您當前的位置:首頁 > 文化

談談 Java 中自定義註解及使用場景

作者:由 是個程式媛鴨 發表于 文化時間:2020-09-03

Java自定義註解一般使用場景為:自定義註解+攔截器或者AOP,使用自定義註解來自己設計框架,使得程式碼看起來非常優雅。本文將先從自定義註解的基礎概念說起,然後開始實戰,寫小段程式碼實現自定義註解+攔截器,自定義註解+AOP。

一. 什麼是註解(Annotation)

Java註解是什麼,以下是引用自維基百科的內容

Java註解又稱Java標註,是JDK5。0版本開始支援加入原始碼的特殊語法元資料。

Java語言中的類、方法、變數、引數和包等都可以被標註。和Javadoc不同,Java標註可以透過反射獲取標註內容。在編譯器生成類檔案時,標註可以被嵌入到位元組碼中。Java虛擬機器可以保留標註內容,在執行時可以獲取到標註內容。當然它也支援自定義Java標註。

二. 註解體系圖

元註解:java。lang。annotation中提供了元註解,可以使用這些註解來定義自己的註解。主要使用的是Target和Retention註解

談談 Java 中自定義註解及使用場景

註解處理類:既然上面定義了註解,那得有辦法拿到我們定義的註解啊。java。lang。reflect。AnnotationElement介面則提供了該功能。註解的處理是透過java反射來處理的。

如下,反射相關的類Class, Method, Field都實現了AnnotationElement介面。

談談 Java 中自定義註解及使用場景

談談 Java 中自定義註解及使用場景

因此,只要我們透過反射拿到Class, Method, Field類,就能夠透過getAnnotation(Class)拿到我們想要的註解並取值。

搜尋Java知音公眾號,回覆“後端面試”,送你一份Java面試題寶典

三. 常用元註解

Target:描述了註解修飾的物件範圍,取值在java。lang。annotation。ElementType定義,常用的包括:

METHOD:用於描述方法

PACKAGE:用於描述包

PARAMETER:用於描述方法變數

TYPE:用於描述類、介面或enum型別

Retention: 表示註解保留時間長短。取值在java。lang。annotation。RetentionPolicy中,取值為:

SOURCE:在原始檔中有效,編譯過程中會被忽略

CLASS:隨原始檔一起編譯在class檔案中,執行時忽略

RUNTIME:在執行時有效

只有定義為RetentionPolicy。RUNTIME時,我們才能透過註解反射獲取到註解。

所以,假設我們要自定義一個註解,它用在欄位上,並且可以透過反射獲取到,功能是用來描述欄位的長度和作用。

@Target

ElementType

FIELD

// 註解用於欄位上

@Retention

RetentionPolicy

RUNTIME

// 保留到執行時,可透過註解獲取

public

@interface

MyField

{

String

description

();

int

length

();

}

四. 示例-反射獲取註解

先定義一個註解:

@Target

ElementType

FIELD

@Retention

RetentionPolicy

RUNTIME

public

@interface

MyField

{

String

description

();

int

length

();

}

透過反射獲取註解

public

class

MyFieldTest

{

//使用我們的自定義註解

@MyField

description

=

“使用者名稱”

length

=

12

private

String

username

@Test

public

void

testMyField

(){

// 獲取類模板

Class

c

=

MyFieldTest

class

// 獲取所有欄位

for

Field

f

c

getDeclaredFields

()){

// 判斷這個欄位是否有MyField註解

if

f

isAnnotationPresent

MyField

class

)){

MyField

annotation

=

f

getAnnotation

MyField

class

);

System

out

println

“欄位:[”

+

f

getName

()

+

“], 描述:[”

+

annotation

description

()

+

“], 長度:[”

+

annotation

length

()

+

“]”

);

}

}

}

}

執行結果

談談 Java 中自定義註解及使用場景

應用場景一:自定義註解+攔截器 實現登入校驗

接下來,我們使用springboot攔截器實現這樣一個功能,如果方法上加了@LoginRequired,則提示使用者該介面需要登入才能訪問,否則不需要登入。

首先定義一個LoginRequired註解

@Target

ElementType

METHOD

@Retention

RetentionPolicy

RUNTIME

public

@interface

LoginRequired

{

}

然後寫兩個簡單的介面,訪問sourceA,sourceB資源

@RestController

public

class

IndexController

{

@GetMapping

“/sourceA”

public

String

sourceA

(){

return

“你正在訪問sourceA資源”

}

@GetMapping

“/sourceB”

public

String

sourceB

(){

return

“你正在訪問sourceB資源”

}

}

沒新增攔截器之前成功訪問

談談 Java 中自定義註解及使用場景

實現spring的HandlerInterceptor 類先實現攔截器,但不攔截,只是簡單列印日誌,如下:

public

class

SourceAccessInterceptor

implements

HandlerInterceptor

{

@Override

public

boolean

preHandle

HttpServletRequest

request

HttpServletResponse

response

Object

handler

throws

Exception

{

System

out

println

“進入攔截器了”

);

return

true

}

@Override

public

void

postHandle

HttpServletRequest

request

HttpServletResponse

response

Object

handler

ModelAndView

modelAndView

throws

Exception

{

}

@Override

public

void

afterCompletion

HttpServletRequest

request

HttpServletResponse

response

Object

handler

Exception

ex

throws

Exception

{

}

}

實現spring類WebMvcConfigurer,建立配置類把攔截器新增到攔截器鏈中

@Configuration

public

class

InterceptorTrainConfigurer

implements

WebMvcConfigurer

{

@Override

public

void

addInterceptors

InterceptorRegistry

registry

{

registry

addInterceptor

new

SourceAccessInterceptor

())。

addPathPatterns

“/**”

);

}

}

攔截成功如下

談談 Java 中自定義註解及使用場景

在sourceB方法上新增我們的登入註解@LoginRequired

@RestController

public

class

IndexController

{

@GetMapping

“/sourceA”

public

String

sourceA

(){

return

“你正在訪問sourceA資源”

}

@LoginRequired

@GetMapping

“/sourceB”

public

String

sourceB

(){

return

“你正在訪問sourceB資源”

}

}

簡單實現登入攔截邏輯

@Override

public

boolean

preHandle

HttpServletRequest

request

HttpServletResponse

response

Object

handler

throws

Exception

{

System

out

println

“進入攔截器了”

);

// 反射獲取方法上的LoginRequred註解

HandlerMethod

handlerMethod

=

HandlerMethod

handler

LoginRequired

loginRequired

=

handlerMethod

getMethod

()。

getAnnotation

LoginRequired

class

);

if

loginRequired

==

null

){

return

true

}

// 有LoginRequired註解說明需要登入,提示使用者登入

response

setContentType

“application/json; charset=utf-8”

);

response

getWriter

()。

print

“你訪問的資源需要登入”

);

return

false

}

執行成功,訪問sourceB時需要登入了,訪問sourceA則不用登入

談談 Java 中自定義註解及使用場景

談談 Java 中自定義註解及使用場景

應用場景二:自定義註解+AOP 實現日誌列印

先匯入切面需要的依賴包

<

dependency

>

<

groupId

>

org

springframework

boot

groupId

>

<

artifactId

>

spring

-

boot

-

starter

-

aop

artifactId

>

dependency

>

定義一個註解@MyLog

@Target

ElementType

METHOD

@Retention

RetentionPolicy

RUNTIME

public

@interface

MyLog

{

}

定義一個切面類,見如下程式碼註釋理解:

@Aspect

// 1。表明這是一個切面類

@Component

public

class

MyLogAspect

{

// 2。 PointCut表示這是一個切點,@annotation表示這個切點切到一個註解上,後面帶該註解的全類名

// 切面最主要的就是切點,所有的故事都圍繞切點發生

// logPointCut()代表切點名稱

@Pointcut

“@annotation(me。zebin。demo。annotationdemo。aoplog。MyLog)”

public

void

logPointCut

(){};

// 3。 環繞通知

@Around

“logPointCut()”

public

void

logAround

ProceedingJoinPoint

joinPoint

){

// 獲取方法名稱

String

methodName

=

joinPoint

getSignature

()。

getName

();

// 獲取入參

Object

[]

param

=

joinPoint

getArgs

();

StringBuilder

sb

=

new

StringBuilder

();

for

Object

o

param

){

sb

append

o

+

“; ”

);

}

System

out

println

“進入[”

+

methodName

+

“]方法,引數為:”

+

sb

toString

());

// 繼續執行方法

try

{

joinPoint

proceed

();

}

catch

Throwable

throwable

{

throwable

printStackTrace

();

}

System

out

println

methodName

+

“方法執行結束”

);

}

}

在步驟二中的IndexController寫一個sourceC進行測試,加上我們的自定義註解:

@MyLog

@GetMapping

“/sourceC/{source_name}”

public

String

sourceC

@PathVariable

“source_name”

String

sourceName

){

return

“你正在訪問sourceC資源”

}

啟動springboot web專案,輸入訪問地址

談談 Java 中自定義註解及使用場景

標簽: 註解  自定義  攔截器  Java  訪問