我們常用@Override,@SuppressWarnings,@Service等注解,這些都是預(yù)定義的注解。注解就是某種注解類型的一種實(shí)例,我們可以把它用在某個(gè)類上進(jìn)行標(biāo)注。下面這張圖解釋注解都是什么?

上圖可以看出注解大體分為三種:元注解,標(biāo)記注解,一般注解;如果我們實(shí)現(xiàn)自定義的標(biāo)記注解,就需要了解標(biāo)準(zhǔn)元注解和相關(guān)定義注解的語(yǔ)法。元注解的作用就是負(fù)責(zé)注解其他注解。Java5.0定義了4個(gè)標(biāo)準(zhǔn)的meta-annotation類型,它們被用來提供對(duì)其它 annotation類型作說明。Java5.0定義的元注解:
- @Target
- @Retention
- @Document
- @Inherited
@Target
@Target說明了Annotation所修飾的對(duì)象范圍:Annotation可被用于 packages、types(類、接口、枚舉、Annotation類型)、類型成員(方法、構(gòu)造方法、成員變量、枚舉值)、方法參數(shù)和本地變量(如循環(huán)變量、catch參數(shù))。在Annotation類型的聲明中使用了target可更加明晰其修飾的目標(biāo)。取值(ElementType):
- CONSTRUCTOR:用于描述構(gòu)造器
- FIELD:用于描述符
- LOCAL_VARIABLE:用于描述局部變量
- METHOD:用于描述方法
- PACKAGE:用于描述包
- PARAMETER: 用于描述參數(shù)
- TYPE: 用于描述類、接口(包括注解類型)或者enum聲明
@Retention
@Retention定義了該Annotation被保留的時(shí)間長(zhǎng)短:某些Annotation僅出現(xiàn)在源代碼中,而被編譯器丟棄;而另一些卻被編譯在class文件中;編譯在class文件中的Annotation可能會(huì)被虛擬機(jī)忽略,而另一些在class被裝載時(shí)將被讀取(請(qǐng)注意并不影響class的執(zhí)行,因?yàn)锳nnotation與class在使用上是被分離的)。使用這個(gè)meta-Annotation可以對(duì) Annotation的“生命周期”限制。取值(RetentionPoicy):
- SOURCE:在源文件中有效(即源文件保留)
- CLASS:在class文件中有效(即class保留)
- RUNTIME:在運(yùn)行時(shí)有效(即運(yùn)行時(shí)保留)
@Documented
@Documented用于描述其它類型的annotation應(yīng)該被作為被標(biāo)注的程序成員的公共API,因此可以被例如javadoc此類的工具文檔化。Documented 是一個(gè)標(biāo)記注解,沒有成員。
@Inherited
@Inherited 元注解是一個(gè)標(biāo)記注解,@Inherited闡述了某個(gè)被標(biāo)注的類型是被繼承的。如果一個(gè)使用了@Inherited修飾的annotation類型被用于一個(gè)class,則這個(gè)annotation將被用于該class的子類。
注意:@Inherited annotation類型是被標(biāo)注過的class的子類所繼承。類并不從它所實(shí)現(xiàn)的接口繼承annotation,方法并不從它所重載的方法繼承annotation。
當(dāng)@Inherited annotation類型標(biāo)注的annotation的Retention是RetentionPolicy.RUNTIME,則反射API增強(qiáng)了這種繼承性。如果我們使用java.lang.reflect去查詢一個(gè)@Inherited annotation類型的annotation時(shí),反射代碼檢查將展開工作:檢查class和其父類,直到發(fā)現(xiàn)指定的annotation類型被發(fā)現(xiàn),或者到達(dá)類繼承結(jié)構(gòu)的頂層。
自定義注解語(yǔ)法
使用@interface自定義注解時(shí),自動(dòng)繼承了java.lang.annotation.Annotation接口,由編譯程序自動(dòng)完成其他細(xì)節(jié)。在定義注解時(shí),不能繼承其他的注解或接口。@interface用來聲明一個(gè)注解,其中的每一個(gè)方法實(shí)際上是聲明了一個(gè)配置參數(shù)。方法的名稱就是參數(shù)的名稱,返回值類型就是參數(shù)的類型(返回值類型只能是基本類型、Class、String、enum)??梢酝ㄟ^default來聲明參數(shù)的默認(rèn)值。
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* @author chengaoxing
* @date 2019年08月01日
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface PersonAnnotation
{
String name() default "test";
int age();
}
上面是一個(gè)例子,定義了PersonAnnotation的注解,注解類用@interface修飾,有兩個(gè)配置參數(shù)name和age。注解參數(shù)的可支持?jǐn)?shù)據(jù)類型:
- 所有基本數(shù)據(jù)類型(int,float,boolean,byte,double,char,long,short)
- String類型
- Class類型
- enum類型
- Annotation類型
- 以上所有類型的數(shù)組
Annotation類型里面的參數(shù)的設(shè)定:
- 只能用public或默認(rèn)(default)這兩個(gè)訪問權(quán)修飾.例如,String name();這里把方法設(shè)為defaul默認(rèn)類型;
- 參數(shù)成員只能用基本類型byte,short,char,int,long,float,double,boolean八種基本數(shù)據(jù)類型和 String,Enum,Class,annotations等數(shù)據(jù)類型,以及這一些類型的數(shù)組.例如,String name();這里的參數(shù)成員就為String;
- 如果只有一個(gè)參數(shù)成員,最好把參數(shù)名稱設(shè)為"value",后加小括號(hào)
自定義注解使用
可以使用反射獲取類的注解信息,下面是一個(gè)簡(jiǎn)單示例
import org.springframework.core.annotation.AnnotationUtils;
@PersonAnnotation(name = "11", age = 22)
public class TestAnnotation {
public static void print(Class c) {
System.out.println(c.getName());
// java.lang.Class的getAnnotation方法,如果有注解,則返回注解。否則返回null
PersonAnnotation person = AnnotationUtils.findAnnotation(c, PersonAnnotation.class);
if (person != null) {
System.out.println("name:" + person.name() + " age:" + person.age());
} else {
System.out.println("person unknown!");
}
}
public static void main(String[] args) {
TestAnnotation.print(TestAnnotation.class);
}
}
一般的項(xiàng)目中注解的應(yīng)用場(chǎng)景可能是記錄系統(tǒng)日志,登錄驗(yàn)證等地方,結(jié)合spring 的aop 去實(shí)現(xiàn)
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Logined
{
/**
* 其它附加信息
* @return
*/
String[] others() default {};
}
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
/**
* @author chengaoxing on 2019/8/1
*/
@Aspect// 這個(gè)注解表明 使用spring 的aop,需要開啟aop <!--開啟AOP自動(dòng)代理 --><aop:aspectj-autoproxy />
@Component
public class LoginedAspect {
@Around(value = "@annotation(logined)")// 標(biāo)示遇到logined這個(gè)注解的方法進(jìn)行攔截 @Around
public Object checkLogin(ProceedingJoinPoint join, Logined logined) throws Throwable {
// 獲取到使用了注解的方法的參數(shù) 這里面根據(jù)測(cè)試類 是 request,response
Object[] args = join.getArgs();
//獲取使用了注解的方法的 注解的參數(shù) 這里面根據(jù)測(cè)試類 是 username,password
String[] orherString = logined.others();
// 執(zhí)行使用注解方法并返回信息
Object ret = join.proceed();
// 下面是驗(yàn)證登錄身份的邏輯代碼 省略。。。。。。。
...
// 執(zhí)行原方法
Object result = join.proceed();
return result;
}
}
上面的一個(gè)切面通過 @Around(value = "@annotation(logined)")攔截logined注解,在切面執(zhí)行登陸的信息