前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >java基础-注解Annotation原理和用法

java基础-注解Annotation原理和用法

作者头像
allsmallpig
发布2021-02-25 11:31:32
4300
发布2021-02-25 11:31:32
举报
文章被收录于专栏:allsmallpi博客

转载自 http://www.wolfbe.com/detail/201608/265.html

在很多java代码中都可以看到诸如@Override、@Deprecated、@SuppressWarnings这样的字符,这些就是注解Annotation。注解最早在jdk5中被引入,现在已经成为java平台很重要的一部分了,很多的框架程序中也喜欢使用注解,如Spring、Mybatis等。

那么,什么是注解呢?注解就是元数据,一种描述数据的数据,通俗一点就是为程序的元素(类、方法、成员变量)加上更直观的说明,这些说明信息是与程序的业务逻辑无关的。但是,我们可以通过java的反射机制来获取Annotation的信息,并根据这些信息来对程序进行赋值、分发等操作。

java5.0定义了4个标准的meta-annotation元注解,它们被用来提供对其它annotation类型作说明,四种元注解如下:

  • @Target;
  • @Retention;
  • @Inherited;
  • @Documented;

下面详细说明这四种元注解的作用:

@Target

被用于描述注解的使用范围,即注解可以用在所修饰对象的什么地方,取值可以是ElementType中的一种:

  • CONSTRUCTOR:用于描述构造器;
  • FIELD:用于描述域;
  • LOCAL_VARIABLE:用于描述局部变量;
  • METHOD:用于描述方法;
  • PACKAGE:用于描述包;
  • PARAMETER:用于描述参数;
  • TYPE:用于描述类、接口、注解类型或枚举;

@Target(ElementType.TYPE) public @interface Exculde{

    /**

    * 名称,默认值为""

    * @return

    */

    public String name() default "";

}

@Target(ElementType.FIELD)

public @interface Inject{

    /**

    * id,默认值为""

    * @return

    */

    public String id() default "";

    /**

    * 类,默认值为""

    * @return

    */

    public Class clazz() default Object.class;

}

上面定义了两个注解,注解@Exculde只能用于修饰类、接口、注解、枚举,注解@Inject只能修饰类型的域,如:

@Exculde(name="admin")

public class User{

     @Inject(id="username",clazz=String.class)

     private String username;

}

@Retention

用于描述注解的生命周期,即注解能在源码到JVM装载过程中的哪一个级别上有效。有些annotation仅出现在源码中,被编译器丢弃;有些annotation能被编译进class文件中,可能被JVM忽略;有些annotation不但能够被编译进class文件,而且能够在class文件被装载时被读取。这三种情况对应RetentionPoicy的三种取值:

  • SOURCE:源码文件中保留;
  • CLASS:class文件中保留;
  • RUNTIME:运行时保留;

@Inherited

 用于描述注解是可以被继承的,如果一个使用了@Inherited修饰的annotation被用于一个class,那么这个annotation也将被用于这个class的子类。@Inherited是一个标记注解,没有参数选项,它修饰的annotation是被标记的class的子类所继承,类并不从它所实现的接口继承annotation,方法并不从它所重载的方法继承annotation。

     当使用java的反射去获取一个@Inherited修饰的annotation时,反射检查将递归检查,检查class和其父类,直到发现指定的annotation类型被发现,或者到达类继承结构的顶层。

@Documented

    用于描述注解信息应该被作为被标注的程序的公共API,即应该把注解信息保留文档中。@Documented也是一个标记注解,没有参数选项。

@Target(ElementType.TYPE)

@Retention(RetentionPolicy.RUNTIME)

@Inherited

@Documented

public @interface Exculde{

    /**

    * 名称注解,默认值为""

    * @return     */

    public String name() default "";

}

如果你看过了上面的元注解,如果还不能理解也没关系,下面我们通过自定义注解来进一步理解元注解。

     自定义注解需要使用@interface,类似于定义一个类使用class,但定义注解时不能再继承其它的类或者接口,它已经自动继承了java.lang.annotation.Annotation接口。@interface用来声明一个注解,其中的每一个方法实际上声明了一个配置参数,方法的名称就是参数的名称,方法的返回值类型就是参数的类型,也可以使用 default来声明参数的默认值。

定义注解的格式如下:

public @interface 注解名{ 定义体 }

注解参数可支持的数据类型如下:

  • 基本类型;
  • Class;
  • String;
  • Enum;
  • Annotation;
  • 以上所有类型的数组形式;

下面定义一个类似于Spring的注解,用于向实例对象注入属性的值:

@Target(ElementType.FIELD)

@Retention(RetentionPolicy.RUNTIME)

public @interface Inject{

    /**

    * ID,默认值为""

    * @return

    */

    public String id() default "";

    /**

    * 类,默认值为""

    * @return

    */

    public Class clazz() default Object.class;

}

把@Inject标注在类UserAction的属性上:

public class UserAction {

     @Inject(id="userService",clazz=UserService.class)

     private UserService userService;

}

在IOC容器框架中,对象都会被自动初始化,如果我们要实现IOC的这种功能,我们应该为加上@Inject注解的属性userService注入它的值。首先我们应该通过反射获取userService的域对象field,通过field获取@Inject注解的信息,然后根据注解的id和clazz得到它依赖的值:

Inject inject = field.getAnnotation(Inject.class);

String id = inject.id();

Class clazz = inject.clazz();

Object userService = Class.forName(clazz.getName).newInstance();

field.setAccessible(true);

field.set(object,userService);

上面代码中,调用field.getAnnotation(Inject.class)获取到@Inject的对象,然后获取@Inject的id和clazz值,通过反射实例化clazz的对象,再反射赋值给field。这就是Spring那些框架的依赖注入的实现原理,有兴趣的可以自己再优化一下。

读取类的注解信息还有其它的几个方法,在此不再一一说明,可以自行研究java.lang.reflect包。经过上面的说明,由此我们也可以知道注解仅仅是一种元数据,增强类、属性、参数的描述,使用注解的关键在于获取注解的信息,再通过反射的手段来实现注解想达成的功能。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018/06/26 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档