一、什么是注解?
對於很多初次接觸的開發者來說應該都有這個疑問?Annontation是Java5開始引入的新特征,中文名稱叫注解。它提供了一種安全的類似注釋的機制,用來將任何的信息或元數據(metadata)與程序元素(類、方法、成員變量等)進行關聯。為程序的元素(類、方法、成員變量)加上更直觀更明了的說明,這些說明信息是與程序的業務邏輯無關,並且供指定的工具或框架使用。Annontation像一種修飾符一樣,應用於包、類型、構造方法、方法、成員變量、參數及本地變量的聲明語句中。
Java注解是附加在代碼中的一些元信息,用於一些工具在編譯、運行時進行解析和使用,起到說明、配置的功能。注解不會也不能影響代碼的實際邏輯,僅僅起到輔助性的作用。包含在 java.lang.annotation 包中。
二、注解的用處:
1、生成文檔。這是最常見的,也是java 最早提供的注解。常用的有@param @return 等
2、跟蹤代碼依賴性,實現替代配置文件功能。比如Dagger 2 依賴注入,未來java 開發,將大量注解配置,具有很大用處;
3、在編譯時進行格式檢查。如@override 放在方法前,如果你這個方法並不是覆蓋了超類方法,則編譯時就能檢查出。
三、注解的原理:
注解本質是一個繼承了Annotation 的特殊接口,其具體實現類是Java 運行時生成的動態代理類。而我們通過反射獲取注解時,返回的是Java 運行時生成的動態代理對象$Proxy1。通過代理對象調用自定義注解(接口)的方法,會最終調用AnnotationInvocationHandler 的invoke 方法。該方法會從memberValues 這個Map 中索引出對應的值。而memberValues 的來源是Java 常量池。
四、元注解:
java.lang.annotation 提供了四種元注解,專門注解其他的注解(在自定義注解的時候,需要使用到元注解):
@Documented – 注解是否將包含在JavaDoc中
@Retention – 什么時候使用該注解
@Target – 注解用於什么地方
@Inherited – 是否允許子類繼承該注解
1.)@Retention – 定義該注解的生命周期
● RetentionPolicy.SOURCE : 在編譯階段丟棄。這些注解在編譯結束之后就不再有任何意義,所以它們不會寫入字節碼。@Override, @SuppressWarnings都屬於這類注解。
● RetentionPolicy.CLASS : 在類加載的時候丟棄。在字節碼文件的處理中有用。注解默認使用這種方式
● RetentionPolicy.RUNTIME : 始終不會丟棄,運行期也保留該注解,因此可以使用反射機制讀取該注解的信息。我們自定義的注解通常使用這種方式。
2.)Target – 表示該注解用於什么地方。默認值為任何元素,表示該注解用於什么地方。可用的ElementType 參數包括
● ElementType.CONSTRUCTOR: 用於描述構造器
● ElementType.FIELD: 成員變量、對象、屬性(包括enum實例)
● ElementType.LOCAL_VARIABLE: 用於描述局部變量
● ElementType.METHOD: 用於描述方法
● ElementType.PACKAGE: 用於描述包
● ElementType.PARAMETER: 用於描述參數
● ElementType.TYPE: 用於描述類、接口(包括注解類型) 或enum聲明
3.)@Documented – 一個簡單的Annotations 標記注解,表示是否將注解信息添加在java 文檔中。
4.)@Inherited – 定義該注釋和子類的關系
@Inherited 元注解是一個標記注解,@Inherited 闡述了某個被標注的類型是被繼承的。如果一個使用了@Inherited 修飾的annotation 類型被用於一個class,則這個annotation 將被用於該class 的子類。
五、常見標准的Annotation:
1.)Override
java.lang.Override 是一個標記類型注解,它被用作標注方法。它說明了被標注的方法重載了父類的方法,起到了斷言的作用。如果我們使用了這種注解在一個沒有覆蓋父類方法的方法時,java 編譯器將以一個編譯錯誤來警示。
2.)Deprecated
Deprecated 也是一種標記類型注解。當一個類型或者類型成員使用@Deprecated 修飾的話,編譯器將不鼓勵使用這個被標注的程序元素。所以使用這種修飾具有一定的“延續性”:如果我們在代碼中通過繼承或者覆蓋的方式使用了這個過時的類型或者成員,雖然繼承或者覆蓋后的類型或者成員並不是被聲明為@Deprecated,但編譯器仍然要報警。
3.)SuppressWarnings
SuppressWarning 不是一個標記類型注解。它有一個類型為String[] 的成員,這個成員的值為被禁止的警告名。對於javac 編譯器來講,被-Xlint 選項有效的警告名也同樣對@SuppressWarings 有效,同時編譯器忽略掉無法識別的警告名。
@SuppressWarnings("unchecked")
六、自定義注解:
自定義注解類編寫的一些規則:
1. Annotation 型定義為@interface, 所有的Annotation 會自動繼承java.lang.Annotation這一接口,並且不能再去繼承別的類或是接口.
2. 參數成員只能用public 或默認(default) 這兩個訪問權修飾
3. 參數成員只能用基本類型byte、short、char、int、long、float、double、boolean八種基本數據類型和String、Enum、Class、annotations等數據類型,以及這一些類型的數組.
4. 要獲取類方法和字段的注解信息,必須通過Java的反射技術來獲取 Annotation 對象,因為你除此之外沒有別的獲取注解對象的方法
5. 注解也可以沒有定義成員,,不過這樣注解就沒啥用了
PS:自定義注解需要使用到元注解
package com.wxy.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 定義注解Test * 注解中含有兩個元素 id 和 description * description元素有默認值"no description" * @create-time 2011-8-12 下午02:22:28 * @revision $Id */ //該注解用於方法聲明 @Target(ElementType.METHOD) //VM將在運行期也保留注釋,因此可以通過反射機制讀取注解的信息 @Retention(RetentionPolicy.RUNTIME) //將此注解包含在javadoc中 @Documented //允許子類繼承父類中的注解 @Inherited public @interface Test { public int id(); public String description() default "no description"; }
package com.wxy.annotation; import java.lang.reflect.Method; /** * 使用注解和解析注解 * * @creator xiaoyu.wang * @create-time 2011-8-12 下午03:49:17 * @revision $Id */ public class Test_1{ /** * 被注釋的三個方法 */ @Test(id = 1, description = "hello method1") public void method1() { } @Test(id = 2) public void method2() { } @Test(id = 3, description = "last method3") /** * 解析注釋,將Test_1類所有被注解方法的信息打印出來 * @param args */ public static void main(String[] args) { Method[] methods = Test_1.class.getDeclaredMethods(); for (Method method : methods) { //判斷方法中是否有指定注解類型的注解 boolean hasAnnotation = method.isAnnotationPresent(Test.class); if (hasAnnotation) { //根據注解類型返回方法的指定類型注解 Test annotation = method.getAnnotation(Test.class); System.out.println("Test(method=" + method.getName() + ",id=" + annotation.id() + ",description=" + annotation.description() + ")"); } } } }
六、spring中注解@Resource源碼分析
package com.wxy.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target( { ElementType.FIELD, ElementType.METHOD }) public @interface WxyResource { String name() default ""; }
public class PeopleServiceBean implements PeopleService { private PeopleDao peopleDao; private String name = "wxy"; private Integer id = 1; /** * @param peopleDao the peopleDao to set */ @WxyResource public void setPeopleDao(PeopleDao peopleDao) { this.peopleDao = peopleDao; } … }
public class WxyClassPathXMLApplicationContext { //存放BeanDefinition的列表,在beans.xml中定義的bean可能不止一個 private final List<BeanDefinition> beanDefines = new ArrayList<BeanDefinition>(); //將類名作為索引,將創建的Bean對象存入到Map中 private final Map<String, Object> sigletons = new HashMap<String, Object>(); public WxyClassPathXMLApplicationContext(String fileName) { //讀取xml配置文件 this.readXML(fileName); //實例化bean this.instanceBeans(); //處理注解方式 this.annotationInject(); //注入對象 this.injectObject(); } /** * 使用注解方式注入對象方法實現 * @throws IntrospectionException */ private void annotationInject() { //循環所有bean對象 for (String beanName : sigletons.keySet()) { //獲取bean對象 Object bean = sigletons.get(beanName); //如果bean不為空,取得bean的屬性 if (bean != null) { try { //按屬性注入 PropertyDescriptor[] ps = Introspector.getBeanInfo(bean.getClass()) .getPropertyDescriptors(); for (PropertyDescriptor properdesc : ps) { //獲取屬性的setter方法 Method setter = properdesc.getWriteMethod(); //判斷注解是否存在 if (setter != null && setter.isAnnotationPresent(WxyResource.class)) { //取得注解 WxyResource resource = setter.getAnnotation(WxyResource.class); Object value = null; //如果按名字找到 if (resource.name() != null && !"".equals(resource.name())) { //取得容器中的bean對象 value = sigletons.get(resource.name()); } else {//沒有按名字找到,按類型尋找 //取得容器中的bean對象 value = sigletons.get(resource.name()); if (value == null) { for (String key : sigletons.keySet()) { if (properdesc.getPropertyType().isAssignableFrom( sigletons.get(key).getClass())) { value = sigletons.get(key); break; } } } } //把引用對象注入到屬性 setter.setAccessible(true); setter.invoke(bean, value); } } //按字段注入 Field[] fields = bean.getClass().getDeclaredFields(); for (Field field : fields) { //如果注解存在 if (field.isAnnotationPresent(WxyResource.class)) { //取得注解 WxyResource resource = field.getAnnotation(WxyResource.class); Object value = null; //如果按名字找到 if (resource.name() != null && !"".equals(resource.name())) { //取得容器中的bean對象 value = sigletons.get(resource.name()); } else {//沒有按名字找到,按類型尋找 //取得容器中的bean對象 value = sigletons.get(field.getName()); if (value == null) { for (String key : sigletons.keySet()) { if (field.getType().isAssignableFrom( sigletons.get(key).getClass())) { value = sigletons.get(key); break; } } } } //允許訪問private field.setAccessible(true); field.set(bean, value); } } } catch (IntrospectionException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalArgumentException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InvocationTargetException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } /** *為bean對象的屬性注入值 */ private void injectObject() { …. } …. }
public class MyTest { /** * @param args */ public static void main(String[] args) { //MyIOC容器實例化 WxyClassPathXMLApplicationContext ac = new WxyClassPathXMLApplicationContext("beans.xml"); PeopleService peopleService = (PeopleService) ac.getBean("peopleService"); peopleService.save(); } }
注解代表的是某種業務意義,注解背后處理器的工作原理如上源碼實現:首先解析所有屬性,判斷屬性上是否存在指定注解,如果存在則根據搜索規則取得bean,然后利用反射原理注入。如果標注在字段上面,也可以通過字段的反射技術取得注解,根據搜索規則取得bean,然后利用反射技術注入。
以上內容作為筆記轉自
https://www.cnblogs.com/acm-bingzi/p/javaAnnotation.html
https://www.cnblogs.com/fanheyan/articles/5691659.html
后續自己封裝框架的業務操作日志,會借助自定義注解來封裝,如有時間,會繼續分享封裝的思想。