Java 注解

23 篇文章 0 订阅

引言:

在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() 方法判断它是否应用了某个注解

default boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
        return getAnnotation(annotationClass) != null;
    }

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

<T extends Annotation> T getAnnotation(Class<T> annotationClass);

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

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

返回结果:

对于上面的@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

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

 

参考:

《Effect Java》

 

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

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值