java注解

AI权益加码!Claude Code、Cursor等20+工具免费用! 购周边限时加赠Coding Plan Lite,畅享主流AI工具!学习进阶更高效! 阅读详情
在开发Java程序,尤其是Java EE应用的时候,总是免不了与各种配置文件打交道。以Java EE中典型的S(pring)S(truts)H(ibernate)架构来说,Spring、Struts和Hibernate这三个框架都有自己的XML格式的配置文件。这些配置文件需要与Java源代码保存同步,否则的话就可能出现错误。而且这些错误有可能到了运行时刻才被发现。把同一份信息保存在两个地方,总是个坏的主意。理想的情况是在一个地方维护这些信息就好了。其它部分所需的信息则通过自动的方式来生成。JDK 5中引入了源代码中的注解(annotation)这一机制。注解使得Java源代码中不但可以包含功能性的实现代码,还可以添加元数据。注解的功能类似于代码中的注释,所不同的是注解不是提供代码功能的说明,而是实现程序功能的重要组成部分。Java注解已经在很多框架中得到了广泛的使用,用来简化程序中的配置。

  使用注解

  在一般的Java开发中,最常接触到的可能就是@Override和@SupressWarnings这两个注解了。使用@Override的时候只需要一个简单的声明即可。这种称为标记注解(marker annotation ),它的出现就代表了某种配置语义。而其它的注解是可以有自己的配置参数的。配置参数以名值对的方式出现。使用 @SupressWarnings的时候需要类似@SupressWarnings({"uncheck", "unused"})这样的语法。在括号里面的是该注解可供配置的值。由于这个注解只有一个配置参数,该参数的名称默认为value,并且可以省略。而花括号则表示是数组类型。在JPA中的@Table注解使用类似@Table(name = "Customer", schema = "APP")这样的语法。从这里可以看到名值对的用法。在使用注解时候的配置参数的值必须是编译时刻的常量。

  从某种角度来说,可以把注解看成是一个XML元素,该元素可以有不同的预定义的属性。而属性的值是可以在声明该元素的时候自行指定的。在代码中使用注解,就相当于把一部分元数据从XML文件移到了代码本身之中,在一个地方管理和维护。

  开发注解

  在一般的开发中,只需要通过阅读相关的API文档来了解每个注解的配置参数的含义,并在代码中正确使用即可。在有些情况下,可能会需要开发自己的注解。这在库的开发中比较常见。注解的定义有点类似接口。下面的代码给出了一个简单的描述代码分工安排的注解。通过该注解可以在源代码中记录每个类或接口的分工和进度情况。

以下是代码片段:
  @Retention(RetentionPolicy.RUNTIME)
  @Target(ElementType.TYPE)
  public @interface Assignment {
  String assignee();
  int effort();
  double finished() default 0;
  }

  @interface用来声明一个注解,其中的每一个方法实际上是声明了一个配置参数。方法的名称就是参数的名称,返回值类型就是参数的类型。可以通过default来声明参数的默认值。在这里可以看到@Retention和@Target这样的元注解,用来声明注解本身的行为。@Retention用来声明注解的保留策略,有CLASS、RUNTIME和SOURCE这三种,分别表示注解保存在类文件、JVM运行时刻和源代码中。只有当声明为RUNTIME的时候,才能够在运行时刻通过反射API来获取到注解的信息。@Target用来声明注解可以被添加在哪些类型的元素上,如类型、方法和域等。

  处理注解

  在程序中添加的注解,可以在编译时刻或是运行时刻来进行处理。在编译时刻处理的时候,是分成多趟来进行的。如果在某趟处理中产生了新的Java源文件,那么就需要另外一趟处理来处理新生成的源文件。如此往复,直到没有新文件被生成为止。在完成处理之后,再对Java代码进行编译。JDK 5中提供了apt工具用来对注解进行处理。apt是一个命令行工具,与之配套的还有一套用来描述程序语义结构的Mirror API。Mirror API(com.sun.mirror.*)描述的是程序在编译时刻的静态结构。通过Mirror API可以获取到被注解的Java类型元素的信息,从而提供相应的处理逻辑。具体的处理工作交给apt工具来完成。编写注解处理器的核心是AnnotationProcessorFactory和AnnotationProcessor两个接口。后者表示的是注解处理器,而前者则是为某些注解类型创建注解处理器的工厂。

  以上面的注解Assignment为例,当每个开发人员都在源代码中更新进度的话,就可以通过一个注解处理器来生成一个项目整体进度的报告。 首先是注解处理器工厂的实现。

以下是代码片段:
  public class AssignmentApf implements AnnotationProcessorFactory {
  public AnnotationProcessor getProcessorFor(Set atds,? AnnotationProcessorEnvironment env) {
  if (atds.isEmpty()) {
  return AnnotationProcessors.NO_OP;
  }
  return new AssignmentAp(env); //返回注解处理器
  }
  public Collection supportedAnnotationTypes() {
  return Collections.unmodifiableList(Arrays.asList("annotation.Assignment"));
  }
  public Collection supportedOptions() {
  return Collections.emptySet();
  }
  }

  AnnotationProcessorFactory接口有三个方法:getProcessorFor是根据注解的类型来返回特定的注解处理器;supportedAnnotationTypes是返回该工厂生成的注解处理器所能支持的注解类型;supportedOptions用来表示所支持的附加选项。在运行apt命令行工具的时候,可以通过-A来传递额外的参数给注解处理器,如-Averbose=true。当工厂通过 supportedOptions方法声明了所能识别的附加选项之后,注解处理器就可以在运行时刻通过AnnotationProcessorEnvironment的getOptions方法获取到选项的实际值。注解处理器本身的基本实现如下所示。

以下是代码片段:
  public class AssignmentAp implements AnnotationProcessor {
  private AnnotationProcessorEnvironment env;
  private AnnotationTypeDeclaration assignmentDeclaration;
  public AssignmentAp(AnnotationProcessorEnvironment env) {
  this.env = env;
  assignmentDeclaration = (AnnotationTypeDeclaration) env.getTypeDeclaration("annotation.Assignment");
  }
  public void process() {
  Collection declarations = env.getDeclarationsAnnotatedWith(assignmentDeclaration);
  for (Declaration declaration : declarations) {
  processAssignmentAnnotations(declaration);
  }
  }
  private void processAssignmentAnnotations(Declaration declaration) {
  Collection annotations = declaration.getAnnotationMirrors();
  for (AnnotationMirror mirror : annotations) {
  if (mirror.getAnnotationType().getDeclaration().equals(assignmentDeclaration)) {
  Map values = mirror.getElementValues();
  String assignee = (String) getAnnotationValue(values, "assignee"); //获取注解的值
  }
  }
  }
  }

  注解处理器的处理逻辑都在process方法中完成。通过一个声明(Declaration)的getAnnotationMirrors方法就可以获取到该声明上所添加的注解的实际值。得到这些值之后,处理起来就不难了。

在创建好注解处理器之后,就可以通过apt命令行工具来对源代码中的注解进行处理。 命令的运行格式是apt -classpath bin -factory annotation.apt.AssignmentApf src/annotation/work/*.java,即通过-factory来指定注解处理器工厂类的名称。实际上,apt工具在完成处理之后,会自动调用javac来编译处理完成后的源代码。

  JDK 5中的apt工具的不足之处在于它是Oracle提供的私有实现。在JDK 6中,通过JSR 269把自定义注解处理器这一功能进行了规范化,有了新的javax.annotation.processing这个新的API。对Mirror API也进行了更新,形成了新的javax.lang.model包。注解处理器的使用也进行了简化,不需要再单独运行apt这样的命令行工具,Java编译器本身就可以完成对注解的处理。对于同样的功能,如果用JSR 269的做法,只需要一个类就可以了。

 

以下是代码片段:
 @SupportedSourceVersion(SourceVersion.RELEASE_6)
  @SupportedAnnotationTypes("annotation.Assignment")
  public class AssignmentProcess extends AbstractProcessor {
  private TypeElement assignmentElement;
  public synchronized void init(ProcessingEnvironment processingEnv) {
  super.init(processingEnv);
  Elements elementUtils = processingEnv.getElementUtils();
  assignmentElement = elementUtils.getTypeElement("annotation.Assignment");
  }
  public boolean process(Set annotations, RoundEnvironment roundEnv) {
  Set elements = roundEnv.getElementsAnnotatedWith(assignmentElement);
  for (Element element : elements) {
  processAssignment(element);
  }
  }
  private void processAssignment(Element element) {
  List annotations = element.getAnnotationMirrors();
  for (AnnotationMirror mirror : annotations) {
  if (mirror.getAnnotationType().asElement().equals(assignmentElement)) {
  Map values = mirror.getElementValues();
  String assignee = (String) getAnnotationValue(values, "assignee"); //获取注解的值
  }
  }
  }
  }

  仔细比较上面两段代码,可以发现它们的基本结构是类似的。不同之处在于JDK 6中通过元注解@SupportedAnnotationTypes来声明所支持的注解类型。另外描述程序静态结构的javax.lang.model包使用了不同的类型名称。使用的时候也更加简单,只需要通过javac -processor annotation.pap.AssignmentProcess Demo1.java这样的方式即可。

  上面介绍的这两种做法都是在编译时刻进行处理的。而有些时候则需要在运行时刻来完成对注解的处理。这个时候就需要用到Java的反射API。反射API提供了在运行时刻读取注解信息的支持。不过前提是注解的保留策略声明的是运行时。Java反射API的AnnotatedElement接口提供了获取类、方法和域上的注解的实用方法。比如获取到一个Class类对象之后,通过getAnnotation方法就可以获取到该类上添加的指定注解类型的注解。

  实例分析

  下面通过一个具体的实例来分析说明在实践中如何来使用和处理注解。假定有一个公司的雇员信息系统,从访问控制的角度出发,对雇员的工资的更新只能由具有特定角色的用户才能完成。考虑到访问控制需求的普遍性,可以定义一个注解来让开发人员方便的在代码中声明访问控制权限。

以下是代码片段:
  @Retention(RetentionPolicy.RUNTIME)
  @Target(ElementType.METHOD)
  public @interface RequiredRoles {
  String[] value();
  }

  下一步则是如何对注解进行处理,这里使用的Java的反射API并结合动态代理。下面是动态代理中的InvocationHandler接口的实现。

 

以下是代码片段:
 public class AccessInvocationHandler implements InvocationHandler {
  final T accessObj;
  public AccessInvocationHandler(T accessObj) {
  this.accessObj = accessObj;
  }
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  RequiredRoles annotation = method.getAnnotation(RequiredRoles.class); //通过反射API获取注解
  if (annotation != null) {
  String[] roles = annotation.value();
  String role = AccessControl.getCurrentRole();
  if (!Arrays.asList(roles).contains(role)) {
  throw new AccessControlException("The user is not allowed to invoke this method.");
  }
  }
  return method.invoke(accessObj, args);
  }
  }

  在具体使用的时候,首先要通过Proxy.newProxyInstance方法创建一个EmployeeGateway的接口的代理类,使用该代理类来完成实际的操作。

Java注解 为什么要引入注解? 使用【注解】之前(甚至在使用之后),【XML】被广泛的应用于描述元数据,得到各大框架的青睐,它以松耦合的方式完成了框架中几乎所有的配置,但是随着项目越来越庞大,【XML】的内容也越来越复杂,一些应用开发人员和架构师发现维护成本变高。他们希望使用一些和代码紧耦合的东西,于是就有人提出来一种标记式高耦合的配置方式【注解】。方法上可以进行注解,类上也可以注解,字段属性上也可以注解,... 阅读详情

相关推荐

JAVA注解

本文将向你介绍J2SE5.0中的新特性之一:注解。本文将从什么是注解;J2SE5.0中预定义的注解;如何自定义注解;如何对注解进行注解以及如何在程序中读取注解5个方面进行讨论。     一、什么是注解     说起注解,得先提一提什么是元数据(metadata)。所谓元数据就是数据的数据。也就是说,元数据是描述数据的。就象数据表中的字段一样,每个字段描述了这个字段下的数据的含义。而J2SE

rj042的专栏 1万+

java 注解 entity_Java 注解

一. 简介注解java5的新特性。注解可以看做一种注释或者元数据(MetaData),可以把它插入到我们的java代码中,用来描述我们的java类,从而影响java类的行为。二. 基本语法1. 注解的使用形式:一个java注解由一个@符后面跟一个字符串构成,类似于这样:@Entityjava注解中一般包含一些元素,这些元素类似于属性或者参数,可以用来设置值,比如我们有一个包含两个元素的@Enti...

weixin_42384743的博客 2331

java注解怎么写_java注解

写在前面现在的java主流框架中,注解无处不在,因此我们完全有必要搞明白注解到底是什么。如何理解注解,它到底是怎么起作用的。注解的定义java是这样定义注解的:Java 注解用于为 Java 代码提供元数据。作为元数据,注解不直接影响你的代码执行,但也有一些类型的注解实际上可以用于这一目的。什么又是元数据的呢?说白了就是描述数据的数据,既然注解被称为元数据,具有描述作用,那么我们是不是可以把注解理...

weixin_42476912的博客 2234

Java注解以及自定义注解

Java注解以及自定义注解

YIYIYI 1962

java注解的使用及常用注解

java注解的使用及常用注解

qq_42003527的博客 2334

java 注解 id_java注解

java注解:Java 注解用于为 Java 代码提供元数据。作为元数据,注解不直接影响你的代码执行,但也有一些类型的注解实际上可以用于这一目的。Java 注解是从 Java5 开始添加到 Java 的。注解理解的前提:我认为注解就是标签它是在 Java SE 5.0 版本中开始引入的概念,就如同java中的 class和interface说到这个就不得不说注解中的基础元注解注解: 元注解是可以...

weixin_42547335的博客 1291

Java注解与元注解

使用@interface关键字来定义注解注解中的方法称为注解元素,注解元素可以有默认值。// 定义一个简单的注解注解是用于注解其他注解注解,它们可以控制注解的使用范围、生命周期等。Java 提供了几个元注解,常用的有@Retention@Target和@Inherited。

dsq_MaDing的博客 675

java 注解 数组_Java注解,就是那么简单

深入理解注解1、基本介绍2 内置注解2.1@Deprecated注解2.2@Override注解2.3@SuppressWarnings注解3、元注解简介3.1@Target3.2@Rentention3.3@Documented3.4@Inherited4、自定义注解5、获取注解中的参数值5.1参数值是基本类型5.2参数值是对象1、基本介绍基本概念:注解,顾名思义,就是对某一事物进行添...

weixin_34017915的博客 4102

Java 注解

Java注解 文章目录Java注解注解(Annotation)概述常见注解生成文档相关的注解在编译时进行格式检查(JDK内置的三个基本注解)自定义注解JDK中的元注解@Retention@Target@Documented@InheritedJDK8中注解的新特性 注解(Annotation)概述 从 JDK 5.0 开始, Java 增加了对元数据(MetaData) 的支持, 也就是Annotation(注解) Annotation 其实就是代码里的特殊标记, 这些标记可以在编译, 类加 载,

TheAnnoyingDog的博客 754

java 注解和 重复注解

1、什么是java注解 Annotation(注解)是JDK5.0及以后版本引入的一个特性。注解java的一个新的类型(与接口很相似),它与类、接口、枚举是在同一个层次,它们都称作为java的一个类型(TYPE)。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。它的作用非常的多,例如:进行编译检查、生成说明文档、代码分析等。 2.jdk提供的3个基本注解

u012964143的专栏 3497

java 注解 default_java注解

注解:元注解的作用就是负责注解其他注解Java5.0定义了4个标准的meta-annotation类型,它们被用来提供对其它 annotation类型作说明。Java5.0定义的元注解:1.@Target,2.@Retention,3.@Documented,4.@Inherited这些类型和它们所支持的类在java.lang.annotation包中可以找到。下面我们看一下每个元注解的作用和...

weixin_39889329的博客 1837

java注解使用变量_java注解的使用

java注解的使用1.说再前面使用注解开发的好处1.使代码更加干净易读,易于维护修改。比如,以前使用spring的开发,都是基于xml文件实现了统一的配置管理,但是缺点也是显而易见的,就是随着项目的越来越大,xml文件会变得越来越复杂,维护成本也会越来越高。使用注解就可以提供更大的便捷性,易于维护修改。2 可以实现代码的类型检查,特别是在编译器的角度实现一些类型检查,比如预检查(@Overrid...

weixin_36143191的博客 4584
上一篇: Tomcat性能优化配置详解
下一篇: Linux下Java开发环境配置
我是你是我
博客等级 码龄19年 0粉丝 3原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值