注解入门

简介: 注解入门

注解的定位

首先要回答的的第一个问题是注解的定位是什么?带着这个问题我去翻了官方写的指导书《The Java™ Tutorials》,在注释这一节的开始,有如下文字:

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. ---《The Java™ Tutorials》

注解是一种元数据,提供关于程序的信息,但并不是程序的有机组成,注解并不对被它修饰的代码直接起作用。

##那么有啥用处呢? 《The Java™ Tutorials》:

  • Information for the compiler — Annotations can be used by the compiler to detect errors or suppress warnings.
  • 给程序员提供一些信息,比如说注释可以给程序提示过时警告或者显示一些错误(@SuppressWarnings和@Override请出来说话)
  • Compile-time and deployment-time processing — Software tools can process annotation information to generate code, XML files, and so forth.
  • 在编译时和部署时被处理,一些工具可以根据注解提供的信息来产生代码,XML文件等等.(@Data注解出来挨打),@Data注解是 Lombok插件锁提供,适用于这样的场景: 你写了一些属性,但是你不想写get和set方法,你就只需要在类上打上@Data注解,Lombok就可以自动帮你产生get和set方法、无参构造。
  • Runtime processing — Some annotations are available to be examined at runtime.
  • 作用于运行时,一些注解能够在运行时被检测到。(@Controller,@Autowired出来挨打) @Controller是SpringMVC提供的注解,一个类打上@Controller注解后,成为一个控制器。

那该怎么用呢?

如下所示我定义了一个注解:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Inherited
public @interface SimpleAnnotation {
    String name() default "";
}
复制代码

从@interface来看,注解更像是一个接口(事实上它就是一个接口,后文会这一点进行详细阐释,现在我们先关注怎么用),@Rentention、@Target、@Document、@Inherited是Java提供的元注解,怎么理解为元注解呢?就像是建筑师的建房子用的砖头一样,这些元注解就是Java提供给我们的砖头。

我们将注解中的name理解为属性,String限制了赋值的类型,default意味你没给注解中的属性赋值的时候,此时该属性为默认值。

  • @Rentention定义注解在哪里可用,这个属性在赋值是必须为RetentionPolicy类型,RetentionPolicy是一个枚举类型,有三个值
  • SOURCE(源代码)
  • CLASS(字节码可以理解为编译时,但是在运行时不会被保留)
  • RUNTIME (运行时)
  • @Target定义注解可以加在哪里(例如方法上、字段上),这个属性所赋的值必须为ElementType,ElementType也是一个枚举类型。
  • TYPE(类、接口、注解、枚举声明)
  • FIELD(字段声明,包括枚举常量中)
  • METHOD(方法上)
  • PARAMETER(参数上)
  • CONSTRUCTOR(构造函数上)
  • LOCAL_VARIABLE(局部变量)
  • ANNOTATION_TYPE
  • Annotation type declaration 这是JDK的注释,我看的有点懵。单独拿出来说
  • PACKAGE(包声明上)
  • TYPE_PARAMETER(1.8引入) : 类型变量,泛型声明
  • TYPE_USE(1.8引入): 声明类型的地方 当注解未指定Target值时,此注解可以使用任何元素之上。
  • @Documented 该注解将会,Javadoc工具会将此注解标记元素的注解信息包含在javadoc中。默认,注解信息不会包含在Javadoc中。
  • @ Inherited 如果一个注解被@Inherited修饰,那么被该注解修饰的类的子类也能获得该注解。
  • @ Repeatable 该注解解决了,可以让同一个位置存放多个相同的注解。但是它还需要一个注解,充当容器。 简单的例子:
@Repeatable(SimpleAnnotation.class)
public  @interface SimpleAnnotationDemo01 {
    String value();
}
复制代码

这个SimpleAnnotation也是一个注解,其中的属性是一个SimpleAnnotationDemo01 的数组,相当于SimpleAnnotationDemo01 存储了SimpleAnnotation在一个地方多次使用的值。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Inherited
public @interface SimpleAnnotation {
    SimpleAnnotationDemo01[] value() ;
}
复制代码

关于ANNOTATION_TYPE、TYPE_PARAMETER

  • ANNOTATION_TYPE: 该类型标识该注解只能用于修饰注解,也就是说只能用于注释上,也就是说该注解成为了元注解
  • TYPE_PARAMETER 类型参数,声明泛型时: 如下图所示:
public class TestAnnotation {
    private   int name;
    public  <@SimpleAnnotation(name="T type") T> T getName(T t) {
        return  t;
    }
}
复制代码

##注解究竟是什么? 带着这个疑问,我将注解打在类上,并执行编译后,通过java p -c(不知道这个命令,请去看我以前的文章: 两个java命令和一道看起来比较简单的面试题) 来看其反汇编之后的字节码, 示例代码:

@SimpleAnnotationDemo01(value = "01")
@SimpleAnnotationDemo01(value = "01")
public class TestAnnotation {
    public static void main(String[] args) {
        System.out.println("hello world");
    }
}
复制代码

反汇编后的字节码:

public interface com.cxk.annotation.SimpleAnnotationDemo01 extends java.lang.annotation.Annotation {
  public abstract java.lang.String value();
}
复制代码

所以这个注解本质上还是接口喽。那这个方法怎么理解啊? 我传进去值相当于调方法喽,可这是一个接口啊! 那你为啥不弄成属性啊,费这个劲,弄成方法,有些理解不了啊。 所以是动态代理喽,本篇的主题注解,有关动态代理,会专门有一篇文章来进行介绍。注解本质上是一种接口,在运行时,加入你的注解是能够保存到运行时,那么JVM就会帮你创建你的注解的实例。

那么你有什么证据呢? 首先设置一个JVM的虚拟机参数中可以帮助我们捕捉JDK动态代理类: -Dsun.misc.ProxyGenerator.saveGeneratedFiles=true 代码如下:

@SimpleAnnotationDemo01(value = "01")
@SimpleAnnotationDemo01(value = "01")
public class TestAnnotation {
    public static void main(String[] args) throws NoSuchMethodException {
        Class<TestAnnotation> clzz = TestAnnotation.class;
        System.out.println(clzz.getAnnotations());
    }
}
复制代码

InvocationHandler是一个接口,那么它的实例是哪一个呢?我们打断点来看下. 代码如下:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
@Inherited
public @interface SimpleDemo01 {
    String name() default "";
}
复制代码


原来是AnnotationInvocationHandler的实例啊.


相关文章
|
6月前
|
XML Java 程序员
Spring基础篇:注解编程
Spring基础篇:注解编程
|
26天前
|
Java 微服务 Spring
手把手教你实现@RefreshScope注解
【10月更文挑战第16天】 在Spring Cloud中,@RefreshScope注解被广泛用于动态刷新配置。当我们修改了配置文件中的值,并且希望这些更改在不重启应用的情况下立即生效时,这个注解就显得尤为重要。本文将带你一步步实现一个简单的@RefreshScope功能。
46 5
|
5月前
|
JSON Java 数据库
技术笔记:SpringMVC常用注解
技术笔记:SpringMVC常用注解
|
5月前
|
前端开发 Java 关系型数据库
SpringBootWebProject学习5、常用注解说明
SpringBootWebProject学习5、常用注解说明
27 0
|
6月前
|
Java 程序员 编译器
Java注解概述及解析注解
Java注解概述及解析注解
74 1
|
6月前
|
安全 Java 编译器
Java注解详解和自定义注解实战,用代码讲解
Java注解详解和自定义注解实战,用代码讲解
176 0
|
11月前
|
Java
springmvc之自定义注解-->自定义注解简介,基本案例和aop自定义注解
springmvc之自定义注解-->自定义注解简介,基本案例和aop自定义注解
54 0
|
前端开发 Java API
SpringMVC注解完全解析(上)
SpringMVC注解完全解析(上)
|
Java 编译器
注解和反射(一)【注解的基础知识和架构】
注解和反射(一)【注解的基础知识和架构】
131 0
注解和反射(一)【注解的基础知识和架构】
|
Java 编译器 Spring
注解入门指南
注解入门指南
117 0