前言

在使用Spring Boot的时候,大量使用注解的语法去替代XML配置文件,十分好用。

然而,在使用注解的时候只知道使用,却不知道原理。直到需要用到自定义注解的时候,才发现对注解原理一无所知,所以要学习一下。

特别注意的是,注解是Java提供的语法,而不是Spring特殊的语法。Java自5.0版本开始引入注解之后,注解就成为了Java平台中非常重要的一部分。

什么是注解(Annotation)

注解(Annotation)是一种应用于类、方法、参数、变量、构造器及包声明中的特殊修饰符。它是一种由JSR-175标准选择用来描述元数据的工具。

用一个词来描述注解,那就是元数据(描述数据的数据)。 那么就可以说,注解是源代码的元数据。

@Override
public String toString() {
return "良辰美景奈何天";
}

在上面的代码中,我重写了toString()方法并在方法上使用了@Override注解。但是,即使我不使用@Override注解标记代码,程序也能正常执行。那么为什么还要使用注解呢?事实上,@Override注解会告诉编译器这个方法是一个重写的方法(描述方法的元数据),如果父类中不存在该方法,编译器便会报错,提示该方法没有重写父类中的方法。但是如果我不小心拼写错误,例如将toString()写成了toSpring(),而且我也没有使用@Override注解,那么程序依然能够编译运行,虽然运行结果会和我期望的大不相同。到这里我们就可以明白什么是注解,还有使用注解既可以帮助编译器检查代码,也可以帮助我们阅读程序。

注解的用途

注解仅仅是元数据,和业务逻辑无关。但是元数据的用户可以通过读取这些元数据并实现必要的逻辑(第三方代码,注解不能干扰源代码的编译运行),以此来附加相应的业务操作。

生成文档。比如我们用的IDE里面会自动加上的@param,@return,@author等注解。

编译时检查格式。比如@Override,@SuppressWarnings等注解。

跟踪代码依赖性,实现替代配置文件功能。比如@Configuration,@Bean等注解。

Java类库提供的元注解

Java类库中提供了java.lang.annotation包,包中包含了元注解和接口,定义自定义注解所需要的所有内容都在这个包中。

比如java.lang.annotation.Annotation是所有注解自动继承的接口,不需要定义时指定,类似于所有类都自动继承Object一样。

@Documented,@Retention,@Target,@Inherited是Java提供的4个元注解。

@Documented注解表示将此注解包含在javadoc中。

@Documented // 生成文档
@Retention(RetentionPolicy.RUNTIME) // 注解在运行期级别保留注解信息
@Target(ElementType.ANNOTATION_TYPE) // 注解放置的目标位置,ANNOTATION_TYPE是可注解在注解定义上
public @interface Documented { }

使用此注解的类会被javadoc工具提取成文档。在doc文档中的内容会因为此注解的信息内容不同而不同,相当于@see,@param等。

@Retention注解表示在什么级别保留该注解信息。

@Documented // 生成文档
@Retention(RetentionPolicy.RUNTIME) // 注解在运行期级别保留注解信息
@Target(ElementType.ANNOTATION_TYPE) // 注解放置的目标位置,ANNOTATION_TYPE表示是可注解在注解定义上
public @interface Retention { RetentionPolicy value(); // 在哪个阶段保留注解信息的级别,必须参数(无默认值)
}

可选的参数值在枚举类型RetentionPolicy中。

public enum RetentionPolicy {
/**
* Annotations are to be discarded by the compiler.
* 注解将被编译器抛弃
*/
SOURCE, /**
* Annotations are to be recorded in the class file by the compiler but need not be retained by the VM at run time. This is the default behavior.
* 注解在class文件中可用,但会被VM抛弃
*/
CLASS, /**
* Annotations are to be recorded in the class file by the compiler and retained by the VM at run time, so they may be read reflectively.
* VM将在运行期也保留注释,因此可以通过反射机制读取注解信息
* @see java.lang.reflect.AnnotatedElement
*/
RUNTIME
}

@Target注解表示该注解用于什么地方(可以注解的目标位置)。

@Documented // 生成文档
@Retention(RetentionPolicy.RUNTIME) // 注解在运行期级别保留注解信息
@Target(ElementType.ANNOTATION_TYPE) // 注解放置的目标位置,这里表示可以注解在注解定义上
public @interface Target {
/**
* Returns an array of the kinds of elements an annotation type
* can be applied to.
* @return an array of the kinds of elements an annotation type
* can be applied to
*/
ElementType[] value(); // 注解的目标位置,必须参数(无默认值)
}

可能的参数值在枚举类型ElementType中。

public enum ElementType {
/**
* Class, interface (including annotation type), or enum declaration
* 类,接口(包括注解类型)或enum声明
*/
TYPE, /**
* Field declaration (includes enum constants)
* 域声明(包括enum实例)
*/
FIELD, /**
* Method declaration
* 方法声明
*/
METHOD, /**
* Formal parameter declaration
* 参数声明
*/
PARAMETER, /**
* Constructor declaration
* 构造器声明
*/
CONSTRUCTOR, /**
   * Local variable declaration
   * 局部变量声明
*/
LOCAL_VARIABLE, /**
* Annotation type declaration
   * 注解量声明
   */
ANNOTATION_TYPE, /**
* Package declaration
* 包声明
*/
PACKAGE, /**
* Type parameter declaration
* 类型变量声明
* @since 1.8 新特性
*/
TYPE_PARAMETER, /**
* Use of a type
* 使用类型的任何语句(声明、泛型和强制转换语句中的类型)
* @since 1.8 新特性
*/
TYPE_USE
}

@Inherited注解表示允许子类继承父类中的注解。

@Documented // 生成文档
@Retention(RetentionPolicy.RUNTIME) // 注解在运行期级别上保留注解信息
@Target(ElementType.ANNOTATION_TYPE) // 注解放置的目标位置,ANNOTATION_TYPE是可注解在注解定义上
public @interface Inherited { }

自定义注解 

使用@interface关键字来自定义注解时,自动继承java.lang.annotation.Annotation接口(隐式继承),由编译程序自动完成其它细节。在定义注解时,不能显式继承其它的注解或接口。@interface关键字用来声明一个注解,其中的每一个方法实际上是声明了一个配置参数。方法的名称就是参数的名称,返回值类型就是参数的类型(返回值类型只能是基本类型、Class、String、enum),可以通过default来声明参数的默认值。

定义注解的通用格式:

public @interface 注解名 {
访问修饰符 返回值类型 参数名() default 默认值;
}

注解参数成员的可支持数据类型:

1.所有基本数据类型(int,float,boolean,byte,double,char,long,short)

2.String类型

3.Class类型

4.enum类型

5.Annotation类型

6.以上所有类型的数组

注解参数的规范:

1.只能用public或默认(default)这两个访问修饰符修饰。一般使用default,即不指定访问修饰符。

2.参数成员只能用上面列出的、注解参数支持的数据类型。

3.如果只有一个参数成员,最好把参数名称设为value。

简单使用例子:

首先定义注解。

@Documented
@Target({ElementType.METHOD, ElementType.TYPE}) // 注解放置的目标位置,这里可以添加在METHOD上,也可以添加在TYPE上
@Retention(RetentionPolicy.RUNTIME) // 注解在运行期保留注解信息,在运行期可以通过反射获取注解信息
public @interface MyAnnotation { String name() default ""; // 姓名,默认值空字符串 String sex() default "男"; // 性别,默认值男
}

将注解使用在类上。

@MyAnnotation(name = "谢小猫")
public class MyAnnotationOnClass { }

将注解使用在方法上。

public class MyAnnotationOnMethod {

    @MyAnnotation(name = "赵小虾")
public void zhaoShrimp() { } @MyAnnotation(name = "李小猪", sex = "女")
public void liPig() { }
}

最后通过反射获取注解信息并打印出来。

public class MyAnnotationTest {

    public static void main(String[] args) {
// 调用Class的isAnnotationPresent方法,检查类MyAnnotationUse是否含有@AnnotationTest注解
if (MyAnnotationOnClass.class.isAnnotationPresent(MyAnnotation.class)) {
// 若存在就获取注解
MyAnnotation myAnnotation = MyAnnotationOnClass.class.getAnnotation(MyAnnotation.class);
System.out.println("完整注解:" + myAnnotation);
// 获取注解属性
System.out.println("性别:" + myAnnotation.sex());
System.out.println("姓名:" + myAnnotation.name());
System.out.println("---------------------------------------------------------");
} // 调用Class的getDeclaredMethods方法,获取MyAnnotationUse的所有方法声明
Method[] methods = MyAnnotationOnMethod.class.getDeclaredMethods();
for (Method method : methods) {
System.out.println("方法声明:" + method);
// 调用Method的isAnnotationPresent方法,检查方法是否含有@AnnotationTest注解
if (method.isAnnotationPresent(MyAnnotation.class)) {
MyAnnotation myAnnotationInMethod = method.getAnnotation(MyAnnotation.class);
System.out.println("方法名:" + method.getName()
+ ",姓名:" + myAnnotationInMethod.name()
+ ",性别:" + myAnnotationInMethod.sex() + ")");
}
}
}
}

查看结果。

通过上面的简单使用例子,我们对注解的使用基本有些了解了:注解的使用与反射是离不开的。我们可以通过Java反射机制读取注解的信息,并根据这些信息更改目标程序的逻辑。

这样,我们就可以利用代码中的注解,达到间接控制程序代码运行的目的。

注解的总结

1.Java中所有注解都隐式继承(自动继承)于java.lang.annotation.Annotation,注解不允许显式继承其他接口。

2.注解不能直接干扰程序代码的运行,无论增加或删除注解,代码都能够正常运行。Java语言解释器会忽略这些注解,而由第三方工具负责对注解进行处理。

3.一个注解可以拥有多个成员,成员声明和接口方法声明类似,成员声明有规范限制。

java注解和自定义注解的简单使用的更多相关文章

  1. Java注解-元数据、注解分类、内置注解和自定义注解|乐字节

    大家好,我是乐字节的小乐,上次说过了Java多态的6大特性|乐字节,接下来我们来看看Java编程里的注解. Java注解有以下几个知识点: 元数据 注解的分类 内置注解 自定义注解 注解处理器 Ser ...

  2. Java中的注解及自定义注解你用的怎么样,能不能像我这样应用自如?

    Java注解提供了关于代码的一些信息,但并不直接作用于它所注解的代码内容.在这个教程当中,我们将学习Java的注解,如何定制注解,注解的使用以及如何通过反射解析注解. Java1.5引入了注解,当前许 ...

  3. Springboot--元注解及自定义注解(表单验证)

    本文简单说明一下元注解,然后对元注解中的@Retention做深入的讨论,在文章最后使用元注解写一个自定义注解来结尾. 一.结论: @Target:注解的作用目标 @Target(ElementTyp ...

  4. Java反射与自定义注解

    反射,在Java常用框架中屡见不鲜.它存在于java.lang.reflact包中,就我的认识,它可以拿到类的字段和方法,及构造方法,还可以生成对象实例等.对深入的机制我暂时还不了解,本篇文章着重在使 ...

  5. java内置注解、元注解和自定义注解

    注解的作用: 1.生成文档 2.跟踪代码依赖性 3.编译时进行格式检查 ---------------------------------------------------------------- ...

  6. Java:深入自定义注解(Annotation)

    在网上找了很多资料也有写的比较好的,但是总有有一点半点的细节没有写出来,在这里自己总结下使用. 使用Java的自定义注解,首先个人需要了解下Java为我们提供的元注解和相关定义注解的语法.(这个我在网 ...

  7. Java注解(自定义注解、view注入)

    注解这东西虽然在jdk1.5就加进来了,但他的存在还是因为使用Afinal框架的view注入才知道的.一直觉得注入特神奇,加了一句就可以把对应view生成了. 下面我们来认识一下注解这个东西 一.注解 ...

  8. Java中的自定义注解

    ## 元注解 要声明一个注解, 我们需要元注解, 元注解是指注解的注解,包括@Retention, @Target, @Document, @Inherited. @Retention 注解的保留位置 ...

  9. Spring注解之-自定义注解

    1.自定义注解,先自定义三个水果属性的注解 元注解: java.lang.annotation提供了四种元注解,专门注解其他的注解(在自定义注解的时候,需要使用到元注解):   @Documented ...

随机推荐

  1. CentOS TinyProxy http(s)上网代理及置代理上网的方法

    http://blog.csdn.net/fwj380891124/article/details/42168683 http://computer.uoh.edu.cn/linux/2159.htm ...

  2. 在线编辑器ACE Editor的使用

    ACE 是一个开源的.独立的.基于浏览器的代码编辑器,可以嵌入到任何web页面或JavaScript应用程序中.ACE支持超过60种语言语法高亮,并能够处理代码多达400万行的大型文档.ACE开发团队 ...

  3. 雅礼集训【Day6-1】字符串

    雅礼集训[Day6-1]字符串 假设我们有串\(a\),我们设\(a'\)为\(a\)翻转后按为取反过后的串. 我们只考虑前一半的,长为\(m\)的串.如果前半截匹配了\(a\)或者\(a'\),则\ ...

  4. python nt, bool, str 试题

    # 1.有变量量name = "aleX leNb" 完成如下操作:name = "aleX leNb"# 1)移除 name 变量对应的值两边的空格,并输出处 ...

  5. C#深度学习の委托深度解析

    一.我们在使用C#的过程中,不可避免的用到了委托. 委托的本质是什么呢? 从语法上看,委托是对方法的抽象封装,例如:public void print1(),public void print2(), ...

  6. 一款国内好用的Linux发行版?Deepin(深度)Linux

    一款国内好用的Linux发行版?Deepin(深度)Linux 目前来说,要将Linux作为桌面解决方案,对于大多数PC用户来说,当然是不现实的,毕竟Linux的主力用户群体依然是少数极客用户.说白了 ...

  7. js中arguments详解

    在js中一切都是对象,连函数也是对象,函数名其实是引用函数定义对象的变量. 什么是arguments? 这个函数体内的arguments非常特殊,实际上是所在函数的一个内置类数组对象,可以用数组的[i ...

  8. 在ASP.NET MVC中使用Redis

    一.Redis基本认知 1.含义: REmote DIctionary Server(Redis) | 是一个key-value存储系统 2.特性: 2.1 持久化:可以将内存中的数据保存在磁盘中,重 ...

  9. ASM problem : ORA-15001: diskgroup "DGROUP1" does not exist or is not mounted ORA-15040: diskgroup is incomplete

    ============================================================= mos中的详细解释: ODA: After Apply ODA 12.2.1 ...

  10. Vue2.x源码学习笔记-Vue静态方法和静态属性整理

    Vue静态方法和静态属性,其实直接在浏览器中可以查看到的,如下 圈起来的是其静态属性,但是有的属性对象中的属性的值又是函数.未圈起来的则是函数. 其实它来自如下各个目录下的js文件 // src/co ...