您好,登錄后才能下訂單哦!
這篇文章主要介紹“怎么使用注解”,在日常操作中,相信很多人在怎么使用注解問題上存在疑惑,小編查閱了各式資料,整理出簡(jiǎn)單好用的操作方法,希望對(duì)大家解答”怎么使用注解”的疑惑有所幫助!接下來,請(qǐng)跟著小編一起來學(xué)習(xí)吧!
首關(guān)之程媧造注
注解一旦構(gòu)造出來,就享有編譯器的類型檢查保護(hù)。讓我們先看下一組代碼熱熱身:
public class TestService { @MyAnnotation public void runTset() { System.out.println("annotation test"); } }
不要納悶,Java 中確實(shí)沒有一個(gè)注解名為MyAnnotation,但這個(gè)怎么來的呢,就是我們自己造的。
那么關(guān)子賣完了,接下來就來揭秘注解的制造:
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation { }
這樣子,一個(gè)簡(jiǎn)單的注解就新鮮出爐了。需要注意的是這可不是一個(gè)接口,需要留言interface前面還有一個(gè)@,這個(gè)標(biāo)識(shí)要是漏掉了,那可以天差地別。
細(xì)心的小伙伴可能注意到了,定義的注解頭上怎么還有注解。
這就是接下來要講到,敲黑板,注意看!
元注解來幫忙
在定義注解時(shí),會(huì)需要一些元注解。上面出現(xiàn)了兩個(gè),分別是@Target和@Retention.
其中@Target用來定義你的注解將應(yīng)用于什么地方(例如一個(gè)方法或一個(gè)域),@Retention用來定義該注解在哪一個(gè)級(jí)別可用,在源代碼中(「SOURCE」),類文件中(「CLASS」)或者運(yùn)行時(shí)(「RUNTIME」)。Java 提供了四種元注解,如下:
名稱 | 用處 |
---|---|
「@Target」 | 標(biāo)識(shí)該注解可以用于什么地方。其中 ElementType 參數(shù)包括:1. CONSTARUCTOR :構(gòu)造器的聲明2. FIELD :域聲明(包括enum實(shí)例)3. LOCAL_VARIABLE :局部變量聲明4. METHOD :方法聲明5. PACKAGE :包聲明6. TYPE :類、接口(包括注解類型)或enum 聲明 |
「@Retention」 | 表示需要在什么級(jí)別保存該注解信息,其中RetentionPolicy 參數(shù)包括:1. SOURCE :注解將被編譯器丟棄2. CLASS :注解在 class 文件中可用,但會(huì)被 VM 丟棄3. RUNTIME :VM 將在運(yùn)行期也保留注解,因此可以通過反射機(jī)制讀取注解的信息 |
「@Documented」 | 將此注解包含在 JavaDoc 中 |
「@Inherited」 | 允許子類繼承父類的注解 |
注解也分類
我們?cè)谏厦媸纠袆?chuàng)建了一個(gè) @MyAnnotation 注解。看起來很簡(jiǎn)單,沒什么內(nèi)容,因此這種注解我們也稱為 「標(biāo)記注解」,注解也分幾類:
標(biāo)記注解:注解內(nèi)部沒有屬性。使用方式:「@注解名」
//定義 @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation { }
單值注解:注解內(nèi)部只有一個(gè)屬性。使用方式:「@注解名(key = value)」
//定義 @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface SingleValueAnnotation { String name(); } //使用 @SingleValueAnnotation(name = "test") public void singleTest() {}
多值注解:注解內(nèi)部有過個(gè)屬性。使用方式:「@注解名(key = value, key = value, ...)」
//定義 @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface MultiValueAnnotation { String name(); int num(); } //使用 @MultiValueAnnotation(name = "test", num = 1) public void multiTest() {}
值也有默認(rèn)
當(dāng)我們使用的不是標(biāo)記注解時(shí),如果在使用注解的時(shí)候不給注解中的屬性賦上值,那么編譯器就會(huì)報(bào)錯(cuò),提示我們需要賦值。
這樣子是很不方便,有時(shí)候我們并沒有使用到或值是固定的不想重復(fù)寫,那么這個(gè)時(shí)候就需要借助「default」關(guān)鍵字來幫忙我們解決這種問題。
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface MultiValueAnnotation { String name(); int num() default 0; }
我們?cè)趯傩陨鲜褂昧?default 關(guān)鍵字來聲明 num 屬性的默認(rèn)值為 0 ,這樣子我們?cè)谑褂蒙鲜瞿莻€(gè)注解的時(shí)候就可以不用手動(dòng)給num賦值了。
次關(guān)之造器解注
注解具有讓編譯器進(jìn)行編譯檢查的作用,但是如果沒有用來讀取注解的工具,那注解也不會(huì)比注釋更有用,起碼注釋可以讓開發(fā)人員更直觀的看到此段代碼的用處。
重回反射想要?jiǎng)?chuàng)建與使用 「注解處理器」,我們還需要借助反射機(jī)制來構(gòu)造這類工具。以下是簡(jiǎn)單的例子:
public class AnnotationHandle { public static void track(Class<?> c) { for (Method m : c.getDeclaredMethods()) { MultiValueAnnotation annotation = m.getAnnotation(MultiValueAnnotation.class); if (annotation != null) { System.out.println("name:" + annotation.name() + "\n num:" + annotation.num()); } } } public static void main(String[] args) { track(TestService.class); } } /* OUTPUT: name:test num:0 */
在上述例子中我們用到了兩個(gè)反射的方法:getDeclaredMethods()和getAnnotation()。
其中g(shù)etDeclaredMethods() 用來返回該類的所有方法,getAnnotation()用來獲取指定類型的注解對(duì)象。如果方法上沒有該注解則會(huì)返回 「null」 值。
注解元素可用類型
上述@MultiValueAnnotation注解中我們定義了 String類型的 「name」 和 int類型的 「num」,除此之外我們還可以使用其他類型如下:
「基本類型」(「int、float、boolean等」)
「String」
「Class」
「enum」
「Annotation」
「以上類型的數(shù)組」
如果使用了上面以外的其他類型,那么編譯器就會(huì)報(bào)錯(cuò)。而且要注意的是,「也不能使用基本類型的包裝類型」
默認(rèn)值的限制
上述例子中我們也看到了,我們可以在使用注解的時(shí)候給注解屬性賦值,也可以在定義注解的時(shí)候給注解一個(gè)默認(rèn)值,但是這兩者都說明了一件事:「那就是,注解元素不能有不確定的值,要么具有默認(rèn)值,要么在使用注解時(shí)提供元素的值」
基本元素不存在null值,因此對(duì)于非基本類型的元素,無論是在使用中聲明,還是在定義時(shí)聲明, 「都不能將 null 值作為其值」。因此在實(shí)際開發(fā)中,我們往往會(huì)定義一些特殊值作為不存在的標(biāo)識(shí),例如 「負(fù)數(shù)」 或 「空字符串」
三關(guān)之運(yùn)注帷幄
在前面兩關(guān)中,我們學(xué)會(huì)了定義注解和創(chuàng)建注解處理器。接下來我們就要來更加深入掌握注解!
注解也能嵌套
在修飾注解元素的時(shí)候我們看到可以使用Annotation來修飾,估計(jì)看到那的時(shí)候會(huì)覺得有點(diǎn)奇怪。在這里就來為你來揭秘。
先來看一組注解:
@Constraints
@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface Constraints { boolean primaryKey() default false; boolean unique() default false; }
@SQLString
@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface SQLString { String name() default ""; Constraints constraints() default @Constraints; }
我們?cè)贎SQLString注解中使用Constraints注解元素,并將默認(rèn)值設(shè)為@Constraints。這個(gè)時(shí)候Constraints中的值都是@Constraints注解中定義的默認(rèn)值,如果我們要使用自定義的話,做法如下:
@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface SQLString { String name() default ""; Constraints constraints() default @Constraints(primaryKey = true); }
這樣子我們就可以使用自己定義的「value」
注解不支持繼承
我們不能使用extends來繼承某個(gè)@interface,但是可以通過嵌套的方式來解決這一煩惱。
AOP與注解的搭配
「AOP」 在當(dāng)今開發(fā)中我們并不陌生,那么 「AOP」 和 「注解」 能產(chǎn)生什么化學(xué)反應(yīng)呢,請(qǐng)看以下代碼:
@ApiLog:
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD, ElementType.TYPE}) public @interface ApiLog { /** * 接口名稱 */ String name(); }
使用:
@GetMapping(value = "/getConfig") @ApiLog(name = "獲取系統(tǒng)相關(guān)配置") public Result getConfig() throws Exception { return sendOK(SystemService.getConfig(type)); }
Aop使用:
@Aspect @Component public class SysLogAspect { @Autowired private LogService logService; @Pointcut("@annotation(cbuc.life.annotation.ApiLog)") public void logPointCut() { } @Around("logPointCut()") public Object around(ProceedingJoinPoint point) throws Throwable { long beginTime = System.currentTimeMillis(); //執(zhí)行方法 Object result = point.proceed(); //執(zhí)行時(shí)長(zhǎng)(毫秒) long time = System.currentTimeMillis() - beginTime; //保存日志 saveSysLog(point, time); return result; } private void saveSysLog(ProceedingJoinPoint joinPoint, long time) { MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); LogEntity log = new LogEntity(); ApiLog apiLog = method.getAnnotation(ApiLog.class); if(apiLog != null){ //注解上的描述 log.setMethodDescribe(syslog.value()); } //請(qǐng)求的方法名 String className = joinPoint.getTarget().getClass().getName(); String methodName = signature.getName(); log.setMethod(className + "." + methodName + "()"); //請(qǐng)求的參數(shù) Object[] args = joinPoint.getArgs(); String params = JSON.toJSONString(args[0]); log.setParams(params); //獲取request HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); //設(shè)置IP地址 log.setIp(ServletUtil.getIpAddress(request)); //用戶名 String username = LoginInfo.getUsername(); log.setUsername(username); //保存系統(tǒng)日志 logService.save(log); } }
到此,關(guān)于“怎么使用注解”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請(qǐng)繼續(xù)關(guān)注億速云網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)砀鄬?shí)用的文章!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。