本文主要分為三部分:
一、注解的基本概念和原理及其簡單實用
二、Spring中如何使用注解
三、編碼剖析spring@Resource的實現原理
本文轉自:http://freewxy.iteye.com/blog/1149128/
一、注解的基本概念和原理及其簡單實用
注解(Annotation) 提供了一種安全的類似注釋的機制,為我們在代碼中添加信息提供了一種形式化得方法,使我們可以在稍后某個時刻方便的使用這些數據(通過解析注解來使用這些 數據),用來將任何的信息或者元數據與程序元素(類、方法、成員變量等)進行關聯。其實就是更加直觀更加明了的說明,這些說明信息與程序業務邏輯沒有關 系,並且是供指定的工具或框架使用的。Annotation像一種修飾符一樣,應用於包、類型、構造方法、方法、成員變量、參數及本地變量的申明語句中。
Annotation其實是一種接口。通過java的反射機制相關的API來訪問Annotation信息。相關類(框架或工具中的類)根據這些信息來決定如何使用該程序元素或改變它們的行為。Java語言解釋器在工作時會忽略這些Annotation,因此在JVM中這些Annotation是“不起作用”的,只能通過配套的工具才能對這些Annotation類型的信息進行訪問和處理。
Annotation和interface的異同:
1、 annotition的類型使用關鍵字@interface而不是interface。它繼承了java.lang.annotition.Annotition接口,並非申明了一個interface。
2、 Annotation類型、方法定義是獨特的、受限制的。Annotation類型的方法必須申明為無參數、無異常拋出的。這些方法定義了Annotation的成員:方法名稱為了成員名,而方法返回值稱為了成員的類型。而方法返回值必須為primitive類型、Class類型、枚舉類型、Annotation類型或者由前面類型之一作為元素的一位數組。方法的后面可以使用default和一個默認數值來申明成員的默認值,null不能作為成員的默認值,這與我們在非Annotation類型中定義方法有很大不同。Annotation類型和他的方法不能使用Annotation類型的參數,成員不能是generic。只有返回值類型是Class的方法可以在Annotation類型中使用generic,因為此方法能夠用類轉換將各種類型轉換為Class。
3、 Annotation類型又與接口有着近似之處。它們可以定義常量、靜態成員類型(比如枚舉類型定義)。Annotation類型也可以如接口一般被實現或者繼承。
*元注解@Target,@Retention,@Documented,@Inherited
*
* @Target 表示該注解用於什么地方,可能的 ElemenetType 參數包括:
* ElemenetType.CONSTRUCTOR 構造器聲明
* ElemenetType.FIELD 域聲明(包括 enum 實例)
* ElemenetType.LOCAL_VARIABLE 局部變量聲明
* ElemenetType.METHOD 方法聲明
* ElemenetType.PACKAGE 包聲明
* ElemenetType.PARAMETER 參數聲明
* ElemenetType.TYPE 類,接口(包括注解類型)或enum聲明
*
* @Retention 表示在什么級別保存該注解信息。可選的 RetentionPolicy 參數包括:
* RetentionPolicy.SOURCE 注解將被編譯器丟棄
* RetentionPolicy.CLASS 注解在class文件中可用,但會被VM丟棄
* RetentionPolicy.RUNTIME VM將在運行期也保留注釋,因此可以通過反射機制讀取注解的信息。
*
* @Documented 將此注解包含在 javadoc 中
*
* @Inherited 允許子類繼承父類中的注解
使用:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
舉例:
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的使用
1、修改bean.xml,引入命名空間(保證項目能引用到common-annotations.jar)
?xml version="1.0" encoding="UTF-8"?> <beans xmlns=http://www.springframework.org/schema/beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context=http://www.springframework.org/schema/context xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd"> <context:annotation-config /> <bean id="peopleDao" class="com.wxy.dao.impl.PeopleDaoBean"></bean> <bean id="peopleService" class="com.wxy.service.impl.PeopleServiceBean" </bean> </beans>
2、在PeopleServiceBean中使用@Resource注解
public class PeopleServiceBean implements PeopleService { @Resource private PeopleDao peopleDao; private String name = "wxy"; private Integer id = 1; … }
3、 測試
public class Test { public static void main(String[] args) { //IOC容器實例化 ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml"); PeopleService peopleService = (PeopleService) ac.getBean("peopleService"); peopleService.save(); } }
4、 結果
--> the method is called save()! name=wxy,id=1
this is the method PeopleDaoBean.add()!
也可以在setter方法上使用@Resource:
public class PeopleServiceBean implements PeopleService { private PeopleDao peopleDao; private String name = "wxy"; private Integer id = 1; /** * @return the peopleDao */ public PeopleDao getPeopleDao() { return peopleDao; } /** * @param peopleDao the peopleDao to set */ @Resource public void setPeopleDao(PeopleDao peopleDao) { this.peopleDao = peopleDao; } ….. }
結果一樣。
三、@Resource注解的實現原理(只用於配置,不用於干活)
1、新建Annotation類型文件
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 ""; }
2、使用自定義的注解@WxyResource
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; } … }
3、事實上上一步沒有實現,這一步開始實現注入原理,在自定義spring容器中添加注解功能
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() { …. } …. }
4、 使用自定義容器測試
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(); } }
5、 測試結果
--> the method is called save()! name=wxy,id=1
this is the method PeopleDaoBean.add()!
注解本身不做任何事情,只是像xml文件一樣起到配置作用。注解代表的是某種業務意義,注解背后處理器的工作原理如上源碼實現:首先解析所有屬性,判斷屬性上是否存在指定注解,如果存在則根據搜索規則取得bean,然后利用反射原理注入。如果標注在字段上面,也可以通過字段的反射技術取得注解,根據搜索規則取得bean,然后利用反射技術注入。