在用hibernate的時候發現idea能自動生成JavaBean,同時帶有一些註解,這引發了個人好奇。當在學習Android的時候,我發現XUtils這個工具包中的DBUtils也可以使用相似hibernate的註解。因而乎在java編程思想中找了找有關注解的用法。java
註解(也稱爲元數據)爲咱們在代碼中添加信息提供了一種形式化的方法,使咱們能夠在稍後某個時刻很是方便的使用這些數據。註解來源於C#之類的其餘語言。spring
註解的語法比較簡單,除了@符號外,它與java的固有語法一致。javaSE5中內置了三種註解:編程
@Override:定義覆蓋超類,當覆寫對應不上被覆蓋的方法,編譯器發出錯誤提示。數組
@Deprecated:當使用了該註解,即表示這個方法已經不推薦被使用。app
@SuppressWarnings:關閉不當的編譯器警告。ide
咱們使用自定義的註解對一個方法進行註解:工具
public class Testable{ public void execute() { System.out.println("execute..."); } @WETest void taskStart() { execute(); } }
在上邊的代碼中,咱們對taskStart方法使用了註解,接下來咱們對WETest註解進行定義:學習
import java.lang.annotation.*; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface WETest{}
咱們給上邊的註解添加一些內容:ui
import java.lang.annotation.*; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface WETest{ public int id(); public String Notes() default "there is no Notes"; }
一樣,咱們對Testable類使用最新的註解:this
public class Testable{ @WETest(id=666) public void execute() { System.out.println("execute..."); } @WETest(id=666,Notes="this is a method") void taskStart() { execute(); } }
註解就是這麼使用的,當註解內容沒有填寫時,他會使用默認的值,如execute方法,他沒有定義Notes,那麼Notes默認值爲"there is no Notes"。
咱們看到註解上邊有兩行內容,它們是元註解,專門對註解的解釋。元註解一共有四種,分別是:
@Target:表示該註解能夠用到哪些地方,ElementType,CONSTRUCTOR構造器聲明,FIELD域聲明(包括enum實例),LOCAL_VARIABLE局部變量聲明,METHOD方法,PACKAGE包,PARAMETER參數,TYPE類、接口或enum。
Retention:表示須要在什麼級別上使用,RetentionPolicy,SOURCE註解會被編譯器丟掉,CLASS在class文件中可用會被VM拋棄,RUNTIME在VM運行期也會保留能夠經過反射獲取註解信息。
Documented:將註解包含在Javadoc中。
Inherited:容許子類繼承父類中的註解。
接下來,我用一個例子來解釋註解的做用。先編寫一些註解定義:
//DBTable.java 用來生成數據表 package annotations; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface DBTable { public String name() default ""; } //Constraints.java 用來定義約束項 package annotations; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface Constraints { boolean primarykey() default false; boolean allownull() default true; } //PrimaryKey.java 將Constraints中的primarykey定義爲真,表示爲主鍵 package annotations; import java.lang.annotation.*; @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface PrimaryKey { Constraints constraints() default @Constraints(primarykey = true); } //SQLInteger.java 定義列的類型 package annotations; import java.lang.annotation.*; @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface SQLInteger { String name() default ""; Constraints constraints() default @Constraints; } //SQLString.java 定義列的類型 package annotations; import java.lang.annotation.*; @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface SQLString { int value() default 64; String name() default ""; Constraints constraints() default @Constraints; }
接下來寫一個javabean,使用上述註解:
//User.java import annotations.Constraints; import annotations.DBTable; import annotations.SQLInteger; import annotations.SQLString; @DBTable(name="user") public class User { @SQLInteger(name="id",constraints = @Constraints(primarykey=true)) public Integer id; @SQLString(value=30) public String name; @SQLString(name="passwd",constraints=@Constraints(allownull=false)) public String password; /*能夠不用 public void setId(Integer id) { this.id = id; } public void setName(String name) { this.name = name; } public void setPassword(String password) { this.password = password; } public Integer getId() { return id; } public String getName() { return name; } public String getPassword() { return password; }*/ }
咱們看到註解中可使用註解,在SQLInteger中咱們使用了Constraints註解。
接下來咱們寫一個註解處理器:
//Test.java import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; import java.util.Scanner; import annotations.Constraints; import annotations.DBTable; import annotations.SQLInteger; import annotations.SQLString; public class Test { public static String getConstraints(Constraints con) { String constraints = ""; if(!con.allownull()) { constraints +=" NOT NULL"; } if(con.primarykey()) { constraints += " PRIMARY KEY"; } return constraints; } public static void main(String[] args) throws ClassNotFoundException { Scanner s = new Scanner(System.in); String name = s.next(); //從控制檯輸入一個類名,咱們輸入User便可 Class<?> cl = Class.forName(name); //加載類,若是該類不在默認路徑底下,會報 java.lang.ClassNotFoundException DBTable dbTable = cl.getAnnotation(DBTable.class); //從User類中獲取DBTable註解 if(dbTable == null){ //若是沒有DBTable註解,則直接返回,咱們寫了,固然有 return; } String tableName = (dbTable.name().length()<1)?cl.getName():dbTable.name();//獲取表的名字,若是沒有在DBTable中定義,則獲取類名做爲Table的名字 List<String> columnDefs = new ArrayList<String>(); for(Field field : cl.getDeclaredFields()) //獲取聲明的屬性 { String columnName = null; Annotation[] anns = field.getDeclaredAnnotations();//獲取註解,一個屬性能夠有多個註解,因此是數組類型 if(anns.length < 1) { continue; } if(anns[0] instanceof SQLInteger) //判斷註解類型 { SQLInteger sInt = (SQLInteger)anns[0]; columnName = (sInt.name().length()<1)?field.getName():sInt.name();//獲取列名稱與獲取表名同樣 columnDefs.add(columnName+" INT"+getConstraints(sInt.constraints()));//使用一個方法,本身寫的getConstraints(Constraints constraints)獲取列定義 } if(anns[0] instanceof SQLString) { SQLString sStr = (SQLString)anns[0]; columnName = (sStr.name().length()<1)?field.getName().toUpperCase():sStr.name(); columnDefs.add(columnName + " VARCHAR("+sStr.value()+")"+getConstraints(sStr.constraints())); } } StringBuilder createCommand = new StringBuilder("CREATE TABLE "+tableName+"("); for(String columnDef :columnDefs) { createCommand.append("\n "+columnDef+","); } String tableCreate = createCommand.substring(0,createCommand.length()-1)+"\n);"; System.out.println(tableCreate); //打印出來 } }
咱們能夠採用上述方法動態的處理一些數據,例如建立數據表。
注意:註解不支持繼承例如 extends @xxx。註解的default默認值不能夠爲null
總結:使用註解能夠減小對xml等外部文件的依賴,使得對類的定義能夠在一處實現,避免了一個類兩處定義的麻煩。spring和hibernate就採用的這樣的方法。