想必在項目中大家一定接觸過不少註解,特別是在Spring項目中,那註解的作用是什麼,有何意義?
什麼是註解?
註解可以理解爲一個標籤,這個標籤可以貼在類上,方法上,成員變量上。
註解用來標識某些特徵,類似的可理解爲Interface接口。
public @interface Table {
String name() default "";
}
元註解
@Retention
Retention 的英文意爲保留期的意思。當 @Retention 應用到一個註解上的時候,它解釋說明了這個註解的的存活時間。
- RetentionPolicy.SOURCE 註解只在源碼階段保留,在編譯器進行編譯時它將被丟棄忽視。
- RetentionPolicy.CLASS 註解只被保留到編譯進行的時候,它並不會被加載到 JVM 中。
- RetentionPolicy.RUNTIME 註解可以保留到程序運行的時候,它會被加載進入到 JVM 中,所以在程序運行時可以獲取到它們。
我們可以這樣的方式來加深理解,@Retention 去給一張標籤解釋的時候,它指定了這張標籤張貼的時間。@Retention 相當於給一張標籤上面蓋了一張時間戳,時間戳指明瞭標籤張貼的時間週期。
@Retention(value = RetentionPolicy.RUNTIME)
public @interface Table {
String name() default "";
}
上面的代碼中,我們指定 Table註解可以在程序運行週期被獲取到,因此它的生命週期非常的長。
@Documented
這個元註解肯定是和文檔有關。它的作用是能夠將註解中的元素包含到 Javadoc 中去。
@Target
Target 是目標的意思,@Target 指定了註解運用的地方。
你可以這樣理解,當一個註解被 @Target 註解時,這個註解就被限定了運用的場景。
類比到標籤,原本標籤是你想張貼到哪個地方就到哪個地方,但是因爲 @Target 的存在,它張貼的地方就非常具體了,比如只能張貼到方法上、類上、方法參數上等等。@Target 有下面的取值
- ElementType.ANNOTATION_TYPE 可以給一個註解進行註解
- ElementType.CONSTRUCTOR 可以給構造方法進行註解
- ElementType.FIELD 可以給屬性進行註解
- ElementType.LOCAL_VARIABLE 可以給局部變量進行註解
- ElementType.METHOD 可以給方法進行註解
- ElementType.PACKAGE 可以給一個包進行註解
- ElementType.PARAMETER 可以給一個方法內的參數進行註解
- ElementType.TYPE 可以給一個類型進行註解,比如類、接口、枚舉
@Inherited
Inherited 是繼承的意思,但是它並不是說註解本身可以繼承,而是說如果一個超類被 @Inherited 註解過的註解進行註解的話,那麼如果它的子類沒有被任何註解應用的話,那麼這個子類就繼承了超類的註解。
@Retention(value = RetentionPolicy.RUNTIME)
@Inherited
public @interface Table {
String name() default "";
}
@Table
class User{
}
class Member extends User{
}
@Repeatable
Repeatable 自然是可重複的意思。@Repeatable 是 Java 1.8 才加進來的,所以算是一個新的特性。
什麼樣的註解會多次應用呢?通常是註解的值可以同時取多個。比如一支筆,可寫可讀
@Retention(value = RetentionPolicy.RUNTIME)
@Inherited
public @interface Takes {
Pen[] value() default {};
}
@Repeatable(value = Takes.class)
@interface Pen {
String method() default "";
}
@Pen(method="write")
@Pen(method="read")
class XiaoMiPen{
}
如何使用註解
接下來我們自定義三個註解,用來模擬數據庫操作
分別是@Table @Column @TableId
@Documented
@Target(value = ElementType.TYPE)
@Retention(value = RetentionPolicy.RUNTIME)
@Inherited
public @interface Table {
String name() default "tableName";
}
@Target(value = ElementType.FIELD)
@Retention(value = RetentionPolicy.RUNTIME)
@Inherited
public @interface Column {
String columnName() default "columnName";
}
@Target(value = ElementType.FIELD)
@Retention(value = RetentionPolicy.RUNTIME)
@Inherited
public @interface TableId {
String Id() default "tableId";
}
創建一個User對象對應User表
package com.test.demo;
@Table(name = "user")
public class User {
@TableId(Id = "user_id")
private String userId;
@Column(columnName = "user_name")
private String userName;
@Column(columnName = "age")
private int age;
@Column(columnName = "memo")
private String memo;
.....getset方法省略
}
利用Java反射我們可以這麼做。此僅爲demo,沒有具體意義
User user = new User("1","張三",18,"這是個逗比");
StringBuilder sb = new StringBuilder();
Class clazz = Class.forName(user.getClass().getName());
//判斷是不是對應有數據表
Annotation annotation = clazz.getAnnotation(Table.class);
if(Objects.isNull(annotation)){
System.out.println("數據庫沒有這張表");
}
Table aClass = (Table) annotation;
//獲取表名
String tableName = aClass.name();
sb.append("select * from ").append(tableName);
// TODO: 2020/6/29 excuteSql
System.out.println(sb.toString());
//獲取主鍵
String id = "";
for (Field declaredField : clazz.getDeclaredFields()) {
TableId column = declaredField.getAnnotation(TableId.class);
if(!Objects.isNull(column) ){
id = column.Id();
System.out.println(id);
declaredField.setAccessible(true);
//查詢
sb.append(" where "+id +"=" + declaredField.get(user));
System.out.println(sb);
}
}
//獲取其他字段
for (Field declaredField : clazz.getDeclaredFields()) {
Column column = declaredField.getAnnotation(Column.class);
if(!Objects.isNull(column) ){
String name = column.columnName();
System.out.println(name);
}
}