通俗易懂地讲解 Java 的注解

    技术2022-07-10  77

      [ 用大白话讲解复杂的技术 ]

    作者 l 会点代码的大叔(CodeDaShu)

    今天,我们来聊聊 Java 的注解。

    01

    注解的概念

    Annotation(注解):先看看官方给出的概念,注解是 Java 提供的一种对元程序中元素关联信息和元数据的途径和方法;这种解释让人看的一脸懵,通俗一点说,可以把注解看做是对人对物的标签,比如:

    凶猛的狮子,温顺的兔子,木讷的程序员,帅气的大叔;这里的“凶猛”、“温顺”、“木讷”、“帅气”就是对各个事物的标签;

    注解也是一种标签,是对 Java 代码的一种标签,可以告诉程序员被注解的代码是用来做什么的;

    当然标签的内容可能跟实际情况有所出入,甚至背道而驰,比如【木讷的程序员】,实际上就是一个很差的标签,所以如果你在 Contrller 层的代码添加了一个 Service 的注解,同样也是一个很差的标签。

    02

    元注解

    元注解是注解的注解,也就是对标签的描述。比如“木讷”、“帅气”只能用在人或动物身上,那么“只能用在人或动物身上”就是对“木讷”、“帅气”这两个标签的标签;恰好元注解中就有 @Target,表示修饰对象的范围,让我们详细看一下元注解都有哪些。

    @Target:表示修饰对象的范围,注解可以作用于 packages、class、interface、方法、成员变量、枚举、方法入参等等,@Target可以指明该注解可以修饰哪些内容。

    @Retention:时间长短,也就是注解在什么时间范围之内有效,比如在源码中有效,在 class 文件中有效,在 Runtime 运行时有效。

    @Documented:表示可以被文档化,比如可以被 javadoc 或类似的工具生成文档。

    @Inherited:表示这个注解会被继承,比如 @MyAnnotation 被 @Inherited 修饰,那么当 @MyAnnotation 作用于一个 class 时,这个 class 的子类也会被 @MyAnnotation 作用。

    03

    内置注解

    Java 中最早内置了三种注解:

    @Override:检查该方法是否是重载方法;如果父类或实现的接口中,如果没有该方法,编译会报错。

    @Deprecated:已经过时的方法;如果使用该方法,会有警告提醒。

    @SuppressWarnings:忽略警告;比如使用了一个过时的方法会有警告提醒,可以为调用的方法增加 @SuppressWarnings 注解,这样编译器不在产生警告。

    JDK 7之后,又增加了三种:

    @SafeVarargs:是一个参数安全类型注解,作用是告诉开发人员,参数可能会存在不安全的操作,要多注意一些;它会产生一个 unchecked 的警告;@SafeVarargs 注解只能用在可变长度参数的方法上,并且这个方法必须是 static 或 final 的,否则会出现编译错误。

    @FunctionalInterface:表示是只有一个方法的接口,JDK 8 引入。

    @Repeatable:表示注解的值可以有多个,比如我又帅气又幽默,这时候我同时有了“帅气”和“幽默”两个标签。

    04

    注解的使用场景

    注解可以让编译器探测错误和警告;编译阶段可以利用注解生成文档、代码或做其他的处理;在代码运行阶段,一些注解还可以帮助完成代码提取之类的工作。

    比如,使用过 Spring 框架的同学应该对 @Autowired 很熟悉了。使用 Spring 开发时,进行配置可以用 xml 配置文件的方式,现在用的更多的就是注解的方式。@Autowired 可以帮助我们注入一个定义好的 Bean。

    @Autowired 的核心代码大概是这样的,作用就是 Spring 可以提取到使用 @Autowired 修饰的字段或方法做注入:

    private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) { //省略...... do { //省略...... //通过反射,获取这个类的所有字段,并遍历所有字段 ReflectionUtils.doWithLocalFields(targetClass, new ReflectionUtils.FieldCallback() { //遍历字段的所有注解 @Override public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException { //如果字段使用了 @Autowired AnnotationAttributes ann = findAutowiredAnnotation(field); if (ann != null) { if (Modifier.isStatic(field.getModifiers())) { if (logger.isWarnEnabled()) { logger.warn("Autowired annotation is not supported on static fields: " + field); } return; } boolean required = determineRequiredStatus(ann); //先放到 currElements 中,后面一起集中处理 currElements.add(new AutowiredFieldElement(field, required)); } } }); //省略...... } while (targetClass != null && targetClass != Object.class); return new InjectionMetadata(clazz, elements); } //统一处理,也就是注入的源码我就不贴了
    Processed: 0.035, SQL: 9