百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 优雅编程 > 正文

Java 深度历险:Java 注解(java注解教程)

sinye56 2024-10-16 15:14 18 浏览 0 评论

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

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

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

注解处理器的处理逻辑都在 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 的做法,只需要一个类就可以了。

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

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

实例分析

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

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

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

参考资料

  • JDK 5和JDK 6中的 apt 工具说明文档
  • Pluggable Annotation Processing API
  • APT: Compile-Time Annotation Processing with Java

相关推荐

程序员:JDK的安装与配置(完整版)_jdk的安装方法

对于Java程序员来说,jdk是必不陌生的一个词。但怎么安装配置jdk,对新手来说确实头疼的一件事情。我这里以jdk10为例,详细的说明讲解了jdk的安装和配置,如果有不明白的小伙伴可以评论区留言哦下...

Linux中安装jdk并配置环境变量_linux jdk安装教程及环境变量配置

一、通过连接工具登录到Linux(我这里使用的Centos7.6版本)服务器连接工具有很多我就不一一介绍了今天使用比较常用的XShell工具登录成功如下:二、上传jdk安装包到Linux服务器jdk...

麒麟系统安装JAVA JDK教程_麒麟系统配置jdk

检查检查系统是否自带java在麒麟系统桌面空白处,右键“在终端打开”,打开shell对话框输入:java–version查看是否自带java及版本如图所示,系统自带OpenJDK,要先卸载自带JDK...

学习笔记-Linux JDK - 安装&配置

前提条件#检查是否存在JDKrpm-qa|grepjava#删除现存JDKyum-yremovejava*安装OracleJDK不分系统#进入安装文件目...

Linux新手入门系列:Linux下jdk安装配置

本系列文章是把作者刚接触和学习Linux时候的实操记录分享出来,内容主要包括Linux入门的一些理论概念知识、Web程序、mysql数据库的简单安装部署,希望能够帮到一些初学者,少走一些弯路。注意:L...

测试员必备:Linux下安装JDK 1.8你必须知道的那些事

1.简介在Oracle收购Sun后,Java的一系列产品就被整合到Oracle官网中,打开官网乍眼一看也不知道去哪里下载,还得一个一个的摸索尝试,而且网上大多数都是一些Oracle收购Sun前,或者就...

Linux 下安装JDK17_linux 安装jdk1.8 yum

一、安装环境操作系统:JDK版本:17二、安装步骤第一步:下载安装包下载Linux环境下的jdk1.8,请去官网(https://www.oracle.com/java/technologies/do...

在Ubuntu系统中安装JDK 17并配置环境变量教程

在Ubuntu系统上安装JDK17并配置环境变量是Java开发环境搭建的重要步骤。JDK17是Oracle提供的长期支持版本,广泛用于开发Java应用程序。以下是详细的步骤,帮助你在Ubuntu系...

如何在 Linux 上安装 Java_linux安装java的步骤

在桌面上拥抱Java应用程序,然后在所有桌面上运行它们。--SethKenlon(作者)无论你运行的是哪种操作系统,通常都有几种安装应用程序的方法。有时你可能会在应用程序商店中找到一个应用程序...

Windows和Linux环境下的JDK安装教程

JavaDevelopmentKit(简称JDK),是Java开发的核心工具包,提供了Java应用程序的编译、运行和开发所需的各类工具和类库。它包括了JRE(JavaRuntimeEnviro...

linux安装jdk_linux安装jdk软连接

JDK是啥就不用多介绍了哈,外行的人也不会进来看我的博文。依然记得读大学那会,第一次实验课就是在机房安装jdk,编写HelloWorld程序。时光飞逝啊,一下过了十多年了,挣了不少钱,买了跑车,娶了富...

linux安装jdk,全局配置,不同用户不同jdk

jdk1.8安装包链接:https://pan.baidu.com/s/14qBrh6ZpLK04QS8ogCepwg提取码:09zs上传文件解压tar-zxvfjdk-8u152-linux-...

运维大神教你在linux下安装jdk8_linux安装jdk1.7

1.到官网下载适合自己机器的版本。楼主下载的是jdk-8u66-linux-i586.tar.gzhttp://www.oracle.com/technetwork/java/javase/downl...

window和linux安装JDK1.8_linux 安装jdk1.8.tar

Windows安装JDK1.8的步骤:步骤1:下载JDK打开浏览器,找到JDK下载页面https://d.injdk.cn/download/oraclejdk/8在页面中找到并点击“下载...

最全的linux下安装JavaJDK的教程(图文详解)不会安装你来打我?

默认已经有了linux服务器,且有root账号首先检查一下是否已经安装过java的jdk任意位置输入命令:whichjava像我这个已经安装过了,就会提示在哪个位置,你的肯定是找不到。一般我们在...

取消回复欢迎 发表评论: