来一点咖啡,准备好进入注解的世界。java
注解一直是 Java 的一个很是重要的部分,它从 J2SE 5.0 开始就已经存在了。在咱们的应用程序代码中,常常看到 @Override
和 @Deprecated
这样的注解。在本文中,我将讨论注解究竟是什么,为何引入注解,它们是如何工做的,如何编写自定义注解(有示例代码),注解的有效场景是什么,最后是注解和 ADF。这将是一个很长的帖子,因此来一点咖啡,准备好进入注解的世界。编程
用一个词来能够解释:注解便是元数据。元数据是关于数据的数据。因此注解是代码的元数据。举个例子,查看下面的代码。数组
@Override
public String toString() {
return "This is String Representation of current object.";
}复制代码
我在以上代码中重写 toString()
方法时使用了 @Override
注解。即便我不使用 @Override
,代码仍然可以正常工做,没有任何问题。那么,这个注解的优势是什么?又表明了什么?@Override
告诉编译器,此方法是一个重写的方法(有关该方法的元数据),若是父类中不存在此类方法,则引起编译器错误(超类中没有被覆盖其的方法)。如今,若是我犯了一个排版错误,并使用方法名为 toStrring() {double r}
若是我没有使用 @Override
,个人代码会成功地编译和执行,可是结果将会与预期不一样。如今,咱们理解了注解是什么,阅读正式的定义是颇有用处的。安全
注解是一种特殊的 Java 构造,用于修饰类、方法、字段、参数、变量、构造函数或包。这是 JSR-175 选择的提供元数据的工具。架构
在注解出现以前(甚至以后),XML 被普遍地用于元数据,可是,一部分特定的应用程序开发人员和架构师认为 XML 维护变得麻烦了。他们但愿可以经过某种方式与代码紧密耦合,来代替 XML,由于 XML 与代码很是松散地耦合(在某些状况下,几乎是独立的)。若是你在谷歌上搜索 “XML vs annotations”,你会发现不少有趣的争论。有趣的一点是,XML配置是为了将配置从代码中分离出来而引入的。最后两种说法可能会在你的脑海中产生一些疑问,即这两种说法都在创造一个循环,但二者各有优缺点。让咱们试着用一个例子来理解。框架
假设您但愿设置一些应用程序范围的常量/参数。在这个场景中,XML将是一个更好的选择,由于这与任何特定的代码段无关。若是您但愿将某些方法公开为服务,那么注解将是一个更好的选择,由于它须要与该方法紧密耦合,而且该方法的开发人员必须知道这一点。ide
另外一个重要因素是注解定义了在代码中定义元数据的标准方法。在注解以前,人们还使用本身的方法来定义元数据。一些例子是使用标记接口、注解、临时关键字等。每一个开发人员都须要根据本身的方式来决定元数据,可是注解是标准化的东西。函数
现在,大多数框架都将XML和注解结合起来,充分利用二者的优势。工具
在开始解释以前,我建议您下载这个注释(AnnotationsSample.zip) 的示例代码,并在您经常使用的 IDE 中保持打开,由于它将帮助您更好地理解下面的解释。google
编写注解很是简单。您能够将注解定义与接口定义进行比较。让咱们看两个例子 — 一个是标准 @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());
}
}复制代码
注解很是的强大,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 或过滤器上声明初始化参数的注解,在 WebFilter
或 WebServlet
上注解。
WebListener
- 用于在给定的Web应用程序上下文中声明各类类型事件的侦听器的注解。
WebServlet
- 此注解用于声明 servlet 的配置。
如今,咱们正在讨论的最后一部分:应用程序开发框架(ADF)。ADF 由 Oracle 开发,用于构建 Oracle 融合应用程序。咱们已经看到了优势和缺点,而且知道如何编写自定义注解,可是在 ADF 中咱们能够在哪里使用自定义注解呢?ADF 是否提供本地注解?
9月福利,关注公众号后台回复:004,领取8月翻译集锦!往期福利回复:001,002, 003便可领取!