文章目录
前言
Java中的Annotation笔者相信大家都不陌生,未使用框架的时候,如 @Override @SuppressWarnings 。使用Spring框架的时候 @Controller、@Mapping等,可以说Annotation是Java世界中不可或缺的医院,笔者想借本文来简单谈谈对于Annotation的理解。
参考
- The Java™ Tutorials - Lesson: Annotations
- @Getter and @Setter - lombok offical doc
- Annotation - Java8 oracle doc
Annotation(注解)的定义
在讨论开始之前,我们先明确在Java世界里Annotation的定义。
Oracle官方的文档里这样写着
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.
“Annotations, 一种元数据,其并是可执行代码本身的一部分,只是提供了一些关于这部分程序的元信息。Annotations并不会对他们标记的代码产生直接影响”
可以得知我们使用Annotation并不会直接作用在程序上,这或许难以理解(如Spring的Annotation明明可以改变程序的行为!),笔者换个说法
如果没有外部框架如Spring、Lombok、Mybatis等的时候,Annotation并不会直接作用在程序上。
Annotation并不会直接改变程序行为,但如果程序本身包含了如Spring控制的部分,那么Annotation里提供的信息会令框架等对此信息做出相应的反应,从而导致程序行为改变(被配置)。
Annotation的作用
Annotation一般有如下的作用
- 为编译器提供信息:Annotations能够为编译器提供检查错误或者禁止警告(Suppress warning)的信息。
- 编译时:编译时能够为外部工具提供信息,以便生成字节码等。如Lombok为你的Java POJO类生成Getter/Setter,你仅需要使用@Setter、@Getter即可。
- 运行时:Annotation信息能够在运行时被加载,而被使用。典型的有Spring框架的@Controller等,能在Spring框架扫描的时候知道,“wow,这个类是Controller,我应该留意他” 或是 “wow,这个类的方法要匹配"/user"路径,来这种请求的时候我应该交给这个方法处理。”。
[实践] 实现自己的Annotation
笔者将在本节实现两个Annotation,并通过下一节来实验,Annotation的信息是如何被获取和利用的。
1. 在运行时会被加载到内存的Annotation
@Retention(RUNTIME) 表明这个标注的信息会被加载到Java程序运行时的内存里,这也是大部分框架注解使用的。毕竟如果内存里没有这些信息,框架也无从得知应该如何配置自己的行为。
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Documented
@Retention(RUNTIME)
@Target(TYPE)
public @interface MyRunTimeAnnotation {
String message() default "打印\"Hello Annotation\",如果你看到这个标记。";
}
2. 在运行时不会被加载到内存的Annotation
@Retention(SOURCE) 或是 @Retention(CLASS)是不会导致注解被加载到JVM内存的。SOURCE通常仅用于提示开发者某些信息。如下你可以写@Author注解用于标注代码的作者~
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.SOURCE;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Documented
@Retention(SOURCE)
@Target(TYPE)
public @interface Author {
String value();
}
[实践] 运行时获取Annotation的信息
在上面我们设计了两个注解,一个能在运行时被加载,一个不能,那么我们来验证一下究竟是不是如此。
import java.lang.annotation.Annotation;
@MyRunTimeAnnotation()
@Author("虎猫")
public class AnnotatedTestClass {
public static void main(String[] args) {
Class<AnnotatedTestClass> clazz = AnnotatedTestClass.class;
for (Annotation annotation : clazz.getAnnotations()) {
System.out.println(annotation);
// 运行时获取自定义注解的内部信息。
if (MyRunTimeAnnotation.class.isInstance(annotation)) {
final MyRunTimeAnnotation myAnnotation = (MyRunTimeAnnotation)annotation;
System.out.println(myAnnotation.message());
}
}
}
}
如果你跑一下会发现结果如下,能看到我们的类运行时只有一个MyRunTimeAnnotation而Author注解的信息没有被加载到内存里。
@per.eicho.test.annotation.MyRunTimeAnnotation(message=任意消息能在运行时被看到)
任意消息能在运行时被看到
通过上述例子,我们能够发现一些信息是能够通过注解Annotation在运行时或者编译时传递给外部工具如编译器、Lombok或如Spring等框架的,如果我们按照外部工具约定好的格式提供对应信息,那么就能够成功”告知“对方我们的意图并被理解。而对方也能根据这些信息做出相应的反应,如以下两个注解就是告诉Spring框架来了/users/{id}这样的Get请求请交给getUser方法处理,而PathVariable注解则告诉Spring框架调用的时候请把路径里的id部分单独抽出来放到这个ID参数里。
@GetMapping("/users/{id}")
public String getUser(@PathVariable("id") String ID)
结语
Annotation,注解是提供信息的一种方式,注解是多面手能为许多不同的对象提供信。当其提供的信息能够被加载到运行时的时候,其作用就像配置文件、配置表、程序启动传入的参数时,都是为程序运行提供配置信息。Spring、Mybatis等框架就是利用这些信息来对其行为作出配置,理解注解和其中的信息是如何被使用的是理解流行框架的基础。我是虎猫,希望本文能够帮到你。