您的位置 首页 java

我的Java Web之路18 – Java注解(Annotation)

本系列文章旨在记录和总结自己在Java Web开发之路上的知识点、经验、问题和思考,原来已经分享在我的CSDN博客,现在分享在头条,希望能帮助更多 码农 和想成为码农的人。版权声明:本文为CSDN博主「普通的码农」的原创文章,遵循CC 4.0 by-sa版权协议,转载请附上原文出处链接及本声明。原文链接:
 

目录

  1. 介绍
  2. 注解的思想、原理、本质
  3. 为什么要引入 Java 注解
  4. Java注解的定义
  5. Java注解的使用
  6. Java注解的解释和处理
  7. 总结

介绍

介绍了如何基于 servlet 技术建立我们的第一个Java Web应用,并且通过阅读Tomcat提供的(Servlet规范的一个实现)源码,了解到 抽象类 接口 多态 等Java基本概念。

但是,还有一个很重要的技术我们还没介绍,这就是在配置我们开发的Servlet时用到的 注解( Annotation ,这里再次把我们的第一个Java Web应用的源码展示出来:

package com.example;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.WebServlet;
@WebServlet(urlPatterns = {"/hello"}) //这就是注解
public class HelloWorld extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContent TYPE ("text/ html ");
PrintWriter writer = response.getWriter();
writer.print("<html><head></head><body>"
+ "<h1>Hello World! Your IP is " + request.getRemoteHost()
+ "</h1>"
+ "</body></html>");
}
}
 

代码中的 @WebServlet 就是一个注解,本篇我们就介绍 Java注解

注解的思想、原理、本质

本质上,Java注解技术也是源于 打标记 的思想。

还记得我们之前介绍过的也是基于打标记思想的技术吗?就是 网页HTML 技术。所以,它们是有异曲同工之妙的。不过,也是完全不一样的技术,我们可以从以下方面来进行比较:

  • 标记的定义 :HTML的标记是由相应的标准组织定义和扩展的;Java注解可以由用户自己定义,而Java语言本身定义了大量注解,第三方库也提供了大量的与库本身相关的注解。
  • 标记的作用目标 :HTML的标记作用在要呈现给用户的信息上;Java注解作用在Java代码上。
  • 标记的解释和处理 :HTML的标记是由浏览器来解释和处理;Java注解就比较复杂且强大多了,它其实说是由其他工具或代码来解释和处理,比如Java编译器可以解释和处理某些注解,第三方工具解释和处理自己提供的注解(比如Tomcat解释并处理@WebServlet),用户可以自己开发代码来解释自己定义的注解。

所谓标记,用专业一点的术语来说就是 元数据 ,即描述数据的数据。

HTML中,要呈现给用户的信息是数据,HTML标签就是描述它们的数据;

Java代码(类、属性域、方法、参数等等)也是一种数据,Java注解就是描述它们的数据。

为什么要引入Java注解

Java注解其实是在Java 1.5版本(或者说是Java 5)引入的,那么为什么要引入这种技术呢?或者说引入Java注解是为了解决什么问题呢?

从Java注解的本质上看,它是一种标记,那么我们就可以针对这个标记进行解释和处理,从而达到对Java代码在 编译时 进行检测,甚至在Java程序 运行时 执行到有Java注解的代码时针对该注解的解释和处理可以添加某些功能逻辑。

这就是标记本身存在的意义,毕竟给某些数据打上标记,当然就是为了针对该标记进行某种解释和处理,你可以用来检测,可以用来配置等等。

但是打标记也只是元数据的其中一种方式而已,你当然可以在代码之外单独的对代码进行描述,比如广泛使用的xml,它广泛使用在Java程序的配置上。既然xml这种技术也可以实现元数据的思想,那么为什么Java还要引入注解技术呢?

答案在于xml技术是 基于代码与配置分离(即松耦合)原则 ,而有时候我们却又希望使用一些 与代码紧耦合 的东西,这样 更方便更易于维护 ,所以Java注解应运而生。这两种方式各有利弊,各有自己的使用场合。

另外,在Java注解之前,描述元数据的方式不仅限于xml,还有 标记接口 注释 transient 关键字等等(这些暂不讨论),因此处于一种混乱不堪的状态,我们需要 一种标准的方式 来描述元数据,这就是Java注解。

Java注解的定义

前面提到,Java注解是由我们用户来定义的,就是说谁都可以定义Java注解。事实上,Java注解就跟Java类和接口一样,谁都可以定义,它也需要 抽象 出来。

我们先来看看@WebServlet是怎样定义的,与 一样,可以查看它的源码:

package javax.servlet.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WebServlet {
 String name() default "";
 String[] value() default {};
 String[] urlPatterns() default {};
 int loadOnStartup() default -1;
 WebInitParam[] initParams() default {};
 boolean asyncSupported() default false;
 String smallIcon() default "";
 String largeIcon() default "";
 String description() default "";
 String displayName() default "";
}
 

我在这省略了大量的注释。

首先,我们可以看到定义注解的关键字是 @interface 是在接口关键字前面加上一个@符号,由此可见注解与接口是多么的相似。注解的名称、修饰符与定义类和接口时遵从的规范是一样的。

然后,我们看注解体。既然跟接口相似,那么里面的内容也就差不多,都是一些方法。不过,这些方法的方法名明显都是 名词 ,实际上这是定义注解的 属性 ,比如WebServlet这个注解有以下这个方法:

String[] urlPatterns() default {};
 

所以,在使用WebServlet这个注解时,可以指定urlPatterns这个属性,它的属性值是字符串数组类型。当然,后面指定了它的默认值是一个空数组。所以,可以像下面的方式那样使用这个注解:

@WebServlet(urlPatterns = {"/hello"}) 
 

Java语言规定,注解体中的方法返回类型只能是:int等基本数据类型、String、 enum (暂且不讨论)、其他注解(比如上面的WebInitParam其实也是一个注解),以及它们对应的数组。

最后,我们来看看WebServlet上面的内容:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
 

很明显,它们也是注解,从import语句可以看出它们来自于Java语言(就是JDK库)。它们其实可以叫做 元注解 ,就是专门注解其他注解的,感觉有点绕。Java提供了四个元注解:

  • @Target :这就是前面提到的 标记的目标 ,即指定你所定义注解的作用目标,大的方面是Java代码,但这没什么意义,更细的目标应该是类、接口、属性域、方法、 构造方法 、参数等等,这个我们可以继续通过查看 ElementType JavaDoc 或者源码看到。
  • @Retention :指定你所定义注解的生命周期,即 注解起作用的时间 ,我们同样可以通过看 RetentionPolicy 的源码,得知注解的生命周期有:
  1. SOURCE :源码阶段,即在编译结束之后就不再有任何意义,所以它们不会写入 字节码
  2. CLASS :字节码阶段,在类加载的时候丢弃。在字节码文件的处理中有用。注解默认使用这种方式。
  3. RUNTIME :运行时阶段,始终不会丢弃,运行期也保留该注解,因此可以使用 反射机制 读取该注解的信息。我们自定义的注解通常使用这种方式。
  • @Documented :指定你所定义的注解是否包含在某个目标(当然该目标使用了你所定义的注解)的JavaDoc中。
  • @Inherited :指定你所定义的注解是否允许子类继承。

自定义注解时,@Target和@Retention通常都会指定,大体就是这样:

@Target( [ElementType.TYPE] )
@Retention( [RetentionPolicy.RUNTIME] )
[public] @interface [annotationName] {
 [String] [fieldName]() [default ""];
 //其他属性省略
}
 

方括号中的都是用户可以自己修改的。

最后,从注解的定义可以看到,它感觉就像是接口一样,不包含任何业务逻辑、功能逻辑,它就只是定义了一个标记而已,这个标记有一些属性。

那么必须有人来实现这些逻辑,这些逻辑就是能够读取并识别这些注解,能够获取到使用注解时赋予属性的值,然后根据它们来执行某些功能。这就是 注解的解释和处理

Java注解的使用

HTML标签的使用是把标记名称放在 尖括号 中,而且有的还有结束标签。

Java注解的使用是在注解名称加上 @符号 ,然后使用小括号,在小括号中为注解的属性赋值即可。比如:

@WebServlet(urlPatterns = {"/hello"}) 
 

当然,注解必须使用在指定的目标类型上,如果定义注解时指定目标是ElementType.TYPE,那么它就只能用在类、接口(包括注解)、枚举(enum)上;

如果是ElementType.FIELD,那么它就只能用在类的属性域(包括枚举常量)上;等等。

剩下的可以我们可以自己看JavaDoc或源码。

从WebServlet注解的使用可以看出,为注解的名称和属性起一个恰当的名字也是相当重要的。

另外,如果注解的属性只有一个且名称为value的时候,使用时可以省略value=。

我们可以把注解简单的理解为一个特殊的接口,那么注解的使用也就可以简单的理解为:

  1. 定义了一个实现该接口的类;
  2. 调用了该类的构造方法生成了一个对象。

至少从使用形式上看是差不多的。

Java注解的解释和处理

由于Java注解的解释和处理用到了Java反射技术,我们以后再讨论。

总结

好,现在我们明白Java注解是什么,起什么作用,能用来干什么了。大多数情况下,我们只要会使用别人提供的注解就行了。如果非要为自己开发的程序提供注解,那么通常都有两项任务:

  • 定义注解;
  • 编写注解的解释和处理逻辑。

下面是一些总结:

  • 注解也是源于 打标记 的思想,即 元数据
  • 不同于xml, Java注解与代码是紧耦合的 ,实际上Java注解的修改也必须要重新编译,所以必须考虑什么场景比较适合用注解,什么场景适合用xml;
  • Java注解提供了一种 标准的定义元数据的方式
  • Java注解的定义使用 @interface
  • Java注解的定义是不包含任何业务逻辑的,只是定义了一个标记名称及其拥有的属性,仅此而已;
  • Java注解的定义和使用都可以拿 接口 来类比,形式上很像。
  • 任何事物都有 生命周期 ,Java注解也一样,它有三种生命周期,分别是源码级别、字节码级别、运行时级别。

文章来源:智云一二三科技

文章标题:我的Java Web之路18 – Java注解(Annotation)

文章地址:https://www.zhihuclub.com/200525.shtml

关于作者: 智云科技

热门文章

网站地图