我的后端打怪升級之路一:注解

原文鏈接 - Joker's
歡迎大家訪問我的個人博客:)

大約有一個月沒寫博啦,中間經(jīng)歷了急性支氣管炎、大雪封山、懶癌附身種種,這次想寫的是「注解」,一個之前一直很懼怕的東西。

起因是跟著【手把手】JavaWeb 入門級項目實戰(zhàn) -- 文章發(fā)布系統(tǒng) (第四節(jié))這篇博文在做入門的 jsp 項目,所以接觸到了。接觸到了,自然就要了解一下。部分內容出自原博客,侵刪。

我是后端新手,以前完全沒接觸過后端項目,所以做的時候理解上會有問題,同時我也想把自己遇到的這些問題,用淺顯易懂的方式說出來,可以供有相同疑惑的朋友參考。如有不足之處,歡迎指正。

這篇文章可能有點長,但寫的應該比較容易看懂,如果你也是新手,希望你能看到最后,不明白的部分可以留言給我討論。文章整體脈絡應該是比較清晰:首先介紹注解是什么,然后再看注解的定義,最后是如何使用,把這些講完之后我又舉了一個完整的實例來講解。


目錄

Step 1. 注解是什么

首先介紹一下,注解到底是個啥?為什么用途這么廣泛?

Step 2. 注解怎么寫

初步了解以后,讓我們直接上手寫一個!

Step 3. 注解如何用

寫完注解不會用相當于沒有寫,那么我們如何使用呢?
3.1 給誰用?
3.2 怎么用?
3.3 幾個注意點

Step 4. 看一個完整的例子:獲取數(shù)據(jù)庫表名

從零開始,展示一個完整的注解用法。
4.1 第 0-1 步:給誰用?(JavaBean)
4.2 第 0-2 步:注解怎么寫(定義注解)
4.3 第 1 步:獲取類的對象
4.4 第 2 步:獲得相應的方法/域
4.5 第 3 步:獲取注解的實例
4.6 第 4 步:獲取對應的屬性

參考


正文

Step 1. 注解是什么

原博主寫了一篇文章非常好,推薦仔細看一遍:用大白話聊聊JavaSE -- 自定義注解入門。我看了三遍,才感覺理解個差不多。

我簡單總結一下原博主的思想:注解就像是寫給電腦看的注釋,有了注解,我們可以通過代碼來獲得關于一個方法或者一個類的信息。在平時的開發(fā)過程中,我們會給一個類、方法或者域添加注釋,比如,我們會在方法前面添加關于入?yún)?、返回值、以及方法作用的注釋。同樣的,我們可以給一個類、方法或者域添加注解。

同時,你仔細觀察的話,會發(fā)現(xiàn)注解非常像一個空的接口。

了解了這些內容,我來介紹一下元注解的概念。典型的元注解有 @Target@Retention ,這是我們在定義一個注解時需要用到的,

@Target 用來定義你的注解用在什么地方:

  • @Target(ElementType.FIELD) 表示用于一個域
  • @Target(ElementType.METHOD) 表示用于一個方法
  • @Target(ElementType.TYPE) 則表示用于一個類。

@Retention 用來定義注解在哪一個級別可用:

  • @Retention(RetentionPolicy.SOURCE) 表示在源代碼中可用
  • @Retention(RetentionPolicy.CLASS) 表示在類文件中可用
  • @Retention(RetentionPolicy.RUNTIME) 表示在運行時可用,一般用的比較多。

Step 2. 注解怎么寫

下面讓我們來看一下注解的格式,我們參照原作者的方式,來給方法寫一個注解:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MethodAnnotation {
    // 方法作用,有默認值
    String description() default "作者很懶,沒有寫本方法的作用";
    // 方法創(chuàng)建時間
    String createTime();
}

很顯然,我們可以通過這個注解來獲取方法的作用和創(chuàng)建時間,當然,我們也可以加上作者,無非是再加這么一行:String author() default "Joker";。此外,在定義注解的過程中,默認值是可選的。

Step 3. 注解如何用

好,通過上面兩部分,我們已經(jīng)了解了注解是什么,以及如何寫一個簡單的注解,接下來看一看注解是怎么用的。這要分為兩部分來說。

3.1 給誰用?

既然明白了我們剛剛編寫的注解時用來寫給方法的,那么我們想獲得哪個方法的作用和創(chuàng)建時間呢?

讓我們來隨便寫一個:

package util;

import java.util.Date;
import java.text.SimpleDateFormat;

public class DateUtil {
    @MethodAnnotation(createTime = "2018-01-01")
    public static String formatDate(Date date , String formatPattern){
        return new SimpleDateFormat(formatPattern).format(date);
    }
}

現(xiàn)在目標明確了,我們是想獲得 formatDate 這個方法的作用和創(chuàng)建時間,那就先給方法加上我們剛剛創(chuàng)建的注解,也就是@MethodAnnotation()。括號里面要填入注解中對應的參數(shù)和值,有默認值的可以不用寫。如果參數(shù)沒有默認值,且在這里沒有填寫的話(比如在這里只寫一個空括號),ide 會報 The annotion @MethodAnnotation must define the attribute createTime錯誤。

3.2 怎么用?

計算機如何在 Java 類中獲取我們上面定義的注解信息呢?

那我們不得不聊聊另外一個名詞:反射。我們先來看一個例子(這里我還是使用原作者的例子,侵刪),在 DateUtil.java 中創(chuàng)建一個單元測試(main 方法)如下:

public static void main(String[] args) throws NoSuchMethodException, SecurityException {
    Class classOfDateUtil = DateUtil.class;
    Method formatDate = classOfDateUtil.getMethod("formatDate", Date.class,String.class);
    MethodAnnotation methodNote = formatDate.getAnnotation(MethodAnnotation.class);

    System.out.println("方法描述:" + methodNote.description());
    System.out.println("創(chuàng)建日期:" + methodNote.createTime());
}

首先讀一下上面的代碼,自己想一想,然后再來看我的總結。

---------------------------------------------------分割線---------------------------------------------------

關于上面的反射過程,我個人總結為四部曲:

  1. 獲取類的對象。如果在編譯器能夠知道名字的話,可以使用上面的方法;如果在編譯器不知道類的名字,但是可以在運行期獲得到類名字符串的話,可以使用以下方法:
String className = ... ;//在運行期獲取的類名字符串
Class class = Class.forName(className);
  1. 根據(jù)類對象獲得相應的方法/域,如果注解是加在類上的,可以省略這一步。主要方法有(具體用法可以自己去查詢 API 文檔):
  • Field getField()
  • Field[] getFields() 返回 Field 數(shù)組,包括這個類及其父類的公有域。
  • Field[] getDeclaredFields() 返回 Field 數(shù)組,包括這個類的所有域。
  • Method getMethod()
  • Method[] getMethods() 返回所有的公有方法,包括從父類繼承來的公有方法。
  • Method[] getDeclaredMethods() 返回所有的方法,但不包括由父類繼承來的方法。
  1. 根據(jù)獲得的域/方法/類對象,結合 getAnnotations() 方法來獲取注解的實例。
  2. 根據(jù)注解實例,獲取對應的信息。結束!

3.3 幾個注意點

  • 不要漏寫 default 值。
  • 在第二步時,注意看下用到的方法是否拋出異常。如果有的話,在3中不要忘記拋出。

Step 4. 看一個完整的例子:獲取數(shù)據(jù)庫表名

這里的情景是,用戶在登陸頁面上輸入了用戶名和密碼,點登陸的時候,發(fā)送請求到我們的 controller 層,此時我們把用戶數(shù)據(jù)封裝為一個 JavaBean,再把 JavaBean 轉化為建表語句。為了簡化過程,最后一步我省略掉,我們只看怎么獲取表名即可。

關于 JavaBean 大家可以看看原博主的這篇文章:用大白話聊聊JavaSE -- 如何理解Java Bean(一),寫的非常好。

我簡單解釋一下 JavaBean 和數(shù)據(jù)庫的對應關系:寫 JavaBean 的過程其實就相當于是數(shù)據(jù)庫的設計過程,對應著屬性對應著字段。

4.1 第 0-1 步:給誰用?(JavaBean)

既然是用戶,我們新建一個 User 類,我只抽了一部分。

public class User {
    private String username;
    private String password;
    ...
    
    public void setUsername(String username) {
        this.username = username;
    }
    
    public String getUsername() {
        return username;
    }
    
    public void setPassword(String password) {
        this.password = password;
    }
    
    public String getPassword() {
        return password;
    }
}

4.2 第 0-2 步:注解怎么寫(定義注解)

我們新建一個 Annotation 文件,就叫 Table。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Table {
    public String tableName(); 
}

這里把導包過程省略了,請大家自行補充。

這步做完之后,我們給 0-1 中的 User 類加上注解,參數(shù)設置為 t_user,即表名:

@Table(tableName = "t_user")
public class User {
}

4.3 第 1 步:獲取類的對象

這里我們封裝一個 TableUtils 作為數(shù)據(jù)庫表的工具類,來使用注解,真正的調用過程我們在單元測試中進行。在剛剛講解的過程中,其實是把這兩部分合二為一了。在實際開發(fā)過程中,可能要將不同的模塊分包處理,比如 annotions 包、util 包、bean 包等等。

// 作為工具類API使用,直接傳入類的對象
public class TableUtils {
    public static String getCreateTablename(Class<?> clazz) {
        
    }
}
// 在這里傳入類
public class TestMain {
    public static void main(String[] args) {
        TableUtils.getCreateTablename(User.class);
    }
}

4.4 第 2 步:獲得相應的方法/域

因為我們的注解是加在類上的,所以這一步可以省略。

4.5 第 3 步:獲取注解的實例

既然是獲取注解的實例,肯定是在直接使用注解的地方,也就是 TableUtils 中進行。

public static String getCreateTablename(Class<?> clazz) {
    Table table = clazz.getAnnotation(Table.class);        
}

4.6 第 4 步:獲取對應的屬性

這里我們要獲取表名,可以控制臺打印下,看看是不是我們的 t_user。

public static String getCreateTablename(Class<?> clazz) {
    Table table = clazz.getAnnotation(Table.class); 
    String tableName = table.tableName();
    System.out.println(tableName);
}

至此,一個完整的注解 demo 完成,感謝大家看到最后~

參考

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內容