来一点咖啡,准备好进入注解的世界。

注解一直是 Java 的一个非常重要的部分,它从 J2SE 5.0 开始就已经存在了。在我们的应用程序代码中,经常看到 @Override@Deprecated 这样的注解。在本文中,我将讨论注解到底是什么,为什么引入注解,它们是如何工作的,如何编写自定义注解(有示例代码),注解的有效场景是什么,最后是注解和 ADF。这将是一个很长的帖子,所以来一点咖啡,准备好进入注解的世界。

1. 注解是什么?

用一个词来可以解释:注解即是元数据。元数据是关于数据的数据。所以注解是代码的元数据。举个例子,查看下面的代码。

@Override
public String toString() {
return "This is String Representation of current object.";
}

我在以上代码中重写 toString() 方法时使用了 @Override 注解。即使我不使用 @Override,代码仍然能够正常工作,没有任何问题。那么,这个注解的优点是什么?又代表了什么?@Override 告诉编译器,此方法是一个重写的方法(有关该方法的元数据),如果父类中不存在此类方法,则引发编译器错误(超类中没有被覆盖其的方法)。现在,如果我犯了一个排版错误,并使用方法名为 toStrring() {double r} 如果我没有使用 @Override,我的代码会成功地编译和执行,但是结果将会与预期不同。现在,我们理解了注解是什么,阅读正式的定义是很有用处的。

注解是一种特殊的 Java 构造,用于修饰类、方法、字段、参数、变量、构造函数或包。这是 JSR-175 选择的提供元数据的工具。

2. 为什么引入注解?

在注解出现之前(甚至之后),XML 被广泛地用于元数据,但是,一部分特定的应用程序开发人员和架构师认为 XML 维护变得麻烦了。他们希望能够通过某种方式与代码紧密耦合,来代替 XML,因为 XML 与代码非常松散地耦合(在某些情况下,几乎是独立的)。如果你在谷歌上搜索 “XML vs annotations”,你会发现很多有趣的争论。有趣的一点是,XML配置是为了将配置从代码中分离出来而引入的。最后两种说法可能会在你的脑海中产生一些疑问,即这两种说法都在创造一个循环,但两者各有优缺点。让我们试着用一个例子来理解。

假设您希望设置一些应用程序范围的常量/参数。在这个场景中,XML将是一个更好的选择,因为这与任何特定的代码段无关。如果您希望将某些方法公开为服务,那么注解将是一个更好的选择,因为它需要与该方法紧密耦合,并且该方法的开发人员必须知道这一点。

另一个重要因素是注解定义了在代码中定义元数据的标准方法。在注解之前,人们还使用自己的方法来定义元数据。一些例子是使用标记接口、注解、临时关键字等。每个开发人员都需要根据自己的方式来决定元数据,但是注解是标准化的东西。

如今,大多数框架都将XML和注解结合起来,充分利用两者的优点。

3.注解如何工作以及如何编写自定义注解

在开始解释之前,我建议您下载这个注释(AnnotationsSample.zip) 的示例代码,并在您常用的 IDE 中保持打开,因为它将帮助您更好地理解下面的解释。

编写注解非常简单。您可以将注解定义与接口定义进行比较。让我们看两个例子 — 一个是标准 @Override 第二个注解是自定义注解。@Todo:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

似乎有些可疑之处 @Override 它没有做任何事情 — 它只是检查是否在父类中定义了一个方法。别惊讶,我不是在开玩笑。重写注解的定义只有那么多代码。这是需要理解的最重要的部分,我重申一遍:注解只是元数据,不包含任何业务逻辑。很难理解但却是真的。如果注解不包含逻辑,那么一定有人在做一些事情,并且有人是这个注解元数据的消费者。注解只提供有关定义的属性(类/方法/包/字段)的信息。消费者是一段代码,它读取这些信息,然后执行必要的逻辑。

当我们谈论标准注解时,比如 @Override , JVM 是使用者,它是在字节码级别工作的。这是应用程序开发人员无法控制、也不能用于自定义注解的东西。因此,我们需要为我们自己的注解写一些消费实例。

让我们逐一理解用于编写注解的关键术语。在上面的例子中,您将看到注解如何被应用。

J2SE 5.0 在 java.lang.annotation 包中提供了四个注解,它们仅在编写注解时使用:


@Documented – 是否将注解放在 Javadocs 中

@Retention – 注解什么时候被保留

@Target? – 注解可以使用的地方

@Inherited – 子类是否可以继承注解。


@Documented - 一个简单的标记注解,它标识了是否将注解添加到 Javadoc 中。

@Retention - 定义应保留注解的时间。

RetentionPolicy.SOURCE 在编译期间丢弃。这些注解在编译完成后没有任何意义,因此它们不会被写入字节码。例子:@Override, @SuppressWarnings

RetentionPolicy.CLASS 在类加载期间丢弃。应用在进行字节码级别的编译期间。有些令人惊讶的是,这是默认的。

RetentionPolicy.RUNTIME 不会丢弃。该注解可以在运行时进行反射。这是我们通常用于自定义注解的内容。

@Target - 注解可以使用的地方。如果不指定这一属性,注解可以应用在任何地方。以下是该注解的有效值。这里的一个要点,它只有包含的形式,这意味着如果您想要对7个属性进行注解,并且只想排除一个属性,这时需要在定义目标时包含所有7个属性。


ElementType.TYPE (类,接口,枚举)

ElementType.FIELD (实例变量)

ElementType.METHOD

ElementType.PARAMETER

ElementType.CONSTRUCTOR

ElementType.LOCAL_VARIABLE

ElementType.ANNOTATION_TYPE (用于其他注解)

ElementType.PACKAGE (记住 package-info.java)


@Inherited - 控制注解是否应该影响子类。

现在,注解定义中包含了什么?注解只支持基本类型、字符串和枚举。注解的所有属性都定义为方法,并且还可以提供默认值。

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface Todo {
public enum Priority {LOW, MEDIUM, HIGH}
public enum Status {STARTED, NOT_STARTED}
String author() default "Yash";
Priority priority() default Priority.LOW;
Status status() default Status.NOT_STARTED;
}

以下是如何使用上述注解的示例:

@Todo(priority = Todo.Priority.MEDIUM, author = "Yashwant", status = Todo.Status.STARTED)
public void incompleteMethod1() {
//Some business logic is written
//But it’s not complete yet
}

如果注解中只有一个属性,则应该将其命名为 “value”,并且可以在使用时不使用属性名称。

@interface Author{
String value();
}
@Author("Yashwant")
public void someMethod() {
}

目前为止一切顺利。我们已经定义了自定义注解,并将其应用于一些业务逻辑方法。现在,是时候写一个消费的实例了。为了完成这个目标,我们需要使用到反射。如果您熟悉反射代码,那么您就知道反射提供了类、方法和字段对象。所有这些对象都有一个 getAnnotation() 方法,该方法返回注解对象。我们需要将此对象转换为自定义注解(在使用 instanceOf() 检查之后),然后,我们可以调用自定义注解中定义的方法。让我们看一下示例代码,它使用了上面的注解:

Class businessLogicClass = BusinessLogic.class;
for(Method method : businessLogicClass.getMethods()) {
Todo todoAnnotation = (Todo)method.getAnnotation(Todo.class);
if(todoAnnotation != null) {
System.out.println(" Method Name : " + method.getName());
System.out.println(" Author : " + todoAnnotation.author());
System.out.println(" Priority : " + todoAnnotation.priority());
System.out.println(" Status : " + todoAnnotation.status());
}
}

4. 注解用例

注解非常的强大,Spring 和 Hibernate 等框架非常广泛地使用注解进行日志记录和验证。注解可以在使用标记接口的地方使用。标记接口用于整个类,但您可以定义可以用于单个方法的注解,例如,某个方法是否公开为服务方法。

在 servlet 3.0 规范中,引入了许多注解,特别是与 servlet 安全有关的注解。我们先来看看几个:

HandlesTypes - 此注解用于声明传递给 ServletContainerInitializer 的应用程序类数组。

HttpConstraint - 此注解表示应用于所有具有HTTP协议方法类型的请求的安全约束,在 ServletSecurity 上没有相应的 HttpMethodConstraint 注解时。

HttpMethodConstraint - 特定的安全约束可以应用于不同类型的请求,在 ServletSecurity 上注解。

MultipartConfig - 此注解用于指示声明它的 servlet 将使用 multiPart/form-Data MIME 类型发出请求。

ServletSecurity - 在 servlet 实现类上声明此注解,对HTTP协议请求执行安全约束。

WebFilter - 用于声明 servlet 过滤器的注解。

WebInitParam - 用于在 servlet 或过滤器上声明初始化参数的注解,在 WebFilterWebServlet 上注解。

WebListener - 用于在给定的Web应用程序上下文中声明各种类型事件的侦听器的注解。

WebServlet - 此注解用于声明 servlet 的配置。

5. 应用程序开发框架(ADF,Application Development Framework)和注解

现在,我们正在讨论的最后一部分:应用程序开发框架(ADF)。ADF 由 Oracle 开发,用于构建 Oracle 融合应用程序。我们已经看到了优点和缺点,并且知道如何编写自定义注解,但是在 ADF 中我们可以在哪里使用自定义注解呢?ADF 是否提供本地注解?

这些无疑是有趣的问题:但是是否存在某些限制来阻止在 ADF 中大规模使用注解?前面提到的框架(如 Spring 和 Hibernate ) 使用的 AOP (面向方面的编程)。在 AOP 中,框架提供了为任何事件的预处理和后处理注入代码的机制。例如,您有一个钩子在方法执行之前和之后放置代码,因此您可以在这些地方编写自定义代码。ADF 不使用 AOP。如果我们有任何有效的注解用例,我们可能需要通过继承方式

原文:https://dzone.com/videos/devnexus2015/how-annotations-work-java

作者:Yashwant Golecha

译者:遗失的拂晓

注解在Java中是如何工作的的更多相关文章

  1. java中jvm的工作原理

    首先我们安装了jdk和jre,但是jdk是为java软件开发工程师而使用的开发工具,我们运行java项目只要含有jre文件即可.对于jvm是内存分配的一块区域,我们知道,当我们开始使用java命令时, ...

  2. Java中GC的工作原理

    转文: 一个优秀的Java程序员必须了解GC的工作原理.如何优化GC的性能.如何与GC进行有限的交互,有一些应用程序对性能要求较高,例如嵌入式系统.实时系统等,只有全面提升内存的管理效率,才能提高整个 ...

  3. Java中动态代理工作流程

    当谈到动态代理就会想到接口,因为接口是一种规范,动态代理对象通过接口便会很清楚地知道他的实现类(被代理对象)是何种类型的(即有哪些方法).Now,然我们来开始编写一个例子来了解动态代理的全过程: 第一 ...

  4. java中的注解(Annotation)

    转载:https://segmentfault.com/a/1190000007623013 简介 注解,java中提供了一种原程序中的元素关联任何信息.任何元素的途径的途径和方法. 注解是那些插入到 ...

  5. Java中@Deprecated、@SupressWarning、@Override的作用

    Annotation注解在 Java 中有着很广泛的,他是做为一种标识 为javac所识别.每一个注解 都对应这一个Java类  在java.lang包中 有三个注解  分别是 Deprecated  ...

  6. Java中的反射和注解

    前言 在Java中,反射机制和注解机制一直是一个很重要的概念,那么他们其中的原理是怎么样呢,我们不仅仅需要会使用,更要知其然而之所以然. 目录 反射机制 反射如何使用 注解定义 注解机制原理 注解如何 ...

  7. 【java】详解java中的注解(Annotation)

    目录结构: contents structure [+] 什么是注解 为什么要使用注解 基本语法 4种基本元注解 重复注解 使用注解 运行时处理的注解 编译时处理的注解 1.什么是注解 用一个词就可以 ...

  8. [转]Java中实现自定义的注解处理器

    Java中实现自定义的注解处理器(Annotation Processor) 置顶2016年07月25日 19:42:49 阅读数:9877 在之前的<简单实现ButterKnife的注解功能& ...

  9. Java中的APT的工作过程

    Java中的APT的工作过程 APT即Annotatino Processing Tool, 他的作用是处理代码中的注解, 用来生成代码, 换句话说, 这是用代码生成代码的工具, 减少boilerpl ...

随机推荐

  1. Java多线程之Runnable与Thread

    Java多线程之Thread与Runnable 一.Thread VS Runnable 在java中可有两种方式实现多线程,一种是继承Thread类,一种是实现Runnable接口:Thread类和 ...

  2. python大纲+变量基础详解

    1.python的历史 ​ python2和python3的区别 ​ 2004 Django框架的诞生 2.python是编程语言 ​ python 是解释型语言 3.python的种类 ​ cpyt ...

  3. 【原创】Linux PSCI框架

    背景 Read the fucking source code! --By 鲁迅 A picture is worth a thousand words. --By 高尔基 说明: Kernel版本: ...

  4. AOJ-2249-Road Construction-dijkstra-最小花费

    Road Construction 题意:在一个无向图中,每条边上有建设的花费和路径的长度,要求求得,在保持每个点到1号点最小距离不变的情况下,求最小的总花费: 思路:用dijkstra 找出每个点的 ...

  5. Docker详解(一)

    目录 Docker简介 Docker组成 永远的HelloWorld 序言:众所周知,近几年的互联网各项技术发展的如火如荼,敏捷开发模式越来越普及,"快"似乎成为了行业的标准,于是 ...

  6. 一篇文章看懂JS执行上下文

     壹 ❀ 引 我们都知道,JS代码的执行顺序总是与代码先后顺序有所差异,当先抛开异步问题你会发现就算是同步代码,它的执行也与你的预期不一致,比如: function f1() { console.lo ...

  7. 在Linux中增加swap空间

    转自: http://blog.csdn.net/chinalinuxzend/article/details/1759593 在Linux中增加swap空间 在安装Linux的时候,不知道swap空 ...

  8. Codeforces1093E_Intersection of Permutations

    题意 给定两个排列a和b,两种操作,交换b_i和b_j,询问a[l_a...r_a]和b[l_b...r_b]有多少个数相同. 分析 由于给的是排列,保证b的每个数都有a的对应,构造数组c,c[i]表 ...

  9. 【LeetCode】435-无重叠区间

    题目描述 给定一个区间的集合,找到需要移除区间的最小数量,使剩余区间互不重叠. 注意: 可以认为区间的终点总是大于它的起点. 区间 [1,2] 和 [2,3] 的边界相互"接触", ...

  10. 使用图灵机器人api搭建微信聊天机器人php实现

    之前通过hook技术实现了微信pc端发送消息功能,如果在结合图灵机器人就能实现微信聊天机器人. 代码下载:http://blog.yshizi.cn/131.html 逻辑如下: 下面我简单介绍一下步 ...