Java :Annotation(注释)
1 基本 Annotation
三个基本的 Annotation 如下:
- @Override
- @Deprecated
- @SuppressWarnings
JDK 提供的三个基本 Annotation 都定义在 java.lang 包下。
1-1 限定重写父类方法:@Override
public class Fruit
public void info()
{
System.out.println("水果的info方法...");
}
}
class Apple extends Fruit
// 使用@Override指定下面方法必须重写父类方法
@Override
public void info()
{
System.out.println("苹果重写水果的info方法...");
}
}
1-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
1-3 抑制编译器警告:@SuppressWarnings
// 关闭整个类里的编译器警告
@SuppressWarnings(value="unchecked")
public class SuppressWarningsTest
{
public static void main(String[] args)
{
List<String> myList = new
2 自定义 Annotation
2-1 定义 Annotation
// 定义一个简单的 Annotation 类型
public @interface
修饰类
// 使用 @Test 修饰类定义
@Test
public class MyClass
{
...
修饰方法
public class MyClass
{
// 使用 @Test 修饰方法
@Test
public void info()
{
...
}
}
添加成员变量
public @interface MyTag
{
// 定义了两个成员变量的 Annotation
String name();
int
使用
public class Test
{
// 使用带成员变量的 Annotation 时,需要为成员变量赋值
@MyTag(name="xx", age=6)
public void info()
{
...
}
}
public @interface MyTag
{
//使用 default 为两个成员变量指定初始值
String name() default "yeeku";
int age() default 32;
}
使用
public class Test
{
//
@MyTag
public void info()
{
...
}
}
2-2 提取 Annotation 的信息
// 获取 Test 类的 info 方法的所有注释
Annotation [] aArray = Class.forName("Test").getMethod("info").getAnnotations();
// 遍历所有注释
for(Annotation an:aArray)
{
System.out.println(an);
// 获取 tt 对象的 info 方法所包含的所有注释
Annotation [] annotation = tt.getClass().getMethod("info").getAnnotations();
// 遍历每个注释对象
for(Annotation tag:annotation)
{
// 如果 tag 注释是 MyTag1 类型
if(tag instanceof MyTag1)
{
System.out.println("Tag is: " + tag);
// 将 tag 强制类型转换为 MyTag1, 并调用 tag 对象的 method1 和 method2 两个方法
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);
// 将 tag 强制类型转换为 MyTag2, 并调用 tag 对象的 method1 和 method2 两个方法
System.out.println("tag.name(): " + ((MyTag2)tag).method1());
System.out.println("tag.age(): " + ((MyTag2)tag).method2());
2-3 使用 Annotation 的例子
两个例子
1. 没有任何成员变量的 Annotation,,用于标识哪些方法是可测试的。
Testable.java
//使用JDK的元数据Annotation:Retention
@Retention(RetentionPolicy.RUNTIME)
// 使用JDK的元数据Annotation:Target
@Target(ElementType.METHOD)
// 定义一个标记注释,不包含任何成员变量,即不可传入元数据
public @interface Testable
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()
{
}
}
ProcessorTest.java
public class ProcessorTest
{
public static void process(String clazz)
throws ClassNotFoundException
{
int passed = 0;
int failed = 0;
// 遍历clazz对应的类里的所有方法
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.println("方法" + m + "运行失败,异常:"
+ ex.getCause());
failed++;
}
}
}
//统计测试结果
System.out.println("共运行了:" + (passed + failed)
+ "个方法,其中:\n" + "失败了:" + failed + "个,\n"
+ "成功了:" + passed + "个!");
}
}
RunTests.java
public class RunTests
{
public static void main(String[] args)
throws Exception
{
//处理MyTest类
ProcessorTest.process("MyTest");
}
}
运行结果:
- 通过 ActionListenerFor Annotation 来为程序中的按钮绑定事件监听器
ActionListenerFor.java
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ActionListenerFor
//定义一个成员变量,用于设置元数据
//该listener成员变量用于保存监听器实现类
AnnotationTest.java
public class AnnotationTest
private JFrame mainWin = new JFrame("使用注释绑定事件监听器");
// 使用Annotation为ok按钮绑定事件监听器
@ActionListenerFor(listener=OkListener.class)
private JButton ok = new JButton("确定");
// 使用Annotation为cancel按钮绑定事件监听器
@ActionListenerFor(listener=CancelListener.class)
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.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainWin.pack();
mainWin.setVisible(true);
}
public static void main(String[] args)
{
new AnnotationTest().init();
}
}
// 定义ok按钮的事件监听器实现类
class OkListener implements ActionListener
public void actionPerformed(ActionEvent evt)
{
JOptionPane.showMessageDialog(null , "单击了确认按钮");
}
}
// 定义cancel按钮的事件监听器实现类
class CancelListener implements ActionListener
public void actionPerformed(ActionEvent evt)
{
JOptionPane.showMessageDialog(null , "单击了取消按钮");
}
}
ActionListenerInstaller.java
public class ActionListenerInstaller
{
// 处理Annotation的方法,其中obj是包含Annotation的对象
public static void processAnnotations(Object obj)
{
try
{
// 获取obj对象的类
Class cl = obj.getClass();
// 获取指定obj对象的所有Field,并遍历每个Field
for (Field f : cl.getDeclaredFields())
{
// 将指定Field设置成可自由访问。
f.setAccessible(true);
// 获取指定Field上ActionListenerFor类型的Annotation
ActionListenerFor a = f.getAnnotation(ActionListenerFor.class);
// 获取f Field实际对应的对象
Object fObj = f.get(obj);
// 如果f是AbstractButton的实例,且a不为null
if (a != null && fObj != null
&& fObj instanceof AbstractButton)
{
// 获取a注释里的元数据listner(它是一个监听器类)
Class<? extends ActionListener> listenerClazz = a.listener();
// 使用反射来创建listner类的对象
ActionListener al = listenerClazz.newInstance();
AbstractButton ab = (AbstractButton)fObj;
// 为ab按钮添加事件监听器
ab.addActionListener(al);
}
}
}
catch
运行结果:
3 JDK 的元 Annotation
3-1 使用 @Retention
// 定义下面的 Testable Annotation 的保留到运行时
@Retention(value = RetentionPoicy.RUNTIME)
public @interface
或
// 定义下面的 Testable Annotation 将被编译器直接丢弃
@Retention(RetentionPoicy.SOURCE)
public @interface
3-2 使用 @Target
@Target(ElementType.FIELD)
public @interface
3-3 使用 @Documented
Testable.java
// 定义下面的 Testable Annotation 将被编译器直接丢弃
@Retention(RetentionPoicy.RUNTIME)
@Target(ElementType.METHOD)
// 定义 Testable Annotation 将被 javadoc 工具提取
@Documented
public @interface Testable{}
MyTest.java
public class MyTest
{
// 使用 @Test 修饰 info 方法
@Testable
public void info()
{
System.out.println("info 方法...");
}
}
3-4 使用 @Inherited
Inheritable.java
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface Inheritable
TestInheritable.java
// 使用@Inheritable修饰的Base类
@Inheritable
class Base
{
}
// TestInheritable类只是继承了Base类,
// 并未直接使用@Inheritable Annotiation修饰
public class InheritableTest extends Base
public static void main(String[] args)
{
// 打印TestInheritable类是否具有@Inheritable修饰
4 使用 APT 处理 Annotation
Persistent.java
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
@Documented
public @interface Persistent
IdProperty.java
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.SOURCE)
@Documented
public @interface IdProperty
Property.java
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.SOURCE)
@Documented
public @interface Property
Person.java
@Persistent(table="person_inf")
public class Person
@IdProperty(column="person_id",type="integer",generator="identity")
private int id;
@Property(column="person_name",type="string")
private String name;
@Property(column="person_age",type="integer")
private int age;
//无参数的构造器
public Person()
{
}
//初始化全部成员变量的构造器
public Person(int id , String name , int age)
{
this.id = id;
this.name = name;
this.age = age;
}
// 下面省略各Field的setter和getter方法
为上面三个 Annotation 提供一个 Annotation 处理器,该处理器的功能是根据注释来生成一个 Hibernate 映射文件。
HibernateAnnotationProcessor.java
@SupportedSourceVersion(SourceVersion.RELEASE_7)
// 指定可处理@Persistent、@Id、@Property三个Annotation
@SupportedAnnotationTypes({"Persistent" , "Id" , "Property"})
public class HibernateAnnotationProcessor
extends AbstractProcessor
// 循环处理每个需要处理的程序对象
public boolean process(Set<? extends TypeElement> annotations
, RoundEnvironment roundEnv)
{
// 定义一个文件输出流,用于生成额外的文件
PrintStream ps = null;
try
{
// 遍历每个被@Persistent修饰的class文件
for (Element t : roundEnv.getElementsAnnotatedWith(Persistent.class))
{
// 获取正在处理的类名
Name clazzName = t.getSimpleName();
// 获取类定义前的@Persistent Annotation
Persistent per = t.getAnnotation(Persistent.class);
// 创建文件输出流
ps = new PrintStream(new FileOutputStream(clazzName
+ ".hbm.xml"));
// 执行输出
ps.println("<?xml version=\"1.0\"?>");
ps.println("<!DOCTYPE hibernate-mapping PUBLIC");
ps.println(" \"-//Hibernate/Hibernate "
+ "Mapping DTD 3.0//EN\"");
ps.println(" \"http://www.hibernate.org/dtd/"
+ "hibernate-mapping-3.0.dtd\">");
ps.println("<hibernate-mapping>");
ps.print(" <class name=\"" + t);
// 输出per的table()的值
ps.println("\" table=\"" + per.table() + "\">");
for (Element f : t.getEnclosedElements())
{
// 只处理Field上的Annotation
if (f.getKind() == ElementKind.FIELD) //①
{
// 获取Field定义前的@Id Annotation
Id id = f.getAnnotation(Id.class); //②
// 当@Id Annotation存在时输出<id.../>元素
if(id != null)
{
ps.println(" <id name=\""
+ f.getSimpleName()
+ "\" column=\"" + id.column()
+ "\" type=\"" + id.type()
+ "\">");
ps.println(" <generator class=\""
+ id.generator() + "\"/>");
ps.println(" </id>");
}
// 获取Field定义前的@Property Annotation
Property p = f.getAnnotation(Property.class); //③
// 当@Property Annotation存在时输出<property.../>元素
if (p != null)
{
ps.println(" <property name=\""
+ f.getSimpleName()
+ "\" column=\"" + p.column()
+ "\" type=\"" + p.type()
+ "\"/>");
}
}
}
ps.println(" </class>");
ps.println("</hibernate-mapping>");
}
}
catch (Exception ex)
{
ex.printStackTrace();
}
finally
{
if (ps != null)
{
try
{
ps.close();
}
catch (Exception ex)
{
ex.printStackTrace();
}
}
}
return true;
}
}
HibernateAnnotationFactory.java
public class HibernateAnnotaionFactory implements AnnotationProcessorFactory
// 所有支持的注释类型
public Collection<String> supportedAnnotationTypes()
{
return Arrays.asList("Property", "IdProperty", "Persistent");
}
// 返回所有支持的选项
public Collection<String> supportedOption()
{
return Arrays.asList(new String[0]);
}
// 返回 Annotation 处理器
public AnnotationProcessor getProcessorFor(Set<AnnotationTypeDeclaration> atds, AnnotationProcessorEnvironment env)
{
return new
提供上述工厂后,就可以使用 APT 工具来处理上面的 源文件,并据此生成一份 xml 文件。
APT 工具位于 JDK 安装路径的 bin 目录下。
rem HibernateAnnotationFactory Person.java Annotation
apt -factory HibernateAnnotationFactory Person.java
生成的 Person.hbm.xml 文件
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="Person" table="person_inf">
<id name="id" column="person_id" type="integer">
<generator class="identity"/>
</id>
<property name="name" column="person_name" type="string"/>
<property name="age" column="person_age" type="integer"/>
</class>
</hibernate-mapping>