欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Java 注解

程序员文章站 2022-03-14 19:27:20
...

引言:

在Spring框架中,及servlet当中会经常看到注解形式,在使用注解之前(甚至在使用之后),XML被广泛的应用于描述元数据,但是有时候XML的维护会越来越糟糕。我们希望使用一些和代码紧耦合的东西,而不是像XML那样和代码是松耦合的代码描述。目前,许多框架将XML和注解两种方式结合使用,平衡两者之间的利弊。

理解注解:

类似于修饰符,比如SpringBoot中的@SpringBootApplication,运行方法时,Spring会自动识别该方法并单独调用。注解对语意没有直接的影响,他们只负责提供信息供相关的程序使用。更一般的讲,注解永远不会改变被注解代码的语意。

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class MasterSpringMvcApplication {

    public static void main(String[] args) {
        SpringApplication.run(MasterSpringMvcApplication.class, args);
    }
}

以下是@SpringBootApplication的部分源码:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
		@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
......
}

对于一个注解的基本形式为:

import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

//保留的环境
@Retention(RUNTIME)
//注释起作用的位置,此处表示它只能给方法注解
@Target(METHOD)
public @interface Test {

}

其中@Target限定注解标记范围,@Retention限定注解的生命周期,@Target和@Retention是Java提供的元注解,所谓元注解,就是注解的注解。

下面分别介绍@Target和@Retention

@Target用来约束注解可以应用的地方(如方法、类或字段),其中ElementType是枚举类型,其定义如下,也代表可能的取值范围

public enum ElementType {
    /**标明该注解可以用于类、接口(包括注解类型)或enum声明*/
    TYPE,

    /** 标明该注解可以用于字段(域)声明,包括enum实例 */
    FIELD,

    /** 标明该注解可以用于方法声明 */
    METHOD,

    /** 标明该注解可以用于参数声明 */
    PARAMETER,

    /** 标明注解可以用于构造函数声明 */
    CONSTRUCTOR,

    /** 标明注解可以用于局部变量声明 */
    LOCAL_VARIABLE,

    /** 标明注解可以用于注解声明(应用于另一个注解上)*/
    ANNOTATION_TYPE,

    /** 标明注解可以用于包声明 */
    PACKAGE,

    /**
     * 标明注解可以用于类型参数声明(1.8新加入)
     * @since 1.8
     */
    TYPE_PARAMETER,

    /**
     * 类型使用声明(1.8新加入)
     * @since 1.8
     */
    TYPE_USE
}

@Retention中文译为保留期,用来约束注解的生命周期,分别有三个值,源码级别(source),类文件级别(class)或者运行时级别(runtime),其含有如下:


    SOURCE:注解将被编译器丢弃(该类型的注解信息只会保留在源码里,源码经过编译后,注解信息会被丢弃,不会保留在编译好的class文件里)

    CLASS:注解在class文件中可用,但会被VM丢弃(当注解未定义Retention值时,默认值是CLASS,如Java内置注解,@Override、@Deprecated、@SuppressWarnning等

    RUNTIME:注解信息将在运行期(JVM)也保留,如SpringMvc中的@Controller、@Autowired、@RequestMapping等。

注解的属性

注解的属性也叫做成员变量。注解只有成员变量,没有方法。注解的成员变量在注解的定义中以“无形参的方法”形式来声明,其方法名定义了该成员变量的名字,其返回值定义了该成员变量的类型

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Test {

    int id();

    String msg();

}

此处有两个属性id和msg使用时,我们通过下面方式赋值

@Test(id=1, msg="test")
public class Test {

}

在注解中定义属性时它的类型必须是 8 种基本数据类型外加 类、接口、注解及它们的数组。

  •    所有基本类型(int,float,boolean,byte,double,char,long,short)
  •     String
  •     Class
  •     enum
  •     Annotation
  •     上述类型的数组
注解中属性可以有默认值,默认值需要用 default 关键值指定。比如:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Test {

    public int id() default -1;

    public String msg() default "Hi";

}

此时注解可以不用输入值,而使用默认值

@Test()
public class Test {}

注解的提取

注解就好比标签,并且在合适的时候撕下来查看标签的内容,此时需要用到方法就是反射。也就是说,注解通过反射获取。

首先可以通过 Class 对象的 isAnnotationPresent() 方法判断它是否应用了某个注解

public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {}

然后通过 getAnnotation() 方法来获取 Annotation 对象

public <T extends Annotation> T getAnnotation(Class<T extends Annotation> annotationClass) {}

或者是 getAnnotations() 方法返回一个Annatation[]数组

public Annotation[] getAnnotations() {}

通过第一种方法获取注解元素代码如下:

@Test
public class Main {
  public static void main(String[] args) {
    //判断是否为Test注解
    boolean hasAnnotation = 
        Main.class.isAnnotationPresent(Test.class);
    if(hasAnnotation) {
      Test testAnnotation = Main.class.getAnnotation(Test.class);
      System.out.println(testAnnotation);
      System.out.println(testAnnotation.id());
    }
    
  }
}

返回结果:

Java 注解

对于上面的@Test再稍微深入的了解一下,这个Test到底是类还是接口呢?由上面的方法:

public <T extends Annotation> T getAnnotation(Class<T extends Annotation> annotationClass) {}

并且根据上面的代码

 Test testAnnotation = Main.class.getAnnotation(Test.class);

综合推测Test是一个继承自Annatation接口的接口,也有可能是抽象类或者类(因为在注解定义中没有显式的结构,所以让我有点疑惑),于是我们来反编译一下看看

javap -p Test.class

Java 注解

由此可证实,自定义的注解继承自Annotation的接口类型。


参考:

《Effect Java》

秒懂,Java 注解 (Annotation)你可以这样学


相关标签: 注解