在學(xué)習(xí)注解之前敞峭,我首先來講一講學(xué)習(xí)注解的好處殖蚕,不管下面看不看,先打個雞血先宛官。不過確定的是刻坊,在正常 JAVA 開發(fā)中,自己寫注解是比較少的胡控,更多的情況就是使用第三方庫的注解橙困,正因為如此肠缔,大多數(shù)開發(fā)者對于注解僅僅停留在會用的地步槽华。試想一下懂鸵,當(dāng)大多數(shù)人都不會的時候你會,那么你是不是就超越的大部分人夺巩。除此之外续镇,我還總結(jié)學(xué)習(xí)注解的3大好處:
- 能夠讀懂別人寫的代碼,特別是框架相關(guān)的代碼;
- 讓編程更加簡潔,代碼更加清晰;
- 讓別人高看一眼挟阻,裝逼利器;
說完了這些,下面就開始真干貨了俯在。
注解概念
JDK 5中引入了源代碼中的注解(annotation)這一機制愕提。 注解使得Java源代碼中不但可以包含功能性的實現(xiàn)代碼如输,還可以添加元數(shù)據(jù)订歪。 注解的功能類似于代碼中的注釋虑润,所不同的是注解不是提供代碼功能的說明过牙,而是實現(xiàn)程序功能的重要組成部分谦秧。
Java中常見注解
對于常見注解集歇,單單看在開發(fā) JAVA 時 IDE 彈出的那些注解就能了解到焕窝,對于JDK 的注解,我們是要非常熟悉才行。
JDK自帶注解
@Override
@Deprecated
@Suppvisewarnings
常見第三方注解
-
Spring
@Autowired
@Service
@Repository
-
Mybatis
@InsertProvider
@UpdateProvider
@Options
注解的分類
按照運行機制分類
- 源碼注解 (注解只在源碼中存在,編譯成
.class
文件就不存在了) - 編譯時注解 (注解在源碼和
.class
文件中都存在) - 運行時注解 (在運行階段還起作用夹纫,甚至?xí)绊戇\行邏輯的注解,例如
@Autowired
)
按照來源分類
- 來自JDK的注解
- 來自第三方的注解
- 自定義注解
元注解
給注解使用的注解
自定義注解
下面是一個典型的注解聲明院刁,大家先看個大概困鸥,下面會對定義注解的注意事項做個說明:
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Description {
String desc();
String author();
int age() default 18;
}
- 使用
@interface
關(guān)鍵字定義注解 - 成員(變量)以無參數(shù)無異常方式聲明
- 可以用
default
為成員指定一個默認值 - 注解中成員的類型是受限制的姑荷,合法的類型包括基本數(shù)據(jù)類型以及
String
、Class
寨闹、Annotation
和Enumeration
- 如果注解只有一個成員净赴,則成員名應(yīng)該為
value()
,在使用時可以忽略成員名和賦值號(=
) - 注解類可以沒有成員魔吐,沒有成員的注解稱為標(biāo)識注解
- 元注解浮定,用于對注解進行的注解,例如
Description
注解上面的4個注解
常用元注解
Target
標(biāo)識注解的作用域嘿棘,作用域有以下幾種坪郭,幾乎包含了 JAVA 所有的類型:
-
ElementType.CONSTRUCTOR
:構(gòu)造方法 -
ElementType.FIELD
:字段 -
ElementType.LOCAL_VARIABLE
:局部變量 -
ElementType.METHOD
:方法 -
ElementType.PACKAGE
:包 -
ElementType.PARAMETER
: 參數(shù) -
ElementType.TYPE
:類、接口
例如上面例子中標(biāo)識了Description
可以用于對方法和類或接口進行注解:
@Target({ElementType.PARAMETER, ElementType.TYPE})
Retention
標(biāo)識注解的生命周期,有以下3種值:
-
RetentionPolicy.SOURCE
:只在源碼顯示碳默,編譯時會丟棄 -
RetentionPolicy.CLASS
:編譯時會記錄到class
文件中,運行時會忽略 -
RetentionPolicy.RUNTIME
:運行時存在,可以通過反射讀取
例如上面的例子中標(biāo)識了Description
可以記錄到class
文件中,但在運行時會忽略:
@Retention(RetentionPolicy.CLASS)
Inherited
標(biāo)識性元注解芝此,標(biāo)識該注解允許子類進行繼承租副。注意這里可不是注解的繼承秸抚,注解之間也沒有繼承什么的鹿驼。這里指的是如果一個類(不是接口)聲明上使用了這個注解,那么當(dāng)它的一個子類繼承該類時辕宏,也會擁有與父類一樣擁有該注解畜晰。
Documented
標(biāo)識在生成 JAVA DOC 時會包含該注解
使用自定義注解
使用注解的語法:
@<注解名>(<成員名1>=<成員值1>, <成員名1>=<成員值1>, <成員名1>=<成員值1>, ...)
其中的成員名則對應(yīng)了注解里的成員,例子:
@Description(desc="description", author="swifter", age=18)
public String getColor() {
return "red";
}
上面只是個簡單的例子匾效,下面給出一個注解的詳細使用方式舷蟀,這也是在正常開發(fā)中會使用到的方式。對于注解面哼,上面的代碼中已經(jīng)給出了一個聲明野宜,下面就寫兩個類來使用該注解。通過這兩個例子魔策,怎么使用注解就顯而易見了匈子。
第一個類 Person
:
@Description(desc="person interface", author="swifter")
public abstract class Person {
@Description(desc="method getName", author="swifter")
abstract String getName();
abstract void doSomething();
}
第二個類 Child
繼承自 Person
:
public class Child extends Person {
@Override
@Description(desc="child method getName", author="swifter")
public String getName() {
return "get child";
}
@Override
public void doSomething() {
System.out.println("do something in child class");
}
}
解析注解
既然已經(jīng)定義了注解,那么就應(yīng)該考慮如何在代碼中解析這個注解了闯袒。解析注解就是通過反射獲取類虎敦、函數(shù)或成員上的運行時注解信息,從而實現(xiàn)動態(tài)控制程序運行的邏輯政敢。
解析主要方式就是通過反射的途徑其徙,通過Class
對象來獲取注解,并拿到注解的字段再進行操作喷户。例如下面的例子:
Class<Child> clazz = Child.class;
if(clazz.isAnnotationPresent(Description.class)) {
Description description = clazz.getAnnotation(Description.class);
System.out.println(description.desc()+" : "+description.author());
}
Method[] methods = clazz.getMethods();
for(Method method : methods) {
if(method.isAnnotationPresent(Description.class)) {
Description description = method.getAnnotation(Description.class);
System.out.println(description.desc()+" : "+description.author());
}
}
運行之后客戶端會給出如下結(jié)果:
person interface : swifter
child method getName : swifter
其中第一行顯示的是類上面的注解信息唾那,第二行則顯示的是方法上的注解信息。對于這兩個注解的對象褪尝,在代碼中可以看到闹获,都是通過反射的形式拿到的期犬。雖然說反射的方式在效率上比較慢,但是想想注解帶來的優(yōu)點避诽,掌握注解還是有很大必要的龟虎。