注解說(shuō)明
Java提供了一種原程序中的元素關(guān)聯(lián)任何信息和任何元數(shù)據(jù)的途徑和方法。
JDK1.5版本引入的一個(gè)特性,可以聲明在包、類(lèi)、屬性、方法、局部變量、方法參數(shù)等的前面,用來(lái)對(duì)這些元素進(jìn)行說(shuō)明、注釋?zhuān)梢岳斫鉃樽⒔饩褪且粋€(gè)標(biāo)記
好處
1.能夠讀懂別人寫(xiě)的代碼,特別是框架相關(guān)的代碼
2.讓編程更加簡(jiǎn)潔,代碼更加清晰
分類(lèi)
運(yùn)行機(jī)制劃分:源碼注解、編譯時(shí)注解、運(yùn)行時(shí)注解
按照來(lái)源劃分:JDK注解、第三方注解、自定義注解
元注解:定義注解的注解

常見(jiàn)第三方注解
通常在框架中出現(xiàn),如SpringMVC、Spring、Mybatis

分類(lèi)解析
源碼注解:注解只在源碼中存在,編譯成 .class 文件就不存在了,如@Override,@SuppressWarnings
編譯時(shí)注解:注解在源碼和 .class 文件中都存在
運(yùn)行時(shí)注解:在運(yùn)行階段還起作用,甚至?xí)绊戇\(yùn)行邏輯的注解,如spring的@Autowired,jdk的@Deprecated
jdk注解

@Override
作用是表示該方法是重寫(xiě)或覆蓋父類(lèi)的方法聲明
使用@Override注解的方法必須重寫(xiě)父類(lèi)或者java.lang.Object中的一個(gè)同名方法
@Deprecated
表示該方法已過(guò)時(shí),或者說(shuō)有更好的方法取代了它
如果使用過(guò)時(shí)的方法,會(huì)報(bào)警告信息 The method xxx from the type xxx is deprecated
@SuppressWarnings
該批注的作用是給編譯器一條指令,告訴它對(duì)被批注的代碼元素內(nèi)部的某些警告保持靜默
@SuppressWarnings("deprecation") 如使用了過(guò)時(shí)方法加上該注解即可消除警告信息
自定義注解
語(yǔ)法說(shuō)明
- 使用
@interface關(guān)鍵字定義注解,也稱(chēng)為注釋類(lèi)型public @interface XXX{} - 成員以無(wú)參無(wú)異常方式聲明,如
String desc(); - 可以使用
default為成員指定一個(gè)默認(rèn)值,如int age() default 18;

- 成員類(lèi)型是受限的,合法類(lèi)型包括原始類(lèi)型及
String、Class、Annotation、Enumeration - 如果注解只有一個(gè)成員,則成員名必須取名為
value(),在使用時(shí)可以忽略成員名和賦值號(hào)(=),直接@XXX(val) - 注解類(lèi)可以沒(méi)有成員,沒(méi)有成員的注解稱(chēng)為標(biāo)識(shí)注解,如:
@Autowired

@Target元注解
語(yǔ)法: @target(ElementType[] value)
如: @Target({ElementType.METHOD,ElementType.TYPE})
作用:指示該注解(注釋類(lèi)型)所適用的程序元素的種類(lèi),如果沒(méi)有該元注解,則聲明的類(lèi)型可以用在任一程序元素上

ElementType
枚舉類(lèi),程序元素類(lèi)型,配合target注解以指定在什么情況下使用注釋類(lèi)型是合法的,取值如下

@Retention元注解
語(yǔ)法:@Retention(RetentionPolicy value)
如: @Retention(RetentionPolicy.RUNTIME)
作用:指示注釋類(lèi)型的注釋要保留多久,如果沒(méi)有該元注解,則保留策略默認(rèn)為 RetentionPolicy.CLASS
RetentionPolicy
枚舉類(lèi),注釋保留策略,取值如下
CLASS 編譯器將把注釋記錄在類(lèi)文件中,但在運(yùn)行時(shí) VM 不需要保留注釋
RUNTIME 編譯器將把注釋記錄在類(lèi)文件中,在運(yùn)行時(shí) VM 將保留注釋?zhuān)虼丝梢酝ㄟ^(guò)反射讀取
SOURCE 只在源碼顯示,編譯時(shí)會(huì)丟棄
標(biāo)識(shí)元注解
@Inherited
允許子類(lèi)繼承父類(lèi),對(duì)于實(shí)現(xiàn)接口的子類(lèi)將不會(huì)繼承父類(lèi)注釋類(lèi)型,對(duì)于子類(lèi)重寫(xiě)的方法將不會(huì)繼承父類(lèi)該方法的注釋類(lèi)型
@Documented
生成javadoc時(shí)會(huì)包含注解信息(在Eclipse中項(xiàng)目右鍵-->export-->javadoc)
使用注解語(yǔ)法
@<注解名>(<成員名1>=<成員值1>,<成員名2>=<成員值2>,...)
注意:注解上定義的全部成員使用時(shí)都必須指明(有默認(rèn)值的成員除外)
自定義注解示例
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Description {
String desc();
String author();
int age() default 18;
}
//在某個(gè)類(lèi)的方法中使用該注解
@Description(desc = "I am eyeColor", author = "silly", age = 22)
public String eyeColor(){
return "red";
}
解析注解
通過(guò)反射獲取類(lèi)、函數(shù)或成員上的運(yùn)行時(shí)注解信息,從而實(shí)現(xiàn)動(dòng)態(tài)控制程序運(yùn)行的邏輯
相關(guān)API
以下是Class類(lèi)、Method類(lèi)、Field類(lèi)所共有方法
Annotation對(duì)象獲取后,使用該對(duì)象.成員即可獲取注解上的成員值

解析示例
有如下自定義注解及使用注解的類(lèi)
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Description {
String desc();
String author() default "ruoxiyuan";
int age() default 18;
}
package com.rxy.annotation;
@Description(author = "dubbo", desc = "I am person")
public class Person {
@Description(author = "zookeeper", desc = "I am person method")
public String name(){
return null;
}
public int age(){
return 0;
}
}
package com.rxy.annotation;
@Description(desc = "I am class Annotation")
public class Child extends Person {
@Override
@Description(author = "activeMQ", desc = "I am method Annotation", age=20)
public String name() {
return null;
}
@Override
public int age() {
return 0;
}
}
對(duì)注解類(lèi)進(jìn)行注解解析
/**
* 注解解析類(lèi)
*/
public class ParseAnn {
public static void main(String[] args) {
//獲取類(lèi)上的注解
try {
//1.使用類(lèi)加載器加載類(lèi),獲取字節(jié)碼對(duì)象
Class clazz = Class.forName("com.rxy.annotation.Child");
//2.判斷類(lèi)是否含有指定注解
boolean isExist = clazz.isAnnotationPresent(Description.class);
if(isExist){
//獲取注解對(duì)象
Description desc = (Description)clazz.getAnnotation(Description.class);
//獲取注解成員值
System.out.println(desc.author() + "," + desc.desc() + "," + desc.age());
}
//獲取方法上的注解
Method[] methods = clazz.getMethods();
for(Method m : methods){
boolean isMExist = m.isAnnotationPresent(Description.class);
if(isMExist){
Description d = m.getAnnotation(Description.class);
System.out.println(m.getName()+ ":" + d.author() + "," + d.desc() + "," + d.age());
}
}
//另一種解析方式
for(Method m : methods){
Annotation[] as = m.getDeclaredAnnotations();
for(Annotation a : as){
if(a instanceof Description){
Description d = (Description)a;
System.out.println(d.author() + "," + d.desc() + "," + d.age());
}
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
/* 打印結(jié)果
* ruoxiyuan,I am class Annotation,18
* name:activeMQ,I am method Annotation,20
* activeMQ,I am method Annotation,20
*/
/*
* 將Child中類(lèi)與方法中的注解全部注釋
* 此時(shí)打印結(jié)果為
* dubbo,I am person,18
*/
/*
* 將Child中類(lèi)與方法中的注解全部注釋?zhuān)瑢ame方法也注釋
* 此時(shí)打印結(jié)果為
* dubbo,I am person,18
* name:zookeeper,I am person method,18
* zookeeper,I am person method,18
*/
}
}
綜合實(shí)戰(zhàn)
項(xiàng)目取自一個(gè)公司的持久層架構(gòu),用來(lái)代替Hibernate的解決方案,核心代碼就是通過(guò)注解來(lái)實(shí)現(xiàn)的。
需求:
1.有一張用戶(hù)表,字段包括用戶(hù)ID,用戶(hù)名,昵稱(chēng),年齡,性別,所在城市,郵箱,手機(jī)號(hào)
2.方便的對(duì)每個(gè)字段或字段的組合條件進(jìn)行檢索,并打印出SQL
3.使用方式要足夠簡(jiǎn)單
定義注解
自定義注解用于映射類(lèi)與表、屬性與表字段的對(duì)應(yīng)關(guān)系
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 Table {
String value();
}
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 Column {
String value();
}
實(shí)體類(lèi)
實(shí)體類(lèi)可以有多個(gè),此處以用戶(hù)表為例
@Table("user")
public class Filter {
//省略getset方法
@Column("id")
private int id;
@Column("user_name")
private String userName;
@Column("nick_name")
private String nickName;
@Column("age")
private int age;
@Column("city")
private String city;
@Column("email")
private String email;
@Column("mobile")
private String mobile;
}
定義注解解析類(lèi)
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class Test {
public static void main(String[] args) {
//測(cè)試
Filter f1 = new Filter();
f1.setId(10);// 查詢(xún)id為10的用戶(hù)
Filter f2 = new Filter();
f2.setAge(18);
f2.setUserName("lucy");// 查詢(xún)用戶(hù)名為lucy年齡18的用戶(hù)
Filter f3 = new Filter();
f3.setEmail("liu@sina.com,zh@163.com,77@qq.com");// 查詢(xún)郵箱為其中任意一個(gè)的用戶(hù)
String sql1 = query(f1);
String sql2 = query(f2);
String sql3 = query(f3);
System.out.println(sql1);
System.out.println(sql2);
System.out.println(sql3);
/*select * from student where 1=1 and id=10
select * from student where 1=1 and user_name='lucy' and age=18
select * from student where 1=1 and email in('liu@sina.com','zh@163.com','77@qq.com')*/
}
/**
* 該方法可以對(duì)多張表對(duì)象進(jìn)行通用解析
* @param f
* @return
*/
@SuppressWarnings("unchecked")
private static String query(Object f) {
StringBuilder sb = new StringBuilder();
// 1.獲取字節(jié)碼對(duì)象
Class clazz = f.getClass();
// 2.獲取table名稱(chēng)
boolean isExist = clazz.isAnnotationPresent(Table.class);
if (!isExist) {
return null;
}
Table t = (Table) clazz.getAnnotation(Table.class);
String tableName = t.value();
sb.append("select * from ").append(tableName).append(" where 1=1");
// 3.遍歷所有字段
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
// 4.處理每個(gè)字段對(duì)應(yīng)的sql
boolean isFExist = field.isAnnotationPresent(Column.class);
if (!isFExist) {
continue;
}
// 4.1獲取字段名稱(chēng)
Column col = field.getAnnotation(Column.class);
String colName = col.value();
// 4.2獲取字段值
String fieldName = field.getName();
String getMethodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
Object fieldValue = null;
try {
Method method = clazz.getMethod(getMethodName);
fieldValue = method.invoke(f, null);
} catch (Exception e) {
e.printStackTrace();
}
// 4.3拼接sql
if (fieldValue == null || (fieldValue instanceof Integer && (Integer) fieldValue == 0)) {
continue;
}
sb.append(" and ").append(colName);
if (fieldValue instanceof String) {
if (((String) fieldValue).contains(",")) {
String[] values = ((String) fieldValue).split(",");
sb.append(" in(");
for (String s : values) {
sb.append("'").append(s).append("'").append(",");
}
sb.deleteCharAt(sb.length() - 1);
sb.append(")");
} else {
sb.append("=").append("'").append(fieldValue).append("'");
}
} else {
sb.append("=").append(fieldValue);
}
}
return sb.toString();
}
}