2021年12月9日 星期四

Java 自製 Annotation (標註、註解) 範例

紀錄了 Java 的 Annotation (標註、註解)  自製範例。

範例的需求為:

  1. 自定一個 Annotation,名為 CustomAnnotation,其可添加在一個簽名為 public String xxx(String somtText) 的 method 上,例如:
    @CustomAnnotation(info="someInfo")
    public printSomthing(String info){
       //////////////
    }
  2. 我們模擬一個可能的框架,其中希望找出有被標上 CustomAnnotation 的 method,讀出 CustomAnnotation 被設定的 info 值,帶進 method 中的 String 參數並執行之,以上例來說就等於執行:

    printSomthing("someInfo")

為了方便的掃描有被標記 annotation 的 class, method,這裡使用了 org.reflections.reflections 的 lib

Maven dependency:

<dependencies>
  	<!-- https://mvnrepository.com/artifact/org.reflections/reflections -->
	<dependency>
	    <groupId>org.reflections</groupId>
	    <artifactId>reflections</artifactId>
	    <version>0.10.2</version>
	</dependency>  	
</dependencies>
首先是檔案結構:


接著來看每一個 Class 的內容:

otherpackage.Class1.java
package otherpackage;

import customannotation.CustomAnnotation;

public class Class1 {
	
	@CustomAnnotation(info = "Class1 info")
	public static void printInfo(String info) {
		System.out.println("Class1 print info: " + info);
	}
}

otherpackageSubpackage.Class2.java:
package otherpackage.subpackage;

import customannotation.CustomAnnotation;

public class Class2 {
	
	@CustomAnnotation
	public static void printInfo(String info) {
		System.out.println("Class2 print info: " + info);
	}
}


Class1.java 和 Class2.java 沒有做什麼特別的事,只是用來測試我們自製 Annotation (@CustomAnnotation) 的 Class 而已,可以注意到,@CustomAnnotation 都加注在了 method 的上面,並且 Class1.java 有設定 info = "Class1 info" 的 info 參數值,而 Class2.java 沒有設定任何值。

CustomAnnotation.java:
package customannotation;

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;

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface CustomAnnotation {
	String info() default "empty info";
}

CustomAnnotation.java 是我們要建立的自製 Annotation,內容非常簡單,只是做了一些基本設定。

main.Test.java:
package main;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Set;

import org.reflections.Reflections;
import org.reflections.scanners.Scanners;
import org.reflections.util.ConfigurationBuilder;

import customannotation.CustomAnnotation;

public class Test {

	public static void main(String[] args) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
		Reflections reflections = new Reflections(new ConfigurationBuilder()
														.forPackage("otherpackage")
														.setScanners(Scanners.MethodsAnnotated)
												 );
		Set<Method> foundMethodSet = reflections.getMethodsAnnotatedWith(CustomAnnotation.class);
		for (Method foundMethod : foundMethodSet) {
			Class<?> classDeclareMethod = foundMethod.getDeclaringClass();
			Object objectInstance = classDeclareMethod.getDeclaredConstructor().newInstance();
			
			CustomAnnotation annotation = foundMethod.getAnnotation(CustomAnnotation.class);
			//foundMethod.invoke(objectInstance, annotation.info()); // for non-static method, nned to new a instance
			foundMethod.invoke(null, annotation.info()); //for static method, don't need to new a instance
			//result :
			//Class1 print info: Class1 info
			//Class2 print info: empty info

		}
	}

}

Test 是主要的 Class,在這裡我們使用了 Reflections.getMethosAnnotatedWith() 找出特定 package 路徑下有被加上  CustomAnnotation 註解的 Method,可以再用 Method.getDeclaringClass() 一併找出 Method 是在哪個 Class 裡,
利 用 Method.getAnnotation() 得到設定在 CustomAnnotation 裡的資料,例如 info 屬性的值,
接著我們利用 Java 的反射 (Reflection) 機制來呼叫 Method ,
如果 Method 是 static 的話可以直接用 invoke 呼叫 ,
如果 Method 不是 static 的話,需要配合建立 Method 所屬的 Class 實體 (Instance)。

最後我們可以看到 Class1 的 method 輸出了 "Class print info: Class1 info",
其中 "Class1 info" 是我們在 Class1 中,設定給 CustomAnnotation 的 info 屬性值。
而 Class2 則輸出了 "Class2 print info: empty info",
其中 "empty info" 是我們為 CustomAnnotatoin 設定的 info 屬性預設值。



參考資料:

沒有留言 :

張貼留言