0
点赞
收藏
分享

微信扫一扫

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】


举报

相关推荐

0 条评论