Java注解
前言
近日在閱讀開源專案,發現專案里好多奇奇怪怪的注解(@DataScope、@Log...)看得我一臉懵,不知道大家是否也有過這樣的經歷,回想了一下,發現自己對于注解的知識,好像只停留在@Override,,,例外尷尬,所以今天就補補注解這個知識,并把自己的識訓記錄在此,與大家一同交流,如有不對的地方,敬請指正!
希望本文能給讀者帶來以下識訓:
- 明白注解是什么,大概有什么用
- 能理解別人代碼里面注解的作用
- 能使用自定義注解
什么是注解
想要了解某個知識點,我首先推薦的都是去官網查看,下面看看Java官方對注解的解釋:
Annotations, a form of metadata, provide data about a program that is not part of the program itself. Annotations have no direct effect on the operation of the code they annotate.
注解是元資料的一種形式,它提供有關程式的資料,但這些資料不是程式本身的一部分,注解對它們注釋的代碼的操作沒有直接影響,
一堆英文讀完,一陣云里霧里,沒關系,這是正常操作,不過我們從翻譯中還是可以了解到注解可以提供資料,并且資料是獨立于程式的,那么我們大致可以推斷出,注解其實是介于程式和資料之間的一種媒介,程式和資料通過注解達成了某種聯系,即注解類似一根紅線,把資料和程式關聯在一起,
從@Override開始
通過對Java官方提供的注解解釋的翻譯,我們篩選推斷出了一個關鍵資訊——關聯,那到底如何理解這個詞呢?別急,我們從最熟悉的陌生人@Override開始,最熟悉是因為我們知道這是方法重寫,子類覆寫父類方法用到的注解,陌生是因為我們從來沒有點進去了解過這個注解,那接下來就進去看看吧!
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
短短的5行,好像除了第一行,其他啥都不知道,,,不急,我們一行一行來解讀!
- 注解匯入了一個
annotation包 - 注解的“套娃”行為
@Target(ElementType.METHOD)、@Retention(RetentionPolicy.SOURCE) - 不同于介面和類的宣告
public @interface Override { }
除了對新注解不認識,我們大致可以了解到注解的定義格式,修飾符 @interface 注解名{},(有點介面的感覺)
禁止套娃——元注解
通過對@Override的剖析,我們了解了注解的定義格式,不過我們發現注解里面又有新的注解,本著刨根問底的好奇心,我們繼續進入@Target注解一探究竟!
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
ElementType[] value();
}
一直點擊,發現始終在@Documented、@Retention、@Target這幾個注解之間套娃,通過Java檔案我們了解到原來這些修飾注解的注解叫做元注解,元注解(meta-annotation)在java.lang.annotation包下:
@Retention
表示如何存盤被標記的注解(指定存盤級別),有以下三個級別
- RetentionPolicy.SOURCE:只保留到原始碼級別,在編譯階段會被忽略,所以他們不會被寫入位元組碼,
- RetentionPolicy.CLASS:(默認)編譯級別,在編譯時由編譯器保留,但被Java虛擬機(JVM)忽略,
- RetentionPolicy.RUNTIME:由JVM保留,可以在運行時環境使用,
@Target
表示被標記的注解可以用于哪種java元素(類、介面、屬性、方法......),有以下八種
| 作用域 | 解釋 |
|---|---|
| ElementType.ANNOTATION_TYPE | 可用于注解型別 |
| ElementType.CONSTRUCTOR | 可以用于建構式 |
| ElementType.FIELD | 可以用于欄位或者屬性 |
| ElementType.LOCAL_VARIABLE | 可以用于區域變數 |
| ElementType.METHOD | 可以用于方法級注解 |
| ElementType.PACKAGE | 可以用于包宣告 |
| ElementType.PARAMETER | 可以用于方法的引數 |
| ElementType.TYPE | 可以用于類的任何元素 |
@Documented
無論何時使用指定的注解,都應使用Javadoc工具記錄這些元素,(即會在生成的javadoc中加入注解說明)
@Inherited
可以從超類繼承注釋型別,僅用于類的宣告(介面不會繼承)
@Repeatable
在Java SE 8中引入的,表示標記的注釋可以多次應用于相同的宣告或型別使用,
注解的分類
通過對元注解的了解,我明白了一個注解都是由這些元注解修飾而來,而且我們也識訓了一個重要資訊——注解可以修飾注解
這樣無限的套娃,就會有各種各樣的注解,那么到底有哪些注解呢?常見的注解大致分為以下四類:
元注解
即上文提及的5個元注解
jdk注解
常見的如@Override @Deprecated @SuppressWarnings @SafeVarargs @FunctionalInterface
第三方注解
即第三方框架提供的注解,例如自動注入依賴@Autowired、@Controller等
自定義注解
即開發人員根據專案需求自定義的注解,用于一些工具在編譯、運行時進行決議和使用,起到說明、配置的功能,
實戰——定義自己的注解
看過了Java提供的注解,相信你已經對注解有個大致的了解了,那你有沒有想過,注解是如何化腐朽為神奇,加了一個簡單的@Autowired就能實作依賴注入、@Setter就能實作set方法的生成,下面通過簡單的實戰來體會一下注解的神奇之處吧!
實戰目標:
使用自定義注解,通過在物體類及其屬性上加注解,實作對物體類查詢sql陳述句的構造
ps:類似
select * from t_user where t_name='kingwan'的形式
自定義注解的撰寫規則
在開始實戰之前,我們先了解一下撰寫自定義注解的規則:
- 注解的定義為
@interface,所有的注解會自動繼承java.lang.Annotation這個介面,并且不能再去繼承別的類或者介面 - 引數成員只能用
public或default(默認)訪問權限符修飾 - 引數成員只能用八大基本資料型別、
String、Enum、Class、annotations等資料型別,以及這些型別的陣列 - 要獲取類方法和欄位的注解資訊,必須通過java反射機制來獲取
- 注解也可以沒有定義成員(只起到標識作用)
了解了注解的定義規范,接下來我們開始進入正式的實戰環節,
1.自定義注解@KingwanTable、KingwanColumn
對于物體類查詢的sql陳述句,我們需要知道兩個資訊:①查詢的表名②欄位名,并且我們通常習慣將用戶表t_user對應于物體類User,那么我們如何和把t_user和User進行關聯呢?一想到關聯,回顧我們最開始從官方檔案中提取出來的資訊,沒錯,就是使用注解關聯,接下來定義兩個自定義注解:
-
@KingwanTable:注解物體類對應的表名@Target(ElementType.TYPE)//作用在類/介面上 @Retention(RetentionPolicy.RUNTIME)//保留作用域:保留到運行時 public @interface KingwanTable { String value();//引數:表名 } -
@KingwanColumn:注解物體類屬性對應的表欄位名@Target(ElementType.FIELD)//表示作用在欄位上 @Retention(RetentionPolicy.RUNTIME)//保留到運行時 public @interface KingwanColumn { String value();//引數:欄位名 }
2.物體類添加上自定義注解
有了自定義的兩個注解,那么我們現在就可以把它們加在物體類上,
-
以下代碼定義了一個
Student物體類,加上了@KingwanTable("t_student")映射表名,@KingwanColumn("stu_birth")映射欄位名,@Data//簡化物體類的set、get方法 @KingwanTable("t_student") public class Student { @KingwanColumn("stu_name") private String stuName; @KingwanColumn("stu_age") private Integer stuAge; @KingwanColumn("stu_birth") private Date stuBirth; } -
以下代碼創建了一個student物件,并初始化資訊
public static void main(String[] args) { Student student = new Student(); //初始化資訊 init(student); } private static void init(Student student) { student.setStuName("kingwan"); student.setStuAge(18); student.setStuBirth(new Date()); }
3.反射獲取注解資訊
有了一個加了自定義注解的Student物體類,那么我們想要構造SQL,就有以下思路:
獲取到注解的資訊(獲取表名、欄位名)=>獲取屬性的值(欄位值)=>構造SQL
如何獲取呢?規則里說了,使用反射,
以下代碼通過獲取student的class物件,獲取類上的注解@KingwanTable資訊,
aClass.isAnnotationPresent:判斷指定的注解是否存在
public static void main(String[] args) throws Exception {
StringBuffer sql = new StringBuffer("");//即將拼接的SQL陳述句
Student student = new Student();
//初始化資訊
init(student);
//反射獲取class類
Class<? extends Student> aClass = student.getClass();
//1. 判斷物體類上是否存在注解@KingwanTable
boolean exist = aClass.isAnnotationPresent(KingwanTable.class);//傳入我們自定義的注解類
String tableName = null;
if(exist){
//1.1 存在注解即獲取注解值---(表名)
KingwanTable annotation = aClass.getAnnotation(KingwanTable.class);
tableName = annotation.value();
sql.append("select * from ").append(tableName).append(" where 1=1");//拼接SQL
}
System.out.println(sql);
}
此時SQL列印的結果:
獲取到了類上的注解資訊,接下來我們來看看如何獲取屬性上的注解資訊
//2. 獲取屬性上的注解
Field[] fields = aClass.getDeclaredFields();
for (Field field : fields) {
//2.1 遍歷每個屬性上是否有KingwanColumn注解
KingwanColumn column = field.getAnnotation(KingwanColumn.class);
if( column != null){
//2.1.1 獲取該屬性的值
String fieldName = field.getName();//屬性名
String methodName = "get"+fieldName.substring(0,1).toUpperCase()+fieldName.substring(1);//構造getter方法
Method method = aClass.getMethod(methodName);
//通過反射代理呼叫get方法,獲取屬性的值(name='kingwan',age=18....)
Object invoke = method.invoke(student);
if(invoke instanceof String){
String value = https://www.cnblogs.com/zhangqingwang/archive/2020/11/08/(String) invoke;
//sql拼接
sql.append(" and ").append(column.value()).append("=").append("'")
.append(value).append("'");
}else{
//想想還有哪些情況...
}
}
System.out.println(sql);
}
此時SQL的結果:
當然,如果有小伙伴跟著本文敲,可能在這一步就走不下去了,這是因為我們的get方法回傳的欄位型別多種多樣,所以僅僅invoke instanceof String是不夠的,我們還需要考慮其他情況(Integer、Date),限于篇幅原因,這里不做過多介紹,大家完全可以自行補充,如果想了解我的實作思路,移步:案例原始碼地址
這樣,是不是就達到了我們要的效果了,對于任意簡單物體類,我們都可以通過加上該注解實作一個簡單的查找SQL的生成
你學廢了嗎!??????
總結
注解有什么用
相信大家看我之后可能會有疑問,注解好復雜,費一大堆功夫,還不如直接點呢!的確,我最開始也覺得注解有點雞肋,不過用久了之后,發現真香!而且注解的作用不僅僅這些,本文的目的是讓大家對注解有一個簡單的了解,當你看到別人寫的注解是多么巧妙時,你也許就會發現,原來注解這么好用!
- 在編譯時進行格式檢查,如
@Override - 跟蹤代碼依賴性,實作替代組態檔功能,通過處理注解資訊生成代碼、XML檔案,
- 一些注釋可以在運行時進行檢查
結尾一張圖
一張思維導圖總結一下內容,保存下來,時常復習!

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/207457.html
標籤:其他
上一篇:第一次作業-四則運算生成程式
