Java 註解詳解

定義:

    註解(Annotation),也叫元數據。一種代碼級別的說明。它是JDK1.5及之後版本引入的一個特性,與@Retention類、接口、枚舉是在同一個層次。它能夠聲明在包、類、字段、方法、局部變量、方法參數等的前面,用來對這些元素進行說明,註釋,利用發射能夠在運行時改變程序的邏輯。請往下看:java

    應該有人會看不懂這句話吧,「元數據」過高大上了,好抽象,雲裏霧裏的,那咱們再看看元數據的定義。數據庫

元數據:爲描述數據的數據數組

綜合理解註解:註解就是Java爲類、接口、方法、字段、局部變量、方法參數能表達更豐富而添加的說明,就好像給它們(類、接口、方法、字段、局部變量、方法參數)加了一個標籤,好比一個方法是被重寫的,那麼Java代碼就是:安全

@Override
public void doDispatch(HttpServletRequest req){

}

 

語法:

    取一段代碼爲例來講明註解的語法(不要被它嚇到,接下來的篇幅會對他一點點的講解)app

package com.jv.annotation;
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;

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyAnnotation {
	String value() default "";
	String[] name() default {};
}

下面開始一點點的講,最終獲得上面的註解eclipse

1.定義最簡單的註解ide

public @interface MyAnnotation {}

使用@interface就聲明瞭一個註解,而後就能夠在其餘地方使用這個註解,如:函數

package com.jv.annotation;

@MyAnnotation
public class MyObject {
	
}

雖然這個註解如今尚未任何屬性或者其餘的修飾,但類或者它的元素被該註解修飾了,那麼他就變得不同了。好比在運行時就能夠掃描全部的類並判斷是否是擁有該註解,而後作特殊的操做測試

2.給註解加屬性ui

package com.jv.annotation;

public @interface MyAnnotation {
	String value() default "";
	String[] name() default {};
}

給註解加value和name兩個屬性,其中name是一個數組類型的,表示他能夠接受多個String值。當註解有了屬性,那麼它就攜帶了更可能是信息,在使用(經過反射得到註解或者註解的值)的時候就可拿它們作邏輯判斷

:註解沒有方法

3.給註解添加註解

package com.jv.annotation;
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;

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyAnnotation {
	String value() default "";
	String[] name() default {};
}

註解的註解就是用來講明被修飾的註解在不一樣功能上的限制

好比:@Target限制了註解能修飾那些元素(類、接口、方法、字段、局部變量、方法參數)

@Target是一種元註解,表示能夠用來修飾註解的註解

 

元註解:

    元註解有@Retention、@Documented、@Target、@Inherited、@Repeatable 5 種

    @Retention

    Retention 的英文意爲保留期的意思。當 @Retention 應用到一個註解上的時候,它解釋說明了這個註解的的存活時間。
    - RetentionPolicy.SOURCE     註解被保留到源碼中,在編譯器工做時它將被丟棄。 
    - RetentionPolicy.CLASS        註解被保留到Java和編譯後的class文件中,但不會被加載到 JVM 中。 
    - RetentionPolicy.RUNTIME  註解被保留到Java和編譯後的class文件中,且會被加載到JVM,因此在程序運行時能夠獲取到它們,並作不少邏輯判斷

    若是對類加載有興趣的同窗能夠查看Java類加載機制

    @Documented    

    它的做用是可以將被@Documented 修飾的註解記錄到 Javadoc 中

    @Target

    限定註解能修飾的元素(類、接口、方法、字段、局部變量、方法參數)

    - ElementType.ANNOTATION_TYPE         能夠給一個註解進行註解

    - ElementType.CONSTRUCTOR                能夠給構造方法進行註解

    - ElementType.FIELD                                能夠給屬性進行註解

    - ElementType.LOCAL_VARIABLE             能夠給局部變量進行註解

    - ElementType.METHOD                          能夠給方法進行註解

    - ElementType.PACKAGE                         能夠給一個包進行註解

    - ElementType.PARAMETER                    能夠給一個方法內的參數進行註解

    - ElementType.TYPE                                 能夠給一個類進行註解,好比類、接口、枚舉

    - ElementType.TYPE_PARAMETER           能夠給一個類或者方法的參數聲明進行註解(JDK1.8)

    - ElementType.TYPE_USE                         能夠給任何使用到類型的地方進行聲明(JDK1.8)

    @Inherited

    一個註解被@Inherited修飾後,那麼被修飾的註解再去修飾其餘類(A),A的子類B(B沒有被其餘註解修飾)會繼承A的註解

@Inherited
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation{}


@MyAnnotation
public class A {}


public class B extends A {}

    @Repeatable

    它是 Java 1.8 新加入的註解,表示能夠重複修飾

@interface Cars {
    Car[]  value();
}


@Repeatable(Cars .class)
@interface Car{
    String value default "";
}


@Car("AA")
@Car("BB")
@Car("CC")
public class MyTest{

}

@Car具備一個value屬性,那屬性拿來幹嗎,又是怎麼使用的喃

註解的屬性

它和類的成員變量同樣,能夠用來保存在使用註解時指定的值

public @interface MyAnnotation {
	String value() default "";
	String[] name() default {};
}

定義了value 和 name屬性,而且使用default指定它們的默認值,如何使用它們喃?

@MyAnnotation(value="abc",name= {"123","456"})
public class MyObject {
	
}

 

JDK內置註解

@Deprecated

用來修飾過期的元素,在使用高版本JDK時常常遇到該註解。編譯時會發出警告,通知開發人員它是已過期的類或者方法

@Override

提示子類中的方法是重寫的

@SuppressWarnings

忽略編譯器發出的警告,在添加了這個註解,eclipse左邊的小黃感嘆號就會消失

@SafeVarargs

Jdk 1.7 加入的新註解,用於提醒開發者不要用參數作一些不安全的操做,它的存在會阻止編譯器產生 unchecked 這樣的警告,可是運行時程序可能會報錯。

@FunctionalInterface

Jdk 1.8 加入的新註解(函數式接口),但願瞭解的同窗能夠看看(強烈推薦)Java8 Lambda表達式

反射提取註解

用一個例子來講明屬性或者方法上的註解如何被拿到,如何取得註解的屬性值

注:測試JDK爲1.8

1.定義註解

/*
 * 指定數據庫名稱
 */
@Target({ElementType.TYPE,ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyTable{
	String value() default "";
}

/*
 * 指明JavaBean中某一個屬性爲主鍵,且一個類中該註解只能有一個
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyId {
	public String value() default "id";
}

/**
 * 指定JavaBean中屬性對應的數據庫字段名
 * @author jionsvolk
 *
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyField {
	public String value() default "";
}

2.用到Bean

@MyTable("tb_cust")
public class Cust implements Serializable{
	private static final long serialVersionUID = 1L;
	
	@MyId("custId")
	private Integer custId;
	@MyField("custName")
	private String name;
	private String address;
	
	public Integer getCustId() {
		return custId;
	}
	public void setCustId(Integer custId) {
		this.custId = custId;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getAddress() {
		return address;
	}
	public void setAddress(String address) {
		this.address = address;
	}
	public Cust(Integer custId, String name, String address) {
		super();
		this.custId = custId;
		this.name = name;
		this.address = address;
	}
	public Cust() {
		super();
	}
	
}
public class MyEntry {
	private String key;
	private Object value;
	
	public boolean isNotEmpty() {
		if(key == null || key.equals("") || value == null) {
			return false;
		}else {
			return true;
		}
	}
	
	
	public String getKey() {
		return key;
	}
	public void setKey(String key) {
		this.key = key;
	}
	public Object getValue() {
		return value;
	}
	public void setValue(Object value) {
		this.value = value;
	}
	
}

3.反射提取註解、提取方法、提取屬性

public class DBHelper {
	private static final String serialFieldName = "serialVersionUID";
	public static String selectSql(Object obj) throws Exception{
		FieldAnnotationHandler fah = new IdFieldAnnotationHandler();
		StringBuilder sb = new StringBuilder();
		//1.拿到Class對象
		Class cl = obj.getClass();
		//2.拿到數據庫表名的註解
		MyTable myTable = (MyTable)cl.getAnnotation(MyTable.class);
		String tblName = myTable.value();
		sb.append("select ");
		//3.獲取對象全部定義的屬性(包含私有屬性)
		Field[] fields = cl.getDeclaredFields();
		//4.循環全部屬性,獲得註解名稱和屬性值
		for(Field f : fields) {
			//4.1 序列化字段無需處理
			if(f.getName().equals(serialFieldName)) {
				continue;
			}
			//4.2 拿到屬性上的全部註解
			Annotation[] ans = f.getAnnotations();
			//4.3 循環處理全部註解,獲得數據庫表的字段名和它對應的值
			for(Annotation an : ans) {
				//使用責任鏈模式來處理每個屬性上的註解(由於我這裏假設只會出現MyId和MyField註解,因此只寫了兩個Handler)
				MyEntry entry = fah.handler(an , f , obj);
				if(entry.isNotEmpty()) {
					sb.append(entry.getKey()+",");
					break;
				}
			}
		}
		//5.剔除最後的","號
		sb = new StringBuilder(sb.toString().substring(0, sb.toString().lastIndexOf(",")!=-1?sb.toString().lastIndexOf(","):sb.toString().length()));
		
		sb.append(" from ").append(tblName);
		
		//處理主鍵
		MyEntry primaryEntry = ((IdFieldAnnotationHandler)fah).getPrimaryEntry();
		if(primaryEntry.isNotEmpty()) {
			sb.append(" where ").append(primaryEntry.getKey()).append(" = ").append(primaryEntry.getValue());
		}
		
		return sb.toString();
	}
}

4.處理屬性和值的Handler

抽象類

public abstract class FieldAnnotationHandler {
	FieldAnnotationHandler handler;

	public FieldAnnotationHandler getHandler() {
		return handler;
	}

	public void setHandler(FieldAnnotationHandler handler) {
		this.handler = handler;
	}
	
	public abstract MyEntry handler(Annotation an,Field f,Object obj) throws Exception;
}

主鍵處理Handler

/*
 * 和其餘的Handler不同,增長了primaryEntry屬性,用於記住主鍵的信息
 */
public class IdFieldAnnotationHandler extends FieldAnnotationHandler{
	private MyEntry primaryEntry;
	
	public MyEntry getPrimaryEntry() {
		return primaryEntry;
	}
	
	public IdFieldAnnotationHandler(){
		this.primaryEntry = new MyEntry();
		this.handler = new MyFieldAnnotationHandler();
	}

	@Override
	public MyEntry handler(Annotation an,Field f,Object obj) throws Exception{
		MyEntry entry = new MyEntry();
		if(an instanceof MyId) {
			MyId myId = (MyId)an;
			primaryEntry.setKey(myId.value());
			if(primaryEntry.getKey() !=null && !primaryEntry.getKey().equals("")) {
				entry.setKey(primaryEntry.getKey());
				Method method = obj.getClass().getMethod("get"+f.getName().substring(0, 1).toUpperCase() + f.getName().substring(1), null);
				entry.setValue(method.invoke(obj, null));
				primaryEntry.setValue(entry.getValue());
				return entry;
			}else {
				entry.setKey(f.getName());
				entry.setValue(f.get(obj));
				return entry;
			}
		}else {
			return handler.handler(an, f,obj);
		}
	}
	
}

字段處理Handler

public class MyFieldAnnotationHandler extends FieldAnnotationHandler{
	
	public MyFieldAnnotationHandler(){
	}

	@Override
	public MyEntry handler(Annotation an,Field f,Object obj) throws Exception{
		MyEntry entry = new MyEntry();
		if(an instanceof MyField) {
			MyField myField = (MyField)an;
			String columnName = myField.value();
			if(columnName != null && !columnName.equals("")) {
				entry.setKey(columnName);
				Method method = obj.getClass().getMethod("get"+f.getName().substring(0, 1).toUpperCase() + f.getName().substring(1), null);
				entry.setValue(method.invoke(obj, null));
				return entry;
			}else {
				entry.setKey(f.getName());
				entry.setValue(f.get(obj));
				return entry;
			}
		}else {
			return new MyEntry();
		}
	}
	
}

測試類

public class TestSql {
	@Test
	public void test() throws Exception {
		System.out.println(DBHelper.selectSql((new Cust(123,"Messi","巴塞羅那"))));
	}
}

輸出:

select custId,custName from tb_cust where custId = 123

我並無按照反射的API一一寫代碼測試,有興趣的朋友能夠看中文或者官方的API

相關文章
相關標籤/搜索