先了解幾個元注解
分别是@Target, @Retention,@Documented,@Inherited
@Target:決定自定義的注解用在什麼地方
屬性值 | 作用 |
---|---|
ElementType.CONSTRUCTOR | 用在構造函數的聲明上 |
ElementType.FIELD | 定義在成員變量上 |
LOCAL_VARIABLE | 局部變量的聲明 |
METHOD | 定義在方法上面 |
PACKAGE | 包的聲明 |
PARAMETER | 用在參數的聲明上 |
TYPE | 類、接口、枚舉的聲明 |
比如我們常用的@Override的定義
@Target(ElementType.METHOD) //聲明該注解用于方法上
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
值得一提,和@RequestMapping類似,@Target(ElementType.METHOD)等同于@Target(value=ElementType.METHOD)
@Retention
這個注解的保留級别
屬性值 | 作用 |
---|---|
RetentionPolicy.SOURCE | 注解将被編譯器丢棄 |
RetentionPolicy.CLASS | 注解在class檔案中可用, 但會被VM丢棄 |
RetentionPolicy.RUNTIME | VM将運作期也保留注解資訊,是以可用通過反射機制來讀取注解的資訊 |
我們之前使用注解做過一個AOP的參數合法性校驗,使用的自定義注解就是RUNTIME,因為要通過反射擷取參數值
而Class的取值可用,則有點類似于lombok的@Data注解,主要用來在class檔案中生成代碼
@Documented @Inherited
這兩個注解比較簡單,@Documented 的主要作用是用來生成注釋,表示需要在什麼級别儲存該注釋資訊,用于描述注解的生命周期(即:被描述的注解在什麼範圍内有效)
@Inhrited 是一個标記注解,@Inherited闡述了某個被标注的類型是被繼承的。如果一個使用了@Inherited修飾的annotation類型被用于一個class,則這個annotation将被用于該class的子類。
注解實作類
一般有兩種方法,一種是通過AOP,攔截所有擁有某注解的類/方法/枚舉…,另一種就是apt,反射使用太多比較消耗性能,但APT好像才安卓中用比較多,這裡隻是簡單描述。
AOP實作自定義注解處理器
aop切面中定義五種切面方法
前置通知:@Before 在目标業務方法執行之前執行
後置通知:@After 在目标業務方法執行之後執行
傳回通知:@AfterReturning 在目标業務方法傳回結果之後執行
異常通知:@AfterThrowing 在目标業務方法抛出異常之後
環繞通知:@Around 功能強大,可代替以上四種通知,還可以控制目标業務方法是否執行以及何時執行
寫一個擷取方法參數并将參數進行加一操作的小栗子。
定義注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ArgsFilter {
String[] rules() default {};
}
定義切面處理類,确定切點位置(下面例子中切點和具體執行方法分開處理)
@Aspect
@Component
public class ArgsFiletImpl {
//等價于 @Pointcut("[email protected](argsFilter)")
//定義哪些方法會被切面攔截,這裡定義的是ArgsFilter注解使用的地方
@Pointcut("@annotation(argsFilter)")
public void checkAnnoation(ArgsFilter argsFilter){
}
切點Pointcut中的參數
由下列方式來定義或者通過 &&、 ||、 !、 的方式進行組合:
execution:用于比對方法執行的連接配接點;
within:用于比對指定類型内的方法執行;
this:用于比對目前AOP代理對象類型的執行方法;注意是AOP代理對象的類型比對,這樣就可能包括引入接口也類型比對;
target:用于比對目前目标對象類型的執行方法;注意是目标對象的類型比對,這樣就不包括引入接口也類型比對;
args:用于比對目前執行的方法傳入的參數為指定類型的執行方法;
@within:用于比對是以持有指定注解類型内的方法;
@target:用于比對目前目标對象類型的執行方法,其中目标對象持有指定的注解;
@args:用于比對目前執行的方法傳入的參數持有指定注解的執行;
@annotation:用于比對目前執行方法持有指定注解的方法
具體處理方法
@Around(value = "auditLogPointcut(auditLog)")
//ProceedingJoinPoint僅用于around方法中,新增兩個方法 執行目标方法和新增參數執行目标方法
public Object around(ProceedingJoinPoint joinPoint, AuditLog auditLog) throws Throwable {
//擷取請求對象
HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();
Map<String, String[]> parameterMap = request.getParameterMap();
StringBuffer requestURL = request.getRequestURL();
MethodSignature signature = (MethodSignature)joinPoint.getSignature(); // 代理對象
Method targetMethod = signature.getMethod(); //擷取參數所在方法
final Class declaringType = signature.getDeclaringType();//擷取聲明所在類
String declaringTypeName = signature.getDeclaringTypeName();//類名
Object object=joinPoint.proceed();//執行目标方法
return object;
}
ProceedingJoinPoint對象
roceedingJoinPoint對象是JoinPoint的子接口,該對象隻用在@Around的切面方法中,
apt介紹
apt(annotation processing tool) 注解處理工具,就是操作Java源檔案,當處理完源檔案後編譯它們,在系統建立的過程中會自動建立一些新的源檔案,這些新檔案會在新的一輪中的注解處理器中接受檢查,直到不再有新的源檔案産生為止。這個過程中是發生在編譯期間(compile time),而非運作期間,
我們可以使用ProcessingEnvironment擷取一些實用類以及擷取選項參數等:
public interface ProcessingEnvironment {
Map<String, String> getOptions(); //傳回指定的參數選項
Messager getMessager(); //傳回實作Messager接口的對象,用于報告錯誤資訊、警告提醒
Filer getFiler(); //傳回實作Filer接口的對象,用于建立檔案、類和輔助檔案
Elements getElementUtils(); //傳回實作Elements接口的對象,用于操作元素的工具類
Types getTypeUtils(); //傳回實作Types接口的對象,用于操作類型的工具類
SourceVersion getSourceVersion();
Locale getLocale();
}
apt一般是需要繼承AbstractProcessor 類
public class ArgsFiletImpl extends AbstractProcessor {
protected ArgsFiletImpl() {
super();
}
//指定該處理器是工作于哪個注解的
@Override
public Set<String> getSupportedAnnotationTypes() {
return Collections.singleton(ArgsFilter.class.getCanonicalName());
}
@Override
public SourceVersion getSupportedSourceVersion() {//指定目前jdk版本
return SourceVersion.latestSupported();
}
//初始化方法
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
}
//核心方法
//這些注解是否由此 Processor 處理,該方法傳回ture表示該注解已經被處理, 後續不會再有其他處理器處理; 傳回false表示仍可被其他處理器處理
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
return false;
}
}