瘋狂Java學習筆記(69)---------Lock">瘋狂Java學習筆記(69)---------Lock
755
2025-03-31
瘋狂Java學習筆記(53)------------Annotation(注釋)第二篇
JDK的元Annotation
JDK除了java.lang下提供了3個基本Annotation之外,還在java.lang.annotation包下提供了四個Meta Annotation(元Annotation),這四個都是用于修飾其它Annotation定義
(1)使用@Retention
@Retention只能用于修飾一個Annotation定義,用于該Annotation可以保留多長時間,@Retention包含一個RetentionPolicy類型的value成員變量,所以使用@Retention時必須為該value成員變量指定值.
value成員變量的值只能是如下三個
RetentionPolicy.CLASS:編譯器將把注釋記錄在class文件中。當運行Java程序時,JVM不再保留注釋。這是默認值
。
RentionPolicy.RUNTIME:編譯器將把注釋記錄在class文件中。當運行java程序時,JVM也會保留注釋,程序也可以通過反射獲取該注釋。
RentionPolicy.SOURCE:編譯器直接丟棄這種策略的注釋。
在前面的程序中,因為我們要通過反射獲取注釋信息,所以我們指定value屬性值為RetentionPolicy.RUNTIME. 使用@Retention元數據Annotation可采用如下代碼為value指定值.
//定義下面的Testable Annotation的保留到過行時
@Retention(value=RetentionPolicy.RUNTIME)
public @interface Testable{}
也可以采用如下代碼來為value指定值
@Retention(RetentionPolicy.SOURCE)
public @interface Testable{}
上面代碼使用@Retention元數據Annotation時,并未直接通過value=RetentionPolicy.SOURCE的方式來為成員變量指定值,這是因為如果Annotation的成員變量名為value時,程序中可以直接在Annotation后的括號里指定該成員變量的值,無須用name=value的形式.
說明
如果我們定義的Annotation類型里只有一個value成員變量,使用該Annotation時可以直接在Annotation后的括號里指定value成員變量的值,無須使用name=value的形式。
(2)使用@Target
@Target也是用于修飾一個Annotation定義,它用于指定被修飾的Annotation能用于修飾哪些程序元素。@Target Annotation也包含一個名為value的成員變量。該成員變量的值只能是如下幾個
ElementType.ANNOTATION_TYPE: 指定該策略的Annotation只能修飾Annotation
ElementType.CONSTRUCTOR:指定該策略的Annotation能修飾構造器
ElementType.FIELD:?????????????????指定該策略的Annotation只能修飾成員變量
ElementType.LOCAL_VARIABLE:指定該策略的Annotation只能修飾局部變量
ElementType.METHOD?????????????指定該策略的Annotation只能修飾方法定義
ElementType.PACKAGE?????????? 指定該策略的Annotaion只能修飾包定義
ElementType.PARAMETER???? ?指定該策略的Annotation可以修飾參數
ElementType.TYPE?????????????????? 指定該策略的Annotaion可以修飾類,接口(包括注釋類型)或枚舉定義
與使用@Retention類似的是,使用@Target也可以直接在括號里指定value值,可以無須使用name=value的形式。如
下代碼指定@ActionListenerFor Annotation只能修飾成員變量
@Target(ElementType.FIELD)
public @interface ActionListenerFor{}
如下代碼指定@Testable Annotation只能修飾方法
@Target(ElementType.METHOD)
public @interface Testable{}
(3)使用@Documented
@Documented用于指定該元Annotation修飾的Annotation類將被javadoc工具提取成文檔,如果定義Annotatin類時使用了@Documented修飾,則所有使用該Annotation修飾的程序元素API文檔中將會包含該Annotation說明
下面程序定義了一個Testable Annotation程序使用@Documented來修飾@Testable Annotation定義,所以該Annotation將被 javadoc工具提取
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
//定義Testable Annotation將被javadoc工具提取
@Documented
public @interface Testable
{
}
上面程序中的@Documented代碼決定了所有使用@Testable Annotation的地方都會被javadoc工具提取到api文檔中
下面程序中定義了一個 MyTest類,該類中的infor方法使用Testable Annotation修飾
程序清單
public class MyTest
{
//使用@Testable修飾info方法
@Testable
public void info()
{
System.out.println("info方法...");
}
}
(4)使用@Inherited
@Inherited元Annotation指定被它修飾的Annotation將具有繼承性。如果某個類使用了Annotaion(使用Annotation時使用了@Inherited修飾)修飾,則其子類將自動具有A注釋。
下面使用@Inherited元數據注釋定義了一個Inherited Annotation,該Annotation將具有繼承性
程序清單
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
// @Inherited
public @interface Inheritable
{
}
上面程序中表明了@Inheritable Annotation具有繼承性,如果某個類使用了該Annotation 修飾,則該類的子類將自動具有@Inheritable Annotation
下面程序定義了一個Base基類,該基類使用了@Inherited修飾,則Base類的子類將自動具有@Inherited Annotation
程序清單
//使用@Inheritable修飾的Base類
@Inheritable
class Base
{
}
//TestInheritable類只是繼承了Base類,
//并未直接使用@Inheritable Annotiation修飾
public class TestInheritable extends Base
{
}
總結
1.上面程序中的Base類使用了@Inheritable Annotation修飾,而該Annotaion具有可繼承性,所以其子類也將具有@Inheritable Annotation,運行上面程序看到輸出 true
2.如果將上面的Inheritable.java程序中的@Inherited注釋或者刪除,將會導致Inheritable Annotation不具有繼承性,運行上面程序將會輸出 false
使用APT處理Annotation
APT(Annotation processing tool)是一種處理注釋的工具,它對源代碼文件進行檢測找出其中的Annotation,使用Annotation進行額外的處理。
Annotation處理器在處理Annotation時可以根據源文件中的Annotation生成額外的源文件和其它的文件(文件具體內容由Annotation處理器的編寫者決定),APT還會編譯生成的源文件和原來的源文件,將它們一起生成class文件.
使用APT主要的目的是簡化開發者的工作量,因為APT可以編譯程序源代碼的同時,生成一些附屬文件(比如源文件,類文件,程序發布描述文件等),這些附屬文件的內容也都是與源代碼相關的,換句話說,使用APT可以代替傳統的對代碼信息和附屬文件的維護工作。
如果有過Hibernate開發經驗的朋友可能知道每寫一個Java文件,還必須額外地維護一個Hibernate映射文件(一個名為*.hbm.xml的文件,當然可以有一些工具可以自動生成),下面將使用Annotation來簡化這步操作。
為了使用系統的apt工具來讀取源文件中的Annotation,程序員必須自定義一個Annotation處理器,編寫Annotation處理器需要使用JDK lib目錄中的tools.jar 里的如下4個包.
com.sun.mirror.apt:和APT交互的接口
com.sun.mirror.declaration:包含各種封裝類成員,類方法,類聲明的接口。
com.sun.mirror.type:包含各種封裝源代碼中程序元素的接口。
com.sun.mirror.util:提供了用于處理類型和聲明的一些工具。
每個Annotation處理器需要實現com.sun.mirror.apt包下的AnnotationProcessor接口,這個接口中定義了一個"process"方法,該方法是由apt調用Annotation處理器時將被用到的。
一個Annotation處理器可以處理一種或多種Annotation類型。
1.通常情況下,Annotation處理器實例是由其相應的工廠返回,Annotation處理器工廠應該實現AnnotationProcessorFactory接口,APT將調用工廠類的getProcessorFor方法來獲得Annotation處理器。
2.在調用過程中,APT將提供給工廠類一個AnnotationProcessorEnvironment對象.
3.AnnotationProcessorEnvironment對象是APT工具與注釋環境通信的途徑。
使用APT工具來處理源文件時,APT首先檢測在源代碼文件中包含哪些Annotation,然后APT將查找所需的處理器工廠,并由工廠來返回相應的Annotation處理器。如果該處理器工廠支持這些Annotaion,處理器工廠返回的Annotaion處理器將會處理這些Annotation,如果生成的源文件中再次包含Annotaion,APT將會重復上面過程,直至沒有新文件生成。
為了說明使用APT來根據源文件中的注釋來生成額外的文件,下面將定義三個Annotation類型,分別用于修飾持久化類,標識屬性和普通屬性。
程序清單
修飾表屬性
@Persistent(table="persons_table")
public class Person
{
@IdProperty(column="person_id",type="integer",generator="identity")
private int id;
@Property(column="person_name",type="string")
private String name;
@Property(column="person_age",type="integer")
private int age;
public Person()
{
}
public Person(int id , String name , int age)
{
this.id = id;
this.name = name;
this.age = age;
}
public void setId(int id)
{
this.id = id;
}
public int getId()
{
return this.id;
}
public void setName(String name)
{
this.name = name;
}
public String getName()
{
return this.name;
}
public void setAge(int age)
{
this.age = age;
}
public int getAge()
{
return this.age;
}
}
定義了三個Annotation之后,下面我們提供一個簡單的Java類文件,這個Java類文件使用了上面三個Annotation來修飾
@Persistent(table="persons_table")
public class Person
{
@IdProperty(column="person_id",type="integer",generator="identity")
private int id;
@Property(column="person_name",type="string")
private String name;
@Property(column="person_age",type="integer")
private int age;
public Person()
{
}
public Person(int id , String name , int age)
{
this.id = id;
this.name = name;
this.age = age;
}
public void setId(int id)
{
this.id = id;
}
public int getId()
{
return this.id;
}
public void setName(String name)
{
this.name = name;
}
public String getName()
{
return this.name;
}
public void setAge(int age)
{
this.age = age;
}
public int getAge()
{
return this.age;
}
}
上面Person類是一個非常普通的Java類,但這個普通的Java類使用了@Persistent,@IdProperty,@IdPropery三個Annotation。下面我們為這三個Annotation提供了一個Annotation處理器,該處理器的功能是根據注釋來生成一個Hibernate的映射文件.
程序清單
import com.sun.mirror.apt.*;
import com.sun.mirror.declaration.*;
import com.sun.mirror.type.*;
import com.sun.mirror.util.*;
import java.beans.*;
import java.io.*;
import java.util.*;
import java.lang.reflect.*;
public class HibernateAnnotationProcessor implements AnnotationProcessor
{
//Annotation處理器環境,是該處理器與APT交互的重要途徑
private AnnotationProcessorEnvironment env;
//構造HibernateAnnotationProcessor對象時,獲得處理器環境
public HibernateAnnotationProcessor(AnnotationProcessorEnvironment env)
{
this.env = env;
}
//循環處理每個對象
public void process()
{
//遍歷每個class文件
for (TypeDeclaration t : env.getSpecifiedTypeDeclarations())
{
//定義一個文件輸出流,用于生成額外的文件
FileOutputStream fos = null;
//獲取正在處理的類名
String clazzName = t.getSimpleName();
//獲取類定義前的Persistent Annotation
Persistent per = t.getAnnotation(Persistent.class);
//當per Annotation不為空時才繼續處理
if(per != null)
{
try
{
//創建文件輸出流
fos = new FileOutputStream(clazzName + ".hbm.xml");
PrintStream ps = new PrintStream(fos);
//執行輸出
ps.println("");
ps.println("
ps.println(" PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"");
ps.println(" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd\">");
ps.println("
ps.print(" //輸出per的table()的值 ps.println("" table="" + per.table() + "">"); for (FieldDeclaration f : t.getFields()) { //獲取指定FieldDeclaration前面的IdProperty Annotation IdProperty id = f.getAnnotation(IdProperty.class); //如果id Annotation不為空 if (id != null) { //執行輸出 ps.println(" + f.getSimpleName() + "" column="" + id.column() + "" type="" + id.type() + "">"); ps.println(" + id.generator() + ""/>"); ps.println(" } //獲取指定FieldDeclaration前面的Property Annotation Property p = f.getAnnotation(Property.class); //如果p Annotation不為空 if (p != null) { //執行輸出 ps.println(" + f.getSimpleName() + "" column="" + p.column() + "" type="" + p.type() + ""/>"); } } ps.println("
ps.println("");
}
catch (Exception e)
{
e.printStackTrace();
}
finally
{
//關閉輸出流
try
{
if (fos != null)
{
fos.close();
}
}
catch (IOException ex)
{
ex.printStackTrace();
}
}
}
}
}
}
上面的Annotation處理器比較簡單,與前面通過反射來獲取Annotation信息不同的是,這個Annotation處理器使用AnnotationProcessorEnvironment來獲取Annotation信息,AnnotationProcessorEnvironment包含了一個getSpecifiedTypeDeclarations方法,可獲取所有需要處理的類聲明,這個類聲明可包括類,接口,和枚舉等聲明,由TypeDeclaration對象表地示,與Classc對象的功能大致相似,區別只是TypeDeclaration是靜態,只要有類文件就可以獲得該對象,而Class是動態的,必須由虛擬機裝載了指定類文件后才會產生。
TypeDeclaration又包含了如下三個常用方法來獲得對應的程序元素。
getFields
:獲取該類聲明里的所有成員變量聲明,返回值是集合元素FieldDeclaration的集合
getMethods
:獲取該類聲明里的所有成員聲明,返回值是集合元素MethodDeclaration的集合
getPackage
:獲取該類聲明里的包聲明,返回值是TypeDeclaration
上面三個方法返回的TypeDeclaration,FieldDeclaration,MethodDeclaration都可調用getAnnotation方法來訪問修飾它們的Annotation,上面程序中就是獲取不同程序元素的Annotation的代碼。
提供了上面的Annotation處理器類之后,還應該為該Annotation處理器提供一個處理工廠,處理工廠負責決定該處理器支持哪些Annotation,并通過getProcessorFor方法來生成一個Annotation處理哭對象。
程序清單如下
import com.sun.mirror.apt.*;
import com.sun.mirror.declaration.*;
import com.sun.mirror.type.*;
import com.sun.mirror.util.*;
import java.beans.*;
import java.io.*;
import java.util.*;
public class HibernateAnnotationFactory implements AnnotationProcessorFactory
{
//所有支持的注釋類型
public Collection
{
return Arrays.asList("Property" , "IdProperty" , "Persistent");
}
//返回所有支持的選項
public Collection
{
return Arrays.asList(new String[0]);
}
//返回Annotation處理器
public AnnotationProcessor getProcessorFor(Set
{
return new HibernateAnnotationProcessor(env);
}
}
提供了上面的處理器工廠后,就可以使用API工具來處理上面的Person.java源文件,并根據該源文件來生成一個XML文件。 APT工具位于JDK的安裝路徑的bin路徑下。。
java Annotation的講解就這些了,需要好好看一看呀!
博客借鑒http://blog.sina.com.cn/s/blog_4c925dca0100hsyt.html
http://blog.csdn.net/zhai56565/article/details/40503743
Java JDK
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。