獲取指定包下所有自定義註解並提取註解資訊
Reflections 通過掃描 classpath,索引元資料,允許在執行時查詢這些元資料,也可以儲存收集專案中多個模組的元資料資訊。
使用Reflections快速掃描指定包下自定義的Controller和RequestMapping兩個註解,先去掃描加了@Controller註解的類,接著獲取這些類下面加了@RequestMapping註解的方法,然後通過Java的反射invoke方法去呼叫加了RequestMapping註解的方法並輸出註解上的資訊。
Maven 專案匯入
<dependency> <groupId>org.reflections</groupId> <artifactId>reflections</artifactId> <version>0.9.10</version> </dependency>
- 1
- 2
- 3
- 4
- 5
annotation包下面自定義了兩個註解。
Controller.java:
package annotationTest.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.TYPE)// 註解會在class位元組碼檔案中存在,在執行時可以通過反射獲取到 @Retention(RetentionPolicy.RUNTIME)//定義註解的作用目標**作用範圍欄位、列舉的常量/方法 @Documented//說明該註解將被包含在javadoc中 public @interface Controller { String value() default ""; }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
RequestMapping.java
package annotationTest.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface RequestMapping { String value() default ""; /** * 是否為序列號 * * @return */ boolean id() default false; /** * 欄位名稱 * * @return */ String name() default ""; /** * 欄位描述 * * @return */ String description() default ""; }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
在model包下面定義了一個存放RequestMapping註解方法的物件
ExecutorBean.java
package annotationTest.model;
import java.lang.reflect.Method;
public class ExecutorBean {
private Object object;
private Method method;
public Object getObject() {
return object;
}
public void setObject(Object object) {
this.object = object;
}
public Method getMethod() {
return method;
}
public void setMethod(Method method) {
this.method = method;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
service包下面定義了幾個類,其中有兩個類使用了自定義的Controller註解
SunService.java
package annotationTest.service;
import annotationTest.annotation.Controller;
import annotationTest.annotation.RequestMapping;
@Controller
public class SunService {
@RequestMapping(id = true, name = "test1", description = "sun測試1", value = "/test1")
public void test1() {
System.out.println("SunService->test1()");
}
@RequestMapping(id = true, name = "test2", description = "sun測試2", value = "/test2")
public void test2() {
System.out.println("SunService->test2()");
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
MoonService.java
package annotationTest.service;
import annotationTest.annotation.Controller;
import annotationTest.annotation.RequestMapping;
@Controller
public class MoonService {
@RequestMapping(id = true, name = "moon測試3", description = "/test3", value = "/test3")
public void test3() {
System.out.println("MoonService->test3()");
}
@RequestMapping(id = true, name = "moon測試4", description = "/test4", value = "/test4")
public void test4() {
System.out.println("MoonService->test4()");
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
Stars.java
package annotationTest.service;
import annotationTest.annotation.RequestMapping;
public class Stars {
@RequestMapping(id = true, name = "test1", description = "stars測試1", value = "/test1")
public void test1() {
System.out.println("Stars->test1()");
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
util包下面定義了一個工具類,來對包進行掃描獲取自定義註解的類和方法
AnnoManageUtil.java
package annotationTest.util;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import annotationTest.annotation.Controller;
import annotationTest.annotation.RequestMapping;
import annotationTest.model.ExecutorBean;
import org.reflections.Reflections;
public final class AnnoManageUtil {
/**
* 獲取指定檔案下面的RequestMapping方法儲存在mapp中
*
* @param packageName
* @return
*/
public static Map<String, ExecutorBean> getRequestMappingMethod(String packageName) {
Reflections reflections = new Reflections(packageName);
Set<Class<?>> classesList = reflections.getTypesAnnotatedWith(Controller.class);
// 存放url和ExecutorBean的對應關係
Map<String, ExecutorBean> mapp = new HashMap<String, ExecutorBean>();
for (Class classes : classesList) {
//得到該類下面的所有方法
Method[] methods = classes.getDeclaredMethods();
for (Method method : methods) {
//得到該類下面的RequestMapping註解
RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
if (null != requestMapping) {
ExecutorBean executorBean = new ExecutorBean();
try {
executorBean.setObject(classes.newInstance());
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
executorBean.setMethod(method);
mapp.put(requestMapping.value(), executorBean);
}
}
}
return mapp;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
test包下面是一個測試的類
package annotationTest.test;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import annotationTest.annotation.Controller;
import annotationTest.annotation.RequestMapping;
import annotationTest.model.ExecutorBean;
import annotationTest.util.AnnoManageUtil;
public class Test {
public static void main(String[] args) {
List<Class<?>> classesList = null;
classesList = AnnoManageUtil.getPackageController("annotationTest.service", Controller.class);
Map<String, ExecutorBean> mmap = new HashMap<String, ExecutorBean>();
AnnoManageUtil.getRequestMappingMethod(classesList, mmap);
ExecutorBean bean = mmap.get("/test1");
try {
bean.getMethod().invoke(bean.getObject());
RequestMapping annotation = bean.getMethod().getAnnotation(RequestMapping.class);
System.out.println("註解名稱:" + annotation.name() + "\t註解描述:" + annotation.description());
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
執行得到:
其他
-
使用 Reflections 可以查詢以下元資料資訊:
- 獲得某個型別的所有子型別
- 獲得標記了某個註解的所有型別/成員變數,支援註解引數匹配。
- 使用正則表示式獲得所有匹配的資原始檔
- 獲得所有特定簽名(包括引數,引數註解,返回值)的方法
Reflections 依賴 Google 的 Guava 庫和 Javassist 庫。
-
使用註解修飾了類/方法/成員變數等之後,這些註解不會自己生效,必須由這些註解的開發者提供相應的工具來提取並處理註解資訊(當然,只有當定義註解時使用了@Retention(RetentionPolicy.RUNTIME)修飾,JVM才會在裝載class檔案時提取儲存在class檔案中的註解,該註解才會在執行時可見,這樣我們才能夠解析).
-
Java使用Annotation介面來代表程式元素前面的註解,該介面是所有註解的父介面。
-
java5在java.lang.reflect包下新增了 用AnnotatedElement介面代表程式中可以接受註解的程式元素.
-
AnnotatedElement介面的實現類有:Class(類元素)、Field(類的成員變數元素)、Method(類的方法元素)、Package(包元素),每一個實現類代表了一個可以接受註解的程式元素型別。
-
這樣, 我們只需要獲取到Class、 Method、 Filed等這些實現了AnnotatedElement介面的類的例項,通過該例項物件呼叫該類中的方法(AnnotatedElement介面中抽象方法的重寫) 就可以獲取到我們想要的註解資訊了。
-
獲得Class類的例項有三種方法:
- 利用物件呼叫getClass()方法獲得Class例項
- 利用Class類的靜態的forName()方法,使用類名獲得Class例項
- 運用.class的方式獲得Class例項,如:類名.class
-
AnnotatedElement介面提供的抽象方法(在該介面的實現類中重寫了這些方法):
- <T extends Annotation> T getAnnotation(Class< T> annotationClass)< T extends Annotation>為泛型引數宣告,表明A的型別只能是Annotation型別或者是Annotation的子類。 功能:返回該程式元素上存在的、指定型別的註解,如果該型別的註解不存在,則返回null
- Annotation[] getAnnotations() 功能:返回此元素上存在的所有註解,包括沒有顯示定義在該元素上的註解(繼承得到的)。(如果此元素沒有註釋,則返回長度為零的陣列。)
- < T extends Annotation> T getDeclaredAnnotation(Class < T> annotationClass) 功能:這是Java8新增的方法,該方法返回直接修飾該程式元素、指定型別的註解(忽略繼承的註解)。如果該型別的註解不存在,返回null.
- Annotation[] getDeclaredAnnotations() 功能:返回直接存在於此元素上的所有註解,該方法將忽略繼承的註釋。(如果沒有註釋直接存在於此元素上,則返回長度為零的一個數組。)
- boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) 功能:判斷該程式元素上是否存在指定型別的註解,如果存在則返回true,否則返回false。
- <T extends Annotation> T[] getAnnotationsByTpye(Class<T> annotationClass) 功能: 因為java8增加了重複註解功能,因此需要使用該方法獲得修飾該程式元素、指定型別的多個註解。
- <T extends Annotation> T[] getDeclaredAnnotationsByTpye(Class<T>annotationClass) 功能: 因為java8增加了重複註解功能,因此需要使用該方法獲得直接修飾該程式元素、指定型別的多個註解。
Class提供了getMethod()、getField()以及getConstructor()方法(還有其他方法),這些方法分別獲取與方法、域變數以及建構函式相關的資訊,這些方法返回Method、Field 以及Constructor型別的物件。