- 注解是一種在原程序中的元素關(guān)聯(lián)任何信息和任何元數(shù)據(jù)的途徑和方法。
- 在 Java 項(xiàng)目中、尤其是框架相關(guān)的代碼,注解往往很常用,可以讓代碼更簡(jiǎn)潔清晰。
常見注解
JDK 自帶注解
- @Override:覆蓋父類、實(shí)現(xiàn)接口的方法;
- @Deprecated:方法已過時(shí)(可以被覆蓋,但調(diào)用時(shí)會(huì)提示過時(shí));
- @SuppressWarnings("deprecation"):忽略方法過時(shí)的提示;
- ...
第三方注解
Spring 注解:
- @Autowired
- @Service
- @Repository
- @Controller
- ...
Mybatis 注解:
- @InsertProvider
- @UpdateProvider
- @Options
- ...

傳統(tǒng)方法引入 DAO

使用注解引入 DAO
注解分類
按運(yùn)行機(jī)制分類:
- 源碼注解:注解只在源碼中存在,編譯成 .class 文件就不存在了;
- 編譯時(shí)注解:在源碼和 .class 文件中都存在;
- 運(yùn)行時(shí)注解:在運(yùn)行階段還起作用,甚至?xí)绊戇\(yùn)行邏輯。
按來源分類:
- JDK 自帶注解
- 第三方(框架)注解
- 自定義注解
- 元注解
自定義注解
定義注解
// 元注解
@Target({ElementType.METHOD, ElementType.TYPE}) // 聲明注解適用范圍:構(gòu)造方法、字段、局部變量、方法、包、參數(shù)、類和接口等
@Retention(RetentionPolicy.RUNTIME) // 生命周期:SOURCE(只存在于源碼)、CLASS(記錄到 class 中,運(yùn)行時(shí)忽略)、RUNTIME(運(yùn)行時(shí)存在,可反射)
@Inherited // 在父類上添加了注解允許子類繼承
@Documented // 生成 JavaDoc 時(shí)包含注解信息
public @interface Description { // @interface 關(guān)鍵字定義注解
// 注解可以沒有成員,沒有成員的注解稱為標(biāo)識(shí)注解
// 如注解只有一個(gè)成員則必須取名為 value(),使用時(shí)可以忽略成員名和復(fù)制號(hào)(=)
String desc(); // 注解成員以無參無異常方式聲明
String author(); // 成員類型受限:String、Class、Annotation、Enumeration
int age() default 18; // 可以為成員指定默認(rèn)值
}
使用自定義注解
@Desciption(desc="my test", author="YWH", age=17)
public String test() {
return "OK";
}
解析注解
通過反射獲取類、方法或成員上的運(yùn)行時(shí)注解信息(只能是運(yùn)行時(shí)注解),從而實(shí)現(xiàn)動(dòng)態(tài)控制程序運(yùn)行的邏輯。
try {
Class c = Class.forName("com.ywh.test.Test");
boolean isExist = c.isAnnotationPresent(Description.class); // 判斷類上是否添加了注解
if (isExist) {
Description d = c.getAnnotation(Description.class);
System.out.println(d.value());
}
Method[] ms = c.getMethods();
for (Method m: ms) {
boolean isMExist = m.isAnnotationPresent(Description.class);
if (isMExist) {
Description d = (Description) m.getAnnotation(Description.class);
System.out.println(d.value());
}
}
// 另一種解析方法
for(Method m: ms) {
Annotation[] as = m.getAnnotations();
for (Annotation a: as) {
if (a instanceof Description) {
Description d = (Description) a;
System.out.println(d.value());
}
}
}
}
catch (ClassNotFoundException e) {
e.printStackTrace();
}
實(shí)例:使用注解實(shí)現(xiàn) ORM 框架(生成查詢語句)
Table.java
// 表注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Table {
String value();
}
Column.java
// 字段注解
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
String value();
}
定義測(cè)試數(shù)據(jù)模型
@Table("user")
public class Filter {
@Column("id")
private int id;
@Column("user_name")
private String name;
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
}
其對(duì)應(yīng)的 MySQL 表:
CREATE TABLE user (
id INT,
user_name VARCHAR(32)
);
Test.java
public class Test {
public static void main(String[] args) {
Filter f1 = new Filter();
f1.setId(1);
Filter f2 = new Filter();
f2.setName("ywh");
String sql1 = query(f1);
String sql2 = query(f2);
System.out.println(sql1);
System.out.println(sql2);
}
private static String query(Filter f) {
// 待拼裝的 SQL 語句
StringBuilder sb = new StringBuilder();
// 1. 獲取類類型,通過反射獲取類注解、從 Table 注解中提取出對(duì)應(yīng)的數(shù)據(jù)庫表名
Class c = f.getClass();
boolean exists = c.isAnnotationPresent(Table.class);
if (!exists) {
return null;
}
Table t = (Table) c.getAnnotation(Table.class);
String tableName = t.value();
sb.append("SELECT * FROM ").append(tableName).append(" WHERE 1 = 1");
// 2. 遍歷數(shù)據(jù)模型類的所有字段(對(duì)應(yīng)數(shù)據(jù)庫表的字段),拼裝查詢條件
Field[] fArray = c.getDeclaredFields();
for (Field field : fArray) {
// 2.1 獲取字段名:從 Column 注解中獲取字段對(duì)應(yīng)的數(shù)據(jù)庫字段名
boolean fExists = field.isAnnotationPresent(Column.class);
if (!fExists) {
continue;
}
Column column = field.getAnnotation(Column.class);
String columnName = column.value();
// 2.2 獲取字段值:根據(jù)類的字段名拼裝出 getter 方法,求得字段值
String fieldName = field.getName();
String getMethodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
Object fieldValue = null;
try {
Method getMethod = c.getMethod(getMethodName);
fieldValue = getMethod.invoke(f);
} catch (Exception e) {
e.printStackTrace();
}
// 2.3 拼裝 SQL(暫不處理空值和等于 0 的值)
if (fieldValue == null ||
(fieldValue instanceof Integer && (Integer) fieldValue == 0)
) {
continue;
}
sb.append(" AND ").append(columnName);
// 字段值為 String 類型
if (fieldValue instanceof String) {
// 以逗號(hào)割開,則解析為 IN 條件
if (((String) fieldValue).contains(",")) {
String[] values = ((String) fieldValue).split(",");
sb.append(" IN (");
for (String v : values) {
sb.append("'").append(v).append("'").append(",");
}
sb.deleteCharAt(sb.length() - 1); // 刪除最后一個(gè)逗號(hào)
sb.append(")");
} else
sb.append(" = ").append("'").append(fieldValue).append("'");
}
// 字段值為其他類型
else if (fieldValue instanceof Integer) {
sb.append(" = ").append(fieldValue);
}
}
return sb.toString();
}
}