`
holdbelief
  • 浏览: 706015 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

第十三课时:Annotation 注释

阅读更多

 

从 JDK 5.0 开始,Java 增加了对元数据 (MetaData)的支持,也就是 Annotation (注释)。用 Annotation 程序员可以在不改变原有逻辑的情况下,在源文件中嵌入一些补充的信息。代码分析工具、开发工具和部署工具可以通过这些补充信息进行验证或者进行部署。

Annotation 提供了一条为程序元素设置元数据的方法,从某些方面来看,Annotation 就像修饰符一样被使用,可以用于修饰包、类、构造器、方法、成员变量、参数、局部变量的声明,这些信息被保存在 Annotation 的 “name=value” 对中。

Annotation 是一个接口,程序可以通过反射来获取指定程序元素的 Annotation 对象,然后通过 Annotation 对象来取得注释里的元数据。

 

一、基本的 Annotation

Java 提供的三个基本 Annotation 的用法:使用 Annotation 时要在其前面增加 @ 符号,并把该 Annotation 当成一个修饰符使用,用于修饰它支持的的程序元素:
  • @Override
  • @Deprecated
  • @SuppressWarnings
Java 提供的三个基本Annotation 都定义在 java.lang 包下。

1、限定重写父类方法:@Override
@Override 就是用来指定方法覆载的,它可以强制一个子类必须要覆盖父类的方法
示例:
public class Fruit
{
public void info()
System.out.println("水果的info方法。。。"); 
}

class Apple extends Fruit
{
//使用@Override指定下面方法必须重写父类方法
@Override 
// public void info()
public void inf0()
System.out.println("苹果重写水果的info方法。。。"); 
}
}

2、标示已过时:@Deprecated
示例:
class Apple
{
//定义info方法已过时
@Deprecated
public void info()
{
System.out.println("Apple的info方法");
}
}
public class DeprecatedTest
{
public static void main(String[] args) 
{
//下面使用info方法时将会被编译器警告
new Apple().info();
}
}

3、抑制编译器警告
示例:取消没有泛型限制的集合引发的编译器警告
@SuppressWarnings(value="unchecked")
public class SuppressWarningsTest
{
public static void main(String[] args) 
{
List<String> myList = new ArrayList();
}
}

二、自定义 Annotation
1、定义 Annotation
定义新的 Annotation 类型使用 @interface 关键字,例如:
// 定义一个简单的 Annotation 类型
public @interface Test
{}
使用 Test 注释:
// 用 @Test 修饰类定义
@Test
public class MyClass
{
……
}

默认情况下,Annotation 可用于修饰任何程序元素,包括类、接口、方法等。
public class MyClass
{
// 使用 @Test Annotation 修饰方法
@Test
public void info()
{
……
}
……
}

定义带成员变量的 Annotation,Annotation 的成员变量在 Annotation 定义中以无参数方法的形式声明,其方法名和返回值定义了该成员变量的名字和类型。示例:
public @interface MyTag
{
// 定义两个成员变量的 Annotation
// Annotation 中的成员变量以方法的形式定义
String name();
int age();
}
使用带成员变量的 Annotation:
public class Test
{
// 使用带成员变量的 Annotation 时,需要为成员变量赋值
@MyTag(name="xx", age=6)
public void info()
{
……
}
……
}

还可以在定义 Annotation 的成员变量时为其指定初始值,使用 default 关键字,示例:
public @interface MyTag
{
String name() default "liuchuanfeng";
int age() default 16;
}
如果为 Annotation 的成员变量指定了默认值,使用该 Annotation 则可以不为这些成员变量指定值,而是直接使用默认值。

2、提取 Annotation 的信息
Java 使用 Annotation 接口来代表程序元素前面的注释,该接口是所有 Annotation 类型的父接口,除此之外,Java 在 java.lang.reflect 包下新增了 AnnotatedElement 接口,该接口代表程序中可以接受注释的程序元素,该接口主要有以下几个实现类:
  • Class:类定义
  • Constructor:构造器定义
  • Field:类成员变量定义
  • Mehtod:类的方法定义
  • Package:类的包定义
AnnotatedElement 接口是所有程序元素(如:Class、Mehtod、Constructor)的父接口,所以程序通过反射获得了某个类的 AnnotatedElement 对象(如:Class、Mehtod、Constructor)之后,程序就可以调用对象的如下三个方法来访问 Annotation 的信息:
  • getAnnotation(Class<T> annotationClass):返回该程序元素上存在的、指定的类型的注释,如果该类型的注释不存在,则返回 null
  • Annotation[] getAnnotations():返回程序元素上存在的所有注释
  • boolean isAnnotationPresent(Class<? extends Annotation> annotationClass):判断该程序元素上是否包含指定类型的注释,存在则返回 true,否则返回 false
示例:下面程序片段用于获取 Test 类 info 方法里的所有注释:
Annotation[] aArray = Class.forName("Test").getMethod("info").getAnnotations();
for (Annotation an : aArray)
{
System.out.println(an);
}
如果需要获取某个注释里的元数据,则可以将注释强制类型转换成所需要的注释类型,然后通过注释对象的抽象方法来访问这些数据,如:
Annotation[] annotation = tt.getClass().getMethod("info").getAnnotations();
// 遍历每个注释对象
for (Annotation tag : annotation)
{
// 如果 tag 注释是 MyTag1 类型
if(tag instanceof MyTag1)
{
System.out.println("Tag is:" + tag);
System.out.println("tag.name(): " + ((MyTag1) tag).method1());
System.out.println("tag.age(): " + ((MyTag1) tag).method2());
}
// 如果 tag 注释是 MyTag2 类型
if(tag instanceof MyTag2)
{
System.out.println("Tag is:" + tag);
System.out.println("tag.name(): " + ((MyTag2) tag).method1());
System.out.println("tag.age(): " + ((MyTag2) tag).method2());
}
}

3、使用 Annotation 的例子
Annotation Testable 没有成员变量,仅仅是一个标记 Annotation,它的作用是标记哪些方法是可测试的。

Testable.java:
import java.lang.annotation.*; 
 
@Retention(RetentionPolicy.RUNTIME)    
@Target(ElementType.METHOD)
//定义一个标记注释,不包含任何成员变量,即不可传入元数据
public @interface Testable 
{
}

MyTest 测试用例里定义了 8 个方法,这 8 个方法没有太大的区别,其中四个方法是用 @Testable 注释来标记这些方法是可测试的。
MyTest
MyTest.java:
public class MyTest
{
//使用@Testable标记注释指定该方法是可测试的
@Testable
public static void m1() 
{
}
public static void m2() 
{
}   
//使用@Testable标记注释指定该方法是可测试的
@Testable
public static void m3() 
{        
throw new RuntimeException("Boom");  
}
public static void m4()
{
}       
//使用@Testable标记注释指定该方法是可测试的
@Testable
public static void m5()
{
}  
    public static void m6()
{
}
//使用@Testable标记注释指定该方法是可测试的
@Testable
public static void m7()
{            
throw new RuntimeException("Crash");   
}        
public static void m8()
{
}
}
仅仅使用注释来标识程序元素对程序是不会有任何影响的,这也是 Java 注释的一条重要原则,为了让程序中这些注释起作用,我们必须为这些注释提供一个注释处理工具。
下面的注释处理工具会分析目标类,如果目标类中方法使用了@Testable 注释修饰,则通过反射运行该测试方法。
public class TestProcessor
{
public static void process(String clazz) throws ClassNotFoundException
{
int passed = 0;
int failed = 0;

// 遍历 obj 对象的所有方法
for (Method m : Class.forName(clazz).getMethods())
{
// 如果包含 @Testable 标记注释
if(m.isAnnotationPresent(Testable.class))
{
try
{
// 调用 m 方法
m.invoke(null);
// passed 加 1
passed++;
}
catch(Exception ex)
{
System.out.printf("方法" + m + "运行失败,异常:" + ex.getCause() + "\n");
failed++;
}
}
}

// 统计测试结果
System.out.printf("共运行了:" + (passed + failed)+ "个方法,其中:\n" + 
"失败了:" + failed + "个,\n" +  
"成功了:" + passed + "个!\n"); 
}
}

主函数类:RunTests.java
public class RunTests
{
public static void main(String[] args) throws Exception
{
// 处理 MyTest 类
TestProcessor.process("MyTest");
}
}

带成员变量的 Annotation 示例:
ActionListenerFor.java
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ActionListenerFor
{
// 定义一个成员变量,用于设置元数据
// 该 listener 成员变量用于保存监听器实例
String listener();
}

public class AnnotationTest
{
private JFrame mainWin = new JFrame("使用注释绑定事件监听器");
// 使用注释为 ok 按钮绑定事件监听器
@ActionListenerFor(listener="OkListener")
private JButton ok = new JButton("确定");
// 使用注释为 cancel 按钮绑定事件监听器
@ActionListenerFor(listener = "CancelListener")
private JButton cancel = new JButton("取消");

public void init()
{
// 初始化主界面
JPanel jp = new JPanel();
jp.add(ok);
jp.add(cancel);
mainWin.add(jp);
ActionListenerInstaller.processAnnotations(this); // ①
mainWin.setDefaultCloseOption(JFrame.EXIT_ON_CLOSE);
mainWin.pack();
mainWin.setVisible(ture);
}

public static void main(String[] args)
{
new AnnotationTest.init();
}
}

// 定义 ok 按钮的事件监听器实现类
class OkListener implements ActionListener
{
public void actionPerformed(ActionEvent evt)
{
JOptionPane.show(null, "点击了确认按钮");
}
}

// 定义 cancel 按钮的事件监听器实现类
class CanelListener implements ActionListener
{
public void actionPerformed(ActionEvent evt)
{
JOptionPane.show(null, "点击了取消按钮");
}
}

正如前面提到,如果仅仅在程序中使用注释是不会有任何作用的,必须使用注释处理工具来处理程序中的注释。程序中 ① 处代码使用了 ActionListenerInstaller 来处理本程序中的注释,该处理器分析目标对象中所有的 Field,如果该 Field 前使用了 ActionListenerFro Annotation,则取出该 Annotation 中的 listener 元数据,并根据该数据来绑定事件监听器。
ActionListenerInstaller.java
public class ActionListenerInstaller
{
try
{
// 获取 obj 对象的类
Class c1 = obj.getClass();
// 获取指定的 obj 对象中所有的 Field,并遍历每个 Field
for (Field f : c1.getDeclaredFields())
{
// 将指定 Field 设置成可自由访问的,避免 private 的 Field 不可访问
f.setAccessible(true);
// 获取指定 Field 的 ActionListenerFor 类型的注释
ActionListenerFor a = f.getAnnotation(ActionLinstenerFor.class);
if (a != null && a instanceof AbstractButton) 
{
// 获取 a 注释里的元数据 listener (它是一个监听器类)
Class listenerClazz = Class.forName(a.listener());
// 使用反射来创建 listener 类的对象
ActionListener al = listenerClazz.newInstance();
// 获取 f Field 实际对应的对象
AbstractButton b = (AbstractButton) f.get(obj);
//为ab对象添加事件监听器 ab.addActionListener(al);
}
}
}
catch (Exception e)
{
e.printStackTrace();
}
}

三、JDK 的元 Annotation
JDK 除了在 java.lang 下提供了 3 个基本 Annotation 之外,还在 java.lang.annotation 包下提供了四个 Meta Annotation (元 Annotation),这四个 Annotation 都是用来修饰其他的 Annotation 定义。
1、使用 @Retention
@Retention 只能用于修饰一个 Annotation 定义,用于指定该 Annotation 可以保留多长时间,@Retention 包含一个 RetentionPolicy 类型的 value 成员变量,
value 成员变量的值只能有如下三个:
  • RetentionPolicy.CLASS:编译器将吧主是记录在 class 文件中。当运行 Java 程序时,JVM 不再保留注释。这是默认值
  • RetentionPolicy.CLASS:编译器将吧主是记录在 class 文件中。当运行 Java 程序时,JVM 也会保留注释,程序可以通过反射获取该注释
  • RetentionPolicy.CLASS:编译器直接丢弃这种策略的注释
2、使用 @Target
@Target 用于指定被修饰的 Annotation 能用于修饰哪些程序元素,它也包含一个名为 value 的成员变量,该成员变量的值只能是如下几个:
  • ElementType.ANNOTATION_TYPE
  • ElementType.CONSTRUCTOR
  • ElementType.FIELD
  • ElementType.LOCAL_VARIABLE
  • ElementType.METHOD
  • ElementType.PACKAGE
  • ElementType.PARAMETER
  • ElementType.TYPE
3、使用 @Documented
@Documented 用于指定被该元 Annotaton 修饰的 Annotation 类将被 javadoc 工具提取成文档,如果定义 Annotation 类时使用了 @Documented 修饰,则所有使用该 Annotation 修饰的程序元素的 API 文档中将会包含该 Annotation 说明
分享到:
评论

相关推荐

    拦截器与冲突解决

    然而,在使用`&lt;mvc:annotation-driven /&gt;`元素时,有时会出现与自定义拦截器的冲突问题。这个问题通常出现在当我们试图同时配置基于注解的控制器处理和自定义拦截器时,Spring可能无法正确地处理这些组件的执行顺序...

    SpringMVC源码总结(二)mvc:mvc:annotation-driven背后的那些事

    在Spring MVC框架中,`mvc:annotation-driven`是Spring MVC配置中的一个重要元素,它使得我们的应用能够支持基于注解的控制器、数据绑定、格式化转换器和服务端验证等功能。这篇博客将深入探讨`mvc:annotation-...

    SpringMVC源码总结(三)mvc:annotation-driven和mvc:message-converters简单介绍

    在Spring MVC框架中,`mvc:annotation-driven`和`mvc:message-converters`是两个非常重要的元素,它们在处理基于注解的控制器和数据转换方面起着关键作用。本篇文章将深入探讨这两个组件的工作原理以及如何在实际...

    jakarta.annotation-api-1.3.5-API文档-中文版.zip

    赠送jar包:jakarta.annotation-api-1.3.5.jar; 赠送原API文档:jakarta.annotation-api-1.3.5-javadoc.jar; 赠送源代码:jakarta.annotation-api-1.3.5-sources.jar; 赠送Maven依赖信息文件:jakarta.annotation...

    (代码)SpringMVC第12讲:<mvc:annotation-driven/>

    首先,`&lt;mvc:annotation-driven/&gt;`的作用是自动配置Spring MVC,启用对处理方法注解的支持,如`@RequestMapping`、`@RequestParam`、`@ModelAttribute`等。通过这个元素,我们可以避免编写大量的XML配置,转而采用...

    spring的annotation-driven配置事务管理器详解 (多数据源配置

    Spring 的 Annotation-Driven 配置事务管理器详解(多数据源配置) Spring 框架提供了强大的事务管理机制,通过使用 Annotation-Driven 配置,可以方便地管理事务。在多数据源配置中,spring 的 Annotation-Driven...

    jakarta.annotation-api-1.3.5-API文档-中英对照版.zip

    赠送jar包:jakarta.annotation-api-1.3.5.jar; 赠送原API文档:jakarta.annotation-api-1.3.5-javadoc.jar; 赠送源代码:jakarta.annotation-api-1.3.5-sources.jar; 赠送Maven依赖信息文件:jakarta.annotation...

    javax.annotation-api-1.2-API文档-中文版.zip

    赠送jar包:javax.annotation-api-1.2.jar; 赠送原API文档:javax.annotation-api-1.2-javadoc.jar; 赠送源代码:javax.annotation-api-1.2-sources.jar; 赠送Maven依赖信息文件:javax.annotation-api-1.2.pom;...

    javax.annotation-api-1.3.2-API文档-中文版.zip

    赠送jar包:javax.annotation-api-1.3.2.jar; 赠送原API文档:javax.annotation-api-1.3.2-javadoc.jar; 赠送源代码:javax.annotation-api-1.3.2-sources.jar; 赠送Maven依赖信息文件:javax.annotation-api-...

    androidx-annotation-1.2.0.jar

    androidx-annotation-1.2.0.jar

    annotation-1.1.0.jar

    @androidx.annotation.NonNull 缺失的兼容、androidx.annotation兼容包

    spring源代码分析:annotation支持的实现

    在Spring框架中,注解(Annotation)支持是其核心特性之一,它极大地简化了配置,提高了代码的可读性和可维护性。这篇博客"spring源代码分析:annotation支持的实现"探讨了Spring如何通过注解处理来实现组件扫描和...

    探索Java注解的神秘世界:Annotation全解析

    import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @...

    androidx-annotation-1.0.0.jar

    androidx-annotation-1.0.0.jar

    mybatis学习总结:annotation与xml结合示例

    本篇文章将聚焦于MyBatis中的注解(Annotation)与XML配置的结合使用,旨在帮助开发者更深入地理解这一关键特性。 首先,MyBatis允许我们使用注解来简化Mapper接口的定义,无需编写XML映射文件。例如,我们可以在...

    pixel_annotation_tool:像素注释工具-源码

    Pixel Annotation_ToolAboutPixel Annotation Tool is a chrome extension to generate Pixel Rule (which used by pixeljs or python module) by annotation in browser.It can both used for ...

    geronimo-annotation_1.0_spec-1.1.1-API文档-中文版.zip

    赠送jar包:geronimo-annotation_1.0_spec-1.1.1.jar; 赠送原API文档:geronimo-annotation_1.0_spec-1.1.1-javadoc.jar; 赠送源代码:geronimo-annotation_1.0_spec-1.1.1-sources.jar; 赠送Maven依赖信息文件:...

    javax.annotation-api-1.2-API文档-中英对照版.zip

    赠送jar包:javax.annotation-api-1.2.jar; 赠送原API文档:javax.annotation-api-1.2-javadoc.jar; 赠送源代码:javax.annotation-api-1.2-sources.jar; 赠送Maven依赖信息文件:javax.annotation-api-1.2.pom;...

    java1.5 annotation注释源代码

    Java 1.5 引入了一种新的元编程机制——注解(Annotation),极大地增强了代码的可读性和可维护性。注解是一种在代码中添加元数据的方式,它允许程序员在源代码上添加一些信息,这些信息可以被编译器或运行时环境...

Global site tag (gtag.js) - Google Analytics