java中注解的使用

點(diǎn)擊上方「藍(lán)字」關(guān)注我們

0x01. 說在前面
使用注解開發(fā)的好處
1.?使代碼更加干凈易讀,易于維護(hù)修改。比如,以前使用spring的開發(fā),都是基于xml文件實(shí)現(xiàn)了統(tǒng)一的配置管理,但是缺點(diǎn)也是顯而易見的,就是隨著項(xiàng)目的越來越大,xml文件會(huì)變得越來越復(fù)雜,維護(hù)成本也會(huì)越來越高。使用注解就可以提供更大的便捷性,易于維護(hù)修改。
2. 可以實(shí)現(xiàn)代碼的類型檢查,特別是在編譯器的角度實(shí)現(xiàn)一些類型檢查,比如預(yù)檢查(@Override)和抑制警告(@SuppressWarnings)等。
3. 自定義注解,作為額外信息的載體,存儲(chǔ)有關(guān)程序的額外信息
0x02.?注解的分類以及使用
Java注解是附加在代碼中的一些元信息,用于編譯和運(yùn)行時(shí)進(jìn)行解析和使用,起到說明、配置的功能。
注解不會(huì)影響代碼的實(shí)際邏輯,僅僅起到輔助性的作用。包含在java.lang.annotation包中。注解的定義類似于接口的定義,使用@interface來定義,定義一個(gè)方法即為注解類型定義了一個(gè)元素,方法的聲明不允許有參數(shù)或throw語句,返回值類型被限定為原始數(shù)據(jù)類型、字符串String、Class、enums、注解類型,或前面這些的數(shù)組,方法可以有默認(rèn)值。注解并不直接影響代碼的語義,但是他可以被看做是程序的工具或者類庫。它會(huì)反過來對(duì)正在運(yùn)行的程序語義有所影響。注解可以從源文件、class文件或者在運(yùn)行時(shí)通過反射機(jī)制多種方式被讀取。
一般來說,注解一般分為三種類型:元注解,標(biāo)準(zhǔn)注解,自定義注解
2.1 元注解
元注解是專職負(fù)責(zé)注解其他注解,主要是標(biāo)明該注解的使用范圍,生效范圍。我們是不能改變它的,只能用它來定義我們自定義的注解。
包括 @Retention @Target @Document @Inherited四種。(java.lang.annotation中提供,為注釋類型)。
| 注解 | 說明 |
|---|---|
| @Target | 定義注解的作用目標(biāo),也就是可以定義注解具體作用在類上,方法上,還是變量上 |
| @Retention | 定義注解的保留策略。RetentionPolicy.SOURCE:注解僅存在于源碼中,在class字節(jié)碼文件中不包含;RetentionPolicy.CLASS:默認(rèn)的保留策略,注解會(huì)在class字節(jié)碼文件中存在,但運(yùn)行時(shí)無法獲得;RetentionPolicy.RUNTIME:注解會(huì)在class字節(jié)碼文件中存在,在運(yùn)行時(shí)可以通過反射獲取到。 |
| @Document | 說明該注解將被包含在javadoc中 |
| @Inherited | 說明子類可以繼承父類中的該注解 |
Target類型主要依賴于ElementType這個(gè)類型,具體的類型如下:
| Target類型 | 說明 |
|---|---|
| ElementType.TYPE | 接口、類、枚舉、注解 |
| ElementType.FIELD | 字段、枚舉的常量 |
| ElementType.METHOD | 方法 |
| ElementType.PARAMETER | 方法參數(shù) |
| ElementType.CONSTRUCTOR | 構(gòu)造函數(shù) |
| ElementType.LOCAL_VARIABLE | 局部變量 |
| ElementType.ANNOTATION_TYPE | 注解 |
| ElementType.PACKAGE | 包 |
2.2 標(biāo)準(zhǔn)注解
Java標(biāo)準(zhǔn)注解提供了三個(gè),定義在java.lang中的注解,我認(rèn)為這三個(gè)注解的作用更多的是一種注釋
@Override 表示當(dāng)前方法覆蓋父類中的方法。
@Deprecated 標(biāo)記一個(gè)元素為已過期,避免使用
支持的元素類型為:CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE
@SuppressWarnings 不輸出對(duì)應(yīng)的編譯警告
一個(gè)簡(jiǎn)單的使用demo:
@SuppressWarnings(value?=?{"unused",?"rawtypes"})
public?class?Children??extends?Parent{
????@Override
????public?void?work()?{
????????System.out.println("我是一個(gè)被重寫的方法");
????}
????@Deprecated
????public?void?play(){
????????System.out.println("這個(gè)方法不推薦使用了");
????}
}
2.3 自定義注解實(shí)現(xiàn)一個(gè)sql語句的拼接
需要注意的方面:注解的定義類似于接口的定義,使用@interface來定義,定義一個(gè)方法即為注解類型定義了一個(gè)元素,方法的聲明不允許有參數(shù)或throw語句,返回值類型被限定為原始數(shù)據(jù)類型、字符串String、Class、enums、注解類型,或前面這些的數(shù)組,方法可以有默認(rèn)值。
自定義注解一般可以分為三步:定義注解,使用注解,讀取注解
定義注解
@Target(ElementType.TYPE)?//注解加載類上
@Retention(RetentionPolicy.RUNTIME)?//?運(yùn)行時(shí)讀取注解
public?@interface?Table?{
????String?value();?
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public@interface?UserFiled?{
????String?name();
????String?type();
????int?length();
}
使用注解
//?將自定義的注解加在用戶上,實(shí)現(xiàn)一個(gè)表的映射
@Table(value?=?"user_table")
public?class?User?{
????@UserFiled(name?=?"user_id",type?=?"int",length?=?8)
????private?int?userId;
????@UserFiled(name?=?"user_name",type?=?"varchar",length?=?16)
????private?String?userName;
????@UserFiled(name?=?"password",type?=?"varchar",length?=?16)
????private?String?password;
????public?int?getUserId()?{
????????return?userId;
????}
????public?void?setUserId(int?userId)?{
????????this.userId?=?userId;
????}
????public?String?getUserName()?{
????????return?userName;
????}
????public?void?setUserName(String?userName)?{
????????this.userName?=?userName;
????}
????public?String?getPassword()?{
????????return?password;
????}
????public?void?setPassword(String?password)?{
????????this.password?=?password;
????}
}
讀取注解的內(nèi)容
/**
?*?讀取注解中的值
?*/
public?class?GetUser?{
????public?static?void?main(String[]?args)?throws?ClassNotFoundException,?NoSuchFieldException?{
????????Class?userClass?=?Class.forName("annocation.blog.User");
????????//?讀取類上的注解
????????Table?table?=?(Table)?userClass.getAnnotation(Table.class);
????????System.out.println(table.value());
????????//?讀取屬性上注解
????????Field?userId?=?userClass.getDeclaredField("userId");
????????UserFiled?userFiledId?=?userId.getAnnotation(UserFiled.class);
????????System.out.println(userFiledId.length()?+?"----"?+?userFiledId.type()?+?"-----"?+?userFiledId.name());
????????Field?userName?=?userClass.getDeclaredField("userName");
????????UserFiled?userFiledName?=?userName.getAnnotation(UserFiled.class);
????????System.out.println(userFiledName.length()+"----"+userFiledName.type()+"----"+userFiledName.name());
????????Field?password?=?userClass.getDeclaredField("password");
????????UserFiled?userFiledPassword?=?password.getAnnotation(UserFiled.class);
????????System.out.println(userFiledPassword.name()?+?"-----"?+?userFiledPassword.type()?+?"-----"?+?userFiledPassword.length());
????????//?拼接一個(gè)sql語句
????????String?name?=?"chenwei";
????????String?sql?="select?*?from"?+?table.value()+"where"+userFiledName.name()+"="+name;
????}
}
結(jié)果:
user_table
user_id----int-----8
user_name----varchar----16
password-----varchar-----16
自定義注解的實(shí)現(xiàn)過程:
1.?定義注解
2.?使用注解,根據(jù)自己定義的注解來達(dá)到一些目的,本例中,就是使用注解來完成數(shù)據(jù)庫表和實(shí)體類的映射關(guān)系
3.?讀取注解的內(nèi)容,也是比較重要的一部分,核心還是利用了反射的思想,得到使用注解的這個(gè)類,根據(jù)類中的getAnnotion的方法得到定義的注解,獲得注解上的值。
0x03. 注解的實(shí)現(xiàn)的原理
注解的實(shí)現(xiàn)的原理很大的一部分是基于反射實(shí)現(xiàn)。
反射可以獲取到Class對(duì)象,進(jìn)而獲取到Constructor、Field、Method等實(shí)例,點(diǎn)開源碼結(jié)構(gòu)發(fā)現(xiàn)Class、Constructor、Field、Method等均實(shí)現(xiàn)了AnnotatedElement接口,AnnotatedElement接口的方法如下
//?判斷該元素是否包含指定注解,包含則返回true
boolean?isAnnotationPresent(Class?extends?Annotation>?annotationClass)
//?返回該元素上對(duì)應(yīng)的注解,如果沒有返回null
?T?getAnnotation(Class?annotationClass);
//?返回該元素上的所有注解,如果沒有任何注解則返回一個(gè)空數(shù)組
Annotation[]?getAnnotations();
//?返回指定類型的注解,如果沒有返回空數(shù)組
T[]?getAnnotationsByType(Class?annotationClass)
//?返回指定類型的注解,如果沒有返回空數(shù)組,只包含直接標(biāo)注的注解,不包含inherited的注解
T?getDeclaredAnnotation(Class?annotationClass)
//?返回指定類型的注解,如果沒有返回空數(shù)組,只包含直接標(biāo)注的注解,不包含inherited的注解
T[]?getDeclaredAnnotationsByType
//?返回該元素上的所有注解,如果沒有任何注解則返回一個(gè)空數(shù)組,只包含直接標(biāo)注的注解,不包含inherited的注解
Annotation[]?getDeclaredAnnotations();
通過一個(gè)實(shí)例再次說明一下注解的使用過程:
定義注解
@Documented
@Target({ElementType.TYPE,?ElementType.FIELD,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public?@interface?MyAnnotaion?{
????String?getValue()?default?"this?is?myAnntaion";
????int?order()?default?0;
}
使用注解
@MyAnnotaion(getValue?=?"annotation?on?class")
public?class?Demo?{
????@MyAnnotaion(getValue?=?"annotation?on?filed")
????public?String?name;
????@MyAnnotaion(getValue?=?"annotation?on?method")
????public?void?hello(){
????}
????@MyAnnotaion
????public?void?defaultMethod(){
????}
}
利用反射讀取注解中的值。
public?class?TestDemo?{
????public?static?void?main(String[]?args)?throws?NoSuchFieldException,?NoSuchMethodException?{
????????/**
?????????*?獲取類上的注解
?????????*/
????????Class?demoClass?=?Demo.class;
????????Annotation[]?annotaion?=?demoClass.getAnnotations();
????????printAnnotation(annotaion);
????????/**
?????????*?讀取成員變量上的注解
?????????*/
????????Field?name?=?demoClass.getField("name");
????????Annotation[]?getOnFiled?=?name.getAnnotations();
????????printAnnotation(getOnFiled);
????????/**
?????????*?讀取方法上的注解
?????????*/
????????Method?hello?=?demoClass.getMethod("hello",?null);
????????MyAnnotaion?onMethod?=?hello.getAnnotation(MyAnnotaion.class);
????????System.out.println(onMethod.getValue());
????????/**
?????????*?獲取默認(rèn)方法上的注解
?????????*/
????????Method?defaultMethod?=?demoClass.getMethod("defaultMethod",?null);
????????MyAnnotaion?onDefaultMethod?=?defaultMethod.getAnnotation(MyAnnotaion.class);
????????System.out.println(onDefaultMethod.getValue());
????}
????public?static?void?printAnnotation(Annotation...?annotations)?{
????????for?(Annotation?annotation?:?annotations)?{
????????????System.out.println(annotation);
????????}
????}
}
運(yùn)行結(jié)果
@annocation.MyAnnotaion(getValue=annotation?on?class,?order=0)
@annocation.MyAnnotaion(getValue=annotation?on?filed,?order=0)
annotation?on?method
this?is?myAnntaion
掃碼二維碼
獲取更多精彩
Java樂園

