Java中的注解是怎么发挥作用的?

注解是啥

注解本身不提供作用,注解只能是被看作元数据,它不包含任何业务逻辑。注解更像是一个标签,一个声明,表面被注释的这个地方,将具有某种特定的逻辑。

注解(Annotation),也叫元数据,是一种代码级别的说明。是Java 的JDK1.5版本开始引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、属性、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。

常见注解的种类

常见的注解有三大类:JDK的,自定义的,第三方的(比如框架)

注解三板斧

定义、使用、读取

  • 定义:包括名字,能用到哪些地方,有效期,是否可以被继承

  • 使用:定义好之后在允许的地方使用标注即可

  • 读取:让注解发挥作用,给注解注入灵魂

光有前两步,没什么用,如最熟悉的@Override注解,为什么能验证重写是否有效,怎么不是验证重载?spring的@Autowired为什么是注入作用,而不是输出一句话?显然,他们在程序中做了实现,使得其注解具有各自的作用,也具有了意义,而赋予灵魂的一步就是读取

第一步:定义注解

定义一个注解很简单,和定义一个类很相似。

前置知识

首先是元注解,Java给我们提供了四个元注解,用于我们自定义的注解上:

@Documented| @Retention | @Target | @Inherited

分别解释下

@Documented

代表着此注解会被javadoc工具提取成文档

@Retention:

代表该注解的有效期
SOURCE 表示编译期,如@Override,只做编译时的提示,不会写入字节码中。
CLASS表示类加载期,会保存在class文件中,但在运行class文件被丢弃,也是默认值
RUNTIME 表示运行期,也是最常用的,可以在代码运行时进行反射执行相关的操作

@Target:

表示这个注解可以放在哪
TYPE:接口、类、枚举、注解
FIELD:字段、枚举的常量
METHOD:方法
PARAMETER:参数
CONSTRUCTOR:构造函数
LOCAL_VARIABLE:局部变量
ANNOTATION_TYPE:注解
PACKAGE:包

@Inherited:

表示子类可以继承该类的注解

定义一个注解,分为以下几步:

  • 注解的名字(声明注解)
  • 注解的修饰目标
  • 注解的生命周期
  • 注解的属性。

声明注解

注解的声明形式如下:

public @interface  注解名字 {
	注解属性
}

例如我们声明注解类型MyAnnotation.java,如下所示:

public @interface MyAnnotation {
}

注解的修饰目标

注解可以用于不同的目标,例如接口、类、构造方法、方法、属性、类型等;

声明注解时,可以使用JDK中已经定义好的元注解@Target声明注解修饰的目标;

@Target中使用枚举ElementType表示修饰目标,有如下几种修饰目标:

表示这个注解可以放在哪
TYPE:接口、类、枚举、注解
FIELD:字段、枚举的常量
METHOD:方法
PARAMETER:参数
CONSTRUCTOR:构造函数
LOCAL_VARIABLE:局部变量
ANNOTATION_TYPE:注解
PACKAGE:包

来看下@Target的源码:

package java.lang.annotation;

@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();
}

在自定义注解中使用元注解:@Target

自定义注解@MyAnnotation使用@Target,指定修饰目标为TYPE和METHOD,即接口、类、枚举、注解、方法上可以使用注解MyAnnotation

@Target(value = {ElementType.TYPE,ElementType.METHOD})
public @interface MyAnnotation {

}

注解的生命周期

元注解@Retention,用来定义注解的声明周期;

代表该注解的有效期
SOURCE 表示编译期,如@Override,只做编译时的提示,不会写入字节码中。
CLASS表示类加载期,会保存在class文件中,但在运行class文件被丢弃,也是默认值
RUNTIME 表示运行期,也是最常用的,可以在代码运行时进行反射执行相关的操作

来看下@Retention的源码:

package java.lang.annotation;

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
    /**
     * Returns the retention policy.
     * @return the retention policy
     */
    RetentionPolicy value();
}

在自定义注解中元注解:@Retention

指注解@MyAnnotation的生命周期为运行时(我们在一个Test类中使用@MyAnnotation,编译后看Test.class文件,里面没有任何关于注解@MyAnnotation的影子)

@Target(value = {ElementType.TYPE,ElementType.METHOD})
@Retention(value=RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
}

注解的属性

注解的属性看起来像个方法,其实是属性,属性类型包括所有基本类型、 String、Class、enum、 Annotation、以上类型的数组形式,注解元素声明形式如下:

@Target(value = {ElementType.TYPE,ElementType.METHOD})
@Retention(value=RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
		// 默认全是public的,所以public可以忽略  
    String message() default "MyAnnotation";
    boolean onload() default false;
}

注解中的属性在使用的时候可以指定值,也可以在声明的时候赋默认值;

第二步:使用注解

使用注解非常简单,不管是JDK中内置的已经定义好的注解还是自定义的注解,只要使用 @注解名称(属性值列表)的形式,均可以使用;

例如:

 public class Test {
     @MyAnnotation(message = "测试", onload = true)
     public void test1() {
         System.out.println("测试注解");
     }
 }

第三步:读取注解

读取注解一般都是通过反射获取到某个类,或者某个方法上的注解,然后得到注解中属性的值,去做一些判断。

可以看下这个例子:

package com.wlw.annotion;

import org.yaml.snakeyaml.events.Event;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;

/**
 * @author glen.wang
 * @date 2022/9/5
 */
public class Test {

    @MyAnnotation(message = "测试", onload = true)
    public void test1() {
        System.out.println("测试注解");
    }


    public static void main(String[] args) {
        //获得测试类
        Class clazz = Test.class;

        Method[] methods = clazz.getMethods();

        for (Method method : methods) {
            MyAnnotation myAnnotation = method.getAnnotation(MyAnnotation.class);
            if (myAnnotation != null) {
                System.out.println(myAnnotation.message());
                System.out.println(myAnnotation.onload());
            }

        }
    }
}

=====
测试
true

但是在实际开发中,我们都是用spring体系开发,所以我们可以写个切面类,切入点就是这个注解,获取到使用 了这个注解的方法或者类,然后根据注解信息做一些处理。【spring 的AOP】

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

悬浮海

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值