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

原文链接 - Joker's
欢迎大家访问我的个人博客:)

大约有一个月没写博啦,中间经历了急性支气管炎、大雪封山、懒癌附身种种,这次想写的是「注解」,一个之前一直很惧怕的东西。

起因是跟着【手把手】JavaWeb 入门级项目实战 -- 文章发布系统 (第四节)这篇博文在做入门的 jsp 项目,所以接触到了。接触到了,自然就要了解一下。部分内容出自原博客,侵删。

我是后端新手,以前完全没接触过后端项目,所以做的时候理解上会有问题,同时我也想把自己遇到的这些问题,用浅显易懂的方式说出来,可以供有相同疑惑的朋友参考。如有不足之处,欢迎指正。

这篇文章可能有点长,但写的应该比较容易看懂,如果你也是新手,希望你能看到最后,不明白的部分可以留言给我讨论。文章整体脉络应该是比较清晰:首先介绍注解是什么,然后再看注解的定义,最后是如何使用,把这些讲完之后我又举了一个完整的实例来讲解。


目录

Step 1. 注解是什么

首先介绍一下,注解到底是个啥?为什么用途这么广泛?

Step 2. 注解怎么写

初步了解以后,让我们直接上手写一个!

Step 3. 注解如何用

写完注解不会用相当于没有写,那么我们如何使用呢?
3.1 给谁用?
3.2 怎么用?
3.3 几个注意点

Step 4. 看一个完整的例子:获取数据库表名

从零开始,展示一个完整的注解用法。
4.1 第 0-1 步:给谁用?(JavaBean)
4.2 第 0-2 步:注解怎么写(定义注解)
4.3 第 1 步:获取类的对象
4.4 第 2 步:获得相应的方法/域
4.5 第 3 步:获取注解的实例
4.6 第 4 步:获取对应的属性

参考


正文

Step 1. 注解是什么

原博主写了一篇文章非常好,推荐仔细看一遍:用大白话聊聊JavaSE -- 自定义注解入门。我看了三遍,才感觉理解个差不多。

我简单总结一下原博主的思想:注解就像是写给电脑看的注释,有了注解,我们可以通过代码来获得关于一个方法或者一个类的信息。在平时的开发过程中,我们会给一个类、方法或者域添加注释,比如,我们会在方法前面添加关于入参、返回值、以及方法作用的注释。同样的,我们可以给一个类、方法或者域添加注解。

同时,你仔细观察的话,会发现注解非常像一个空的接口。

了解了这些内容,我来介绍一下元注解的概念。典型的元注解有 @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 "作者很懒,没有写本方法的作用";
    // 方法创建时间
    String createTime();
}

很显然,我们可以通过这个注解来获取方法的作用和创建时间,当然,我们也可以加上作者,无非是再加这么一行:String author() default "Joker";。此外,在定义注解的过程中,默认值是可选的。

Step 3. 注解如何用

好,通过上面两部分,我们已经了解了注解是什么,以及如何写一个简单的注解,接下来看一看注解是怎么用的。这要分为两部分来说。

3.1 给谁用?

既然明白了我们刚刚编写的注解时用来写给方法的,那么我们想获得哪个方法的作用和创建时间呢?

让我们来随便写一个:

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);
    }
}

现在目标明确了,我们是想获得 formatDate 这个方法的作用和创建时间,那就先给方法加上我们刚刚创建的注解,也就是@MethodAnnotation()。括号里面要填入注解中对应的参数和值,有默认值的可以不用写。如果参数没有默认值,且在这里没有填写的话(比如在这里只写一个空括号),ide 会报 The annotion @MethodAnnotation must define the attribute createTime错误。

3.2 怎么用?

计算机如何在 Java 类中获取我们上面定义的注解信息呢?

那我们不得不聊聊另外一个名词:反射。我们先来看一个例子(这里我还是使用原作者的例子,侵删),在 DateUtil.java 中创建一个单元测试(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("创建日期:" + methodNote.createTime());
}

首先读一下上面的代码,自己想一想,然后再来看我的总结。

---------------------------------------------------分割线---------------------------------------------------

关于上面的反射过程,我个人总结为四部曲:

  1. 获取类的对象。如果在编译器能够知道名字的话,可以使用上面的方法;如果在编译器不知道类的名字,但是可以在运行期获得到类名字符串的话,可以使用以下方法:
String className = ... ;//在运行期获取的类名字符串
Class class = Class.forName(className);
  1. 根据类对象获得相应的方法/域,如果注解是加在类上的,可以省略这一步。主要方法有(具体用法可以自己去查询 API 文档):
  • Field getField()
  • Field[] getFields() 返回 Field 数组,包括这个类及其父类的公有域。
  • Field[] getDeclaredFields() 返回 Field 数组,包括这个类的所有域。
  • Method getMethod()
  • Method[] getMethods() 返回所有的公有方法,包括从父类继承来的公有方法。
  • Method[] getDeclaredMethods() 返回所有的方法,但不包括由父类继承来的方法。
  1. 根据获得的域/方法/类对象,结合 getAnnotations() 方法来获取注解的实例。
  2. 根据注解实例,获取对应的信息。结束!

3.3 几个注意点

  • 不要漏写 default 值。
  • 在第二步时,注意看下用到的方法是否抛出异常。如果有的话,在3中不要忘记抛出。

Step 4. 看一个完整的例子:获取数据库表名

这里的情景是,用户在登陆页面上输入了用户名和密码,点登陆的时候,发送请求到我们的 controller 层,此时我们把用户数据封装为一个 JavaBean,再把 JavaBean 转化为建表语句。为了简化过程,最后一步我省略掉,我们只看怎么获取表名即可。

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

我简单解释一下 JavaBean 和数据库的对应关系:写 JavaBean 的过程其实就相当于是数据库的设计过程,对应着属性对应着字段

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 类加上注解,参数设置为 t_user,即表名:

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

4.3 第 1 步:获取类的对象

这里我们封装一个 TableUtils 作为数据库表的工具类,来使用注解,真正的调用过程我们在单元测试中进行。在刚刚讲解的过程中,其实是把这两部分合二为一了。在实际开发过程中,可能要将不同的模块分包处理,比如 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 完成,感谢大家看到最后~

参考

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,386评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,939评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,851评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,953评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,971评论 5 369
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,784评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,126评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,765评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,148评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,744评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,858评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,479评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,080评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,053评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,278评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,245评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,590评论 2 343