Java的注解Annotation

注解使代码简单易读,提供编译器类型检查,并且可以通过注解构造代码处理工具。

日常开发中我们会遇到很多注解,如@RestController@GetMapping@Autowired@Service等等。
那这些注解是怎么定义的?怎么使用的?为什么要这些注解?

接下来我们从基础知识来慢慢揭开注解的面纱。

注解的基础知识

注解的格式

注解以@符号开头,如下:

@Entity

注解被用在什么地方

注解可以应用于声明:类、字段、方法和方法参数等。如下:

@Author(
   name = "Benjamin Franklin",
   date = "3/27/2003"
)
class MyClass { ... }

@Override
void mySuperMethod() { ... }

class UnmodifiableList implements 
    @Readonly List<@Readonly T> { ... }

定义注解

@interface关键字定义注解,和定义接口有点像,多了个@符号。

注解里面的属性跟接口方法有点像,但没有方法体。属性可以有默认值,如currentRevision默认值为1。

// 注解 类注释
@Retention(value = RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ClassPreamble {
    String name();

    String author();

    String date();

    int currentRevision() default 1;
}

使用注解

@ClassPreamble(
        name = "学生类",
        author = "llh",
        date = "2022-08-01",
        currentRevision = 2)
public class Student {

}

spring 的@Service是这么定义的。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service {
    @AliasFor(annotation = Component.class)
    String value() default "";
}

元注解

自定义注解一定要用到元注解,元注解就是定义注解的注解。有以下五个元注解。

@Retention

@Retention注解指定标记注解的存储方式:

  • RetentionPolicy.SOURCE:注解只保留在源代码级别,编译器会忽略它。
  • RetentionPolicy.CLASS:注解在编译时由编译器保留,但被 Java 虚拟机 (JVM) 忽略。
  • RetentionPolicy.RUNTIME:注解由 JVM 保留,以便运行时环境使用。

@Target

@Target注解标记另一个注解,以限制该注解可以应用于哪种Java元素。目标注释指定以下元素类型之一作为其值:

  • ElementType.ANNOTATION_TYPE:可以应用于注释类型。
  • ElementType.CONSTRUCTOR:可以应用于构造函数。
  • ElementType.FIELD:可以应用于字段或属性。
  • ElementType.LOCAL_VARIABLE:可以应用于局部变量。
  • ElementType.METHOD:可以应用于方法级别的注释。
  • ElementType.PACKAGE:可以应用于包声明。
  • ElementType.PARAMETER:可以应用于方法的参数。
  • ElementType.TYPE:可以应用于类、接口(包括注释类型)或枚举声明

@Inherited

@Inherited注解表示注解类型可以继承自超类。(默认情况下不是这样。)当用户查询注解类型并且该类没有该类型的注解时,将查询该类的超类的注解类型。此注释仅适用于类声明。

@Documented

@Documented注解表示无论何时使用指定的注解,都应使用 Javadoc 工具记录这些元素

@Repeatable

在 Java SE 8 中引入,表示标记的注解可以多次应用于同一个声明或类型使用。

编写注解处理器

如果没有用于读取注解的工具,那么注解不会比注释更有用。使用注解中一个很重要的部分就是,创建与使用注解处理器。Java 拓展了反射机制的 API 用于帮助你创造这类工具。

如下是个很简单注解处理工具,利用反射获取类的注解,然后打印注解信息。

public class Demo {
    public static void main(String[] args) {
        Class c = Student.class;
        ClassPreamble annotation = c.getAnnotation(ClassPreamble.class);
        System.out.println(annotation.name());
        System.out.println(annotation.author());
        System.out.println(annotation.date());
        System.out.println(annotation.currentRevision());
    }
}

模拟 Spring 的 @Service

自定义MyService注解

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyService {
    String value() default "";
}
public interface UserService {
}
@MyService("userService")
public class UserServiceImpl implements UserService {
}

这里模拟Spring容器扫描有MyService注解的类,并自动生成该类的一个实例对象保存到容器中。

public class Demo {
    public static void main(String[] args) {
        try {
            // 模拟 spring 容器
            Map map = new ConcurrentHashMap<>();

            // 模拟 扫描所有有MyService注解的类
            Class c = UserServiceImpl.class;
            MyService annotation = c.getAnnotation(MyService.class);
            if (annotation != null) {
                // 模拟 有MyService注解的类生成该类的一个实例对象
                Object obj = c.newInstance();

                // 模拟 保存到容器中 key就是注解的值 userService
                map.put(annotation.value(), obj);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

实际要比这个复杂的多得多,我这里就是示意说明一下注解的作用,让大家容易理解。

结语

注解我们日常开发经常用,但是很多同学从来没有自己手动去创建过一个自定义的注解类。

1万小时定律:1万小时的锤炼是任何人从平凡变成世界级大师的必要条件。但是写1万小时的代码你就能变成大师吗?非也!不要重复性的工作,要思考要深入!

关注微信公众号:小虎哥的技术博客,每天一篇文章,让你我都成为更好的自己

你可能感兴趣的:(Java的注解Annotation)