Annotation不算常用的技术,早前用它写了一些玩意儿,过了一年又忘干净了,今天写点东西记下来,以备再忘之需。
java.lang.annotation,接口 Annotation。对于Annotation,是Java5的新特性,JDK5引入了Metedata(元数据)很容易的就能够调用Annotations。Annotations提供一些本来不属于程序的数据,比如:一段代码的作者或者告诉编译器禁止一些特殊的错误。An annotation 对代码的执行没有什么影响。Annotations使用@annotation的形式应用于代码:类(class),属性(attribute),方法(method)等等。一个Annotation出现在上面提到的开始位置,而且一般只有一行,也可以包含有任意的参数。
——————百度百科
Annotation是什么,上面说得很清楚了,下面重点说,它怎么写,和有什么用。
一、Annotation的基本写法
/** * @author caiyu * @date 2014-1-21 */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface DataSchemaConfig { String type() default "get";
String value();
}
1、DataSchemaConfig为Annotation的名称
2、 @Retention 表示该Annotation的保留级别
分别以下三种:
RetentionPolicy.RUNTIME
会记录在CLASS里,同时会在运行时保留该注解,以使其可以被反射读取。
RetentionPolicy.SOURCE
只存在于源码里,会被编译器抛弃
RetentionPolicy.CLASS
会被编译器记录在CLASS文件中,但虚拟机不会在运行时保留它。该选项是默认选项
3、@Target表示该Annotation的影响范围,如下所示:
package java.lang.annotation; /** * A program element type. The constants of this enumerated type * provide a simple classification of the declared elements in a * Java program. * * <p>These constants are used with the {@link Target} meta-annotation type * to specify where it is legal to use an annotation type. * * @author Joshua Bloch * @since 1.5 */ public enum ElementType { /** Class, interface (including annotation type), or enum declaration */ TYPE, /** Field declaration (includes enum constants) */ FIELD, /** Method declaration */ METHOD, /** Parameter declaration */ PARAMETER, /** Constructor declaration */ CONSTRUCTOR, /** Local variable declaration */ LOCAL_VARIABLE, /** Annotation type declaration */ ANNOTATION_TYPE, /** Package declaration */ PACKAGE }
4、内容组织形式
String type() default "get";
这段声明里,String为成员类型,type为成员名称(必须写上括号),default "get"表示缺省指为"get"
5、使用见如下示例代码
/** * @author caiyu * @date 2014-1-22 */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface DataType { public String value() default "map"; } @DataType(value = "bean") public class MapModel { private Map<String, Object> o = new HashMap<String, Object>(); @GET public Object get(String key) { return o.get(key); } @PUT public void put(String key, Object value) { o.put(key, value); } }
DataType 这个Annotation被声明为Runtime以及TYPE,所以它可以被用于注解MapModel这个类。
括号里的value="bean",则是为其value赋值1,同时由于value是个特殊成员,可以写作
@DataType("bean")
如果写作
@DataType
则value会使用默认值"map"
6、数组形式的成员类型的使用
把上面的内容改成下面的格式:
String[] value() default "map";
之前的这种写法@DataType("bean")仍然是合法的
同时可以写作@DataType({"map","bean"})
二、Annotaion的用途
在介绍@Retention的时候,其实已经说明了Annotation的三种类型了,SOURCE和CLASS类型使用很少,如果你不是需要自己写一个Java Compiler或者Editor,基本用不上。
这里重点说说RUNTIME类型。
我们知道,RUNTIME会被保存在CLASS文件中,而且其中记录的信息可以通过反射来获取到,于是可以利用这点实现一些方便的配置(比如Spring和Hibernate就是利用这点)。
来看看一个MapModel类:
@DataType public class MapModel { private Map<String, Object> o = new HashMap<String, Object>(); @get public Object getProperty(String key) { return o.get(key); } @put public void putProperty(String key, Object value) { o.put(key, value); } public String toString() { return o.toString(); } }
可以看到,MapModel标记了三个注解,分别是DataType和get、put
下面的代码,是用来把该Model和org.dom4j.Element相互转换的,注意只能参考,并不完整:
/** * 序列化注解类型 * * @param content * @param schema * @return */ private Element serialMapType(Object content, IDataSchema<?> schema) { DataType type = schema.getType().getAnnotation(DataType.class); if (type != null && type.value() == DataTypeValue.MAP) { Element root = DocumentFactory.getInstance().createElement( schema.getName()); Method getMethod = extraMethodByAnnotation(schema.getType(), get.class); if (getMethod == null) throw new InvalidAnnotationConfigException( "Invalid annotation class: " + schema.getType().getName()); try { for (Entry<String, IDataSchema<?>> field : schema .getFieldEntrySet()) { Object o = getMethod.invoke(content, field.getValue() .getId()); Element e = save(o, field.getValue()); root.add(e); } return root; } catch (IllegalAccessException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { throw new InvalidAnnotationConfigException( "Invalid put method : " + getMethod.getName()); } catch (InvocationTargetException e) { e.printStackTrace(); } } return null; } /** * 反序列化注解类型数据 * * @param persistentTarget * @param schema * @return */ private Object deserialMapType(Element persistentTarget, IDataSchema<?> schema) { Object instance = null; DataType type = schema.getType().getAnnotation(DataType.class); if (type != null && type.value() == DataTypeValue.MAP) { Method putMethod = extraMethodByAnnotation(schema.getType(), put.class); if (putMethod == null) throw new InvalidAnnotationConfigException( "Invalid annotation class: " + schema.getType().getName()); try { instance = schema.getType().newInstance(); for (Entry<String, IDataSchema<?>> field : schema .getFieldEntrySet()) { Element e = persistentTarget.element(field.getValue() .getName()); Object v = load(e, field.getValue()); putMethod.invoke(instance, new Object[] { field.getValue().getId(), v }); } } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { throw new InvalidAnnotationConfigException( "Invalid put method : " + putMethod.getName()); } catch (InvocationTargetException e) { e.printStackTrace(); } } return instance; }
/** * 抽取含有指定注解的方法 * * @param type * @param annotationClass * @return */ private Method extraMethodByAnnotation(Class<?> type, Class<? extends Annotation> annotationClass) { for (Method method : type.getDeclaredMethods()) { Annotation t = method.getAnnotation(annotationClass); if (t != null) { return method; } } return null; }