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>
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 ""; }
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 ""; }
在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; } }
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()"); } }
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()"); } }
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()"); } }
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; } }
#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(); } } }
運行得到:
其他
使用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介面提供的抽象方法(在該介面的實作類別中重寫了這些方法):
功能:傳回該程式元素上存在的、指定類型的註解,如果該類型的註解不存在,則傳回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
功能: 因為java8增加了重複註解功能,因此需要使用該方法獲得修飾該程式元素、指定類型的多個註解。
功能: 因為java8增加了重複註解功能,因此需要使用此方法獲得直接修飾該程式元素、指定類型的多個註解。
Class提供了getMethod()、getField()以及getConstructor()方法(還有其他方法),這些方法分別取得與方法、領域變數以及建構子相關的信息,這些方法傳回Method、Field 以及Constructor型別的物件。
以上是取得指定包下所有自訂註解的詳細內容。更多資訊請關注PHP中文網其他相關文章!