一、前言
使用注解Annotations可以使代码开发变得更加的灵活多变,从另外一个维度降低了代码的复杂性和耦合度。现在新版本的JDK或主流Spring等框架扩展都提高了注解重要性,主要在java.lang.annotation包中实现,用来将任何的信息或元数据(metadata)与程序元素(类、方法、成员变量等)进行关联。
二、知识点&源码
注解主要作用:标记(用于告诉编译器一些信息)、编译时动态处理(动态生成代码)、运行时动态处理(得到注解信息)。分为两类:标准注解、元注解。
1. 标准注解
标准Annotation是指Java自带的几个Annotation,主要有@Override、@Deprecated、@SuppressWarnings
@Override
@Target(ElementType.METHOD)@b@@Retention(RetentionPolicy.SOURCE)@b@public @interface Override {@b@}
@Deprecated
@Documented@b@@Retention(RetentionPolicy.RUNTIME)@b@public @interface Deprecated {@b@}
@SuppressWarnings
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})@b@@Retention(RetentionPolicy.SOURCE)@b@public @interface SuppressWarnings {@b@ String[] value();@b@}
2. 元注解
元注解主要包含4个@Documented、@Inherited、@Retention、@Target,
@Documented - 所修饰的Annotation连同自定义Annotation所修饰的元素一同保存到Javadoc文档中
@Documented@b@@Retention(RetentionPolicy.RUNTIME)@b@@Target(ElementType.ANNOTATION_TYPE)@b@public @interface Documented {@b@}
@Inherited - 是一个标记注解,可被继承关系的映射到(子类获取父类的注解Annotation信息)
@Documented@b@@Retention(RetentionPolicy.RUNTIME)@b@@Target(ElementType.ANNOTATION_TYPE)@b@public @interface Inherited {@b@}
@Target - 修饰的对象范围,如 packages、types(类、接口、枚举、Annotation类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量
>ElementType.CONSTRUCTOR 作用于构造器
>ElementType.FIELD 作用于域/属性
>ElementType.LOCAL_VARIABLE 用于描述局部变量
>ElementType.METHOD 作用于方法
>ElementType.PACKAGE 用于描述包
>ElementType.PARAMETER 用于描述参数
>ElementType.TYPE 用于描述类、接口(包括注解类型) 或enum声明,最常用
>用法如下
@Target(ElementType.TYPE) @b@@Target({ ElementType.TYPE, ElementType.METHOD})
>源码
@Documented@b@@Retention(RetentionPolicy.RUNTIME)@b@@Target(ElementType.ANNOTATION_TYPE)@b@public @interface Target {@b@ ElementType[] value();@b@}@b@public enum ElementType {@b@ /** Class, interface (including annotation type), or enum declaration */@b@ TYPE,@b@ /** Field declaration (includes enum constants) */@b@ FIELD,@b@ /** Method declaration */@b@ METHOD,@b@ /** Parameter declaration */@b@ PARAMETER,@b@ /** Constructor declaration */@b@ CONSTRUCTOR,@b@ /** Local variable declaration */@b@ LOCAL_VARIABLE,@b@ /** Annotation type declaration */@b@ ANNOTATION_TYPE,@b@ /** Package declaration */@b@ PACKAGE@b@}
@Retention - 描述注解的生命周期(即:被描述的注解在什么范围内有效)
RetentionPolicy.RUNTIME //注解会在class字节码文件中存在,在运行时可以通过反射获取到@b@RetentionPolicy.CLASS //默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得@b@RetentionPolicy.SOURCE //注解仅存在于源码中,在class字节码文件中不包含
>源码
@Documented@b@@Retention(RetentionPolicy.RUNTIME)@b@@Target(ElementType.ANNOTATION_TYPE)@b@public @interface Retention {@b@ RetentionPolicy value();@b@}@b@public enum RetentionPolicy {@b@ /* Annotations are to be discarded by the compiler. */@b@ SOURCE,@b@ /* Annotations are to be recorded in the class file by the compiler@b@ * but need not be retained by the VM at run time. This is the default@b@ * behavior. */@b@ CLASS,@b@ /* Annotations are to be recorded in the class file by the compiler and@b@ * retained by the VM at run time, so they may be read reflectively.@b@ * @see java.lang.reflect.AnnotatedElement */@b@ RUNTIME@b@}
三、自定义注解语法
1、格式定义
public @interface 注解名 {定义体}
import java.lang.annotation.Documented;@b@import java.lang.annotation.ElementType;@b@import java.lang.annotation.Inherited;@b@import java.lang.annotation.Retention;@b@import java.lang.annotation.RetentionPolicy;@b@import java.lang.annotation.Target;@b@@b@@Documented@b@@Retention(RetentionPolicy.RUNTIME)@b@@Target(ElementType.METHOD)@b@@Inherited@b@public @interface MyMethod{@b@ String author() default "junni@xwood.net";@b@ String date();@b@ int version() default 1;@b@}
>说明
>>通过@interface 定义,注解名即为自定义注解名
>> 注解配置参数名为注解类的方法名
a) 所有方法没有方法体,方法名即为属性名,没有参数没有修饰符,实际只允许 public & abstract 修饰符,默认为 public ,不允许抛异常
b) 方法返回值只能是基本类型,String, Class, annotation, enumeration 或者是他们的一维数组,返回类型即为属性类型
c) 若只有一个默认属性,可直接用 value() 函数。一个属性都没有表示该 Annotation 为 Mark Annotation
d) 可以加 default 表示默认值,null不能作为成员默认值
2、调用
public class MethodInvoker {@b@ @MyMethod( author ="xwood", date="2016/08/03", version=1)@b@ public String getName() {@b@ return "xwood.net";@b@ }@b@}
3. 测试类
import java.lang.reflect.Method;@b@public class Test { @b@ public static void main(String[] args) throws NoSuchMethodException,SecurityException{@b@ Class<MethodInvoker> clazz=MethodInvoker.class;@b@ Method method = clazz.getMethod("getName", new Class[]{});@b@ MyMethod myAnnotation = method.getAnnotation(MyMethod.class); @b@ System.out.println("myAnnotation:"+myAnnotation.author()+";"+myAnnotation.date()+";"+myAnnotation.version());@b@ }@b@}
结果输出
myAnnotation:xwood;2016/08/03;1
四、注释工具类
基于Spring的org.springframework.core.annotation.AnnotationUtils
/*@b@ * Copyright 2002-2012 the original author or authors.@b@ *@b@ * Licensed under the Apache License, Version 2.0 (the "License");@b@ * you may not use this file except in compliance with the License.@b@ * You may obtain a copy of the License at@b@ *@b@ * http://www.apache.org/licenses/LICENSE-2.0@b@ *@b@ * Unless required by applicable law or agreed to in writing, software@b@ * distributed under the License is distributed on an "AS IS" BASIS,@b@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.@b@ * See the License for the specific language governing permissions and@b@ * limitations under the License.@b@ */@b@@b@package org.springframework.core.annotation;@b@@b@import java.lang.annotation.Annotation;@b@import java.lang.reflect.AnnotatedElement;@b@import java.lang.reflect.Method;@b@import java.util.Arrays;@b@import java.util.Map;@b@import java.util.WeakHashMap;@b@@b@import org.springframework.core.BridgeMethodResolver;@b@import org.springframework.util.Assert;@b@@b@@b@public abstract class AnnotationUtils {@b@@b@ @b@ static final String VALUE = "value";@b@@b@ private static final Map<Class<?>, Boolean> annotatedInterfaceCache = new WeakHashMap<Class<?>, Boolean>();@b@ @b@ public static <T extends Annotation> T getAnnotation(AnnotatedElement ae, Class<T> annotationType) {@b@ T ann = ae.getAnnotation(annotationType);@b@ if (ann == null) {@b@ for (Annotation metaAnn : ae.getAnnotations()) {@b@ ann = metaAnn.annotationType().getAnnotation(annotationType);@b@ if (ann != null) {@b@ break;@b@ }@b@ }@b@ }@b@ return ann;@b@ }@b@@b@ @b@ public static Annotation[] getAnnotations(Method method) {@b@ return BridgeMethodResolver.findBridgedMethod(method).getAnnotations();@b@ }@b@@b@ @b@ public static <A extends Annotation> A getAnnotation(Method method, Class<A> annotationType) {@b@ Method resolvedMethod = BridgeMethodResolver.findBridgedMethod(method);@b@ A ann = resolvedMethod.getAnnotation(annotationType);@b@ if (ann == null) {@b@ for (Annotation metaAnn : resolvedMethod.getAnnotations()) {@b@ ann = metaAnn.annotationType().getAnnotation(annotationType);@b@ if (ann != null) {@b@ break;@b@ }@b@ }@b@ }@b@ return ann;@b@ }@b@@b@ @b@ public static <A extends Annotation> A findAnnotation(Method method, Class<A> annotationType) {@b@ A annotation = getAnnotation(method, annotationType);@b@ Class<?> cl = method.getDeclaringClass();@b@ if (annotation == null) {@b@ annotation = searchOnInterfaces(method, annotationType, cl.getInterfaces());@b@ }@b@ while (annotation == null) {@b@ cl = cl.getSuperclass();@b@ if (cl == null || cl == Object.class) {@b@ break;@b@ }@b@ try {@b@ Method equivalentMethod = cl.getDeclaredMethod(method.getName(), method.getParameterTypes());@b@ annotation = getAnnotation(equivalentMethod, annotationType);@b@ }@b@ catch (NoSuchMethodException ex) {@b@ // No equivalent method found@b@ }@b@ if (annotation == null) {@b@ annotation = searchOnInterfaces(method, annotationType, cl.getInterfaces());@b@ }@b@ }@b@ return annotation;@b@ }@b@@b@ private static <A extends Annotation> A searchOnInterfaces(Method method, Class<A> annotationType, Class<?>[] ifcs) {@b@ A annotation = null;@b@ for (Class<?> iface : ifcs) {@b@ if (isInterfaceWithAnnotatedMethods(iface)) {@b@ try {@b@ Method equivalentMethod = iface.getMethod(method.getName(), method.getParameterTypes());@b@ annotation = getAnnotation(equivalentMethod, annotationType);@b@ }@b@ catch (NoSuchMethodException ex) {@b@ // Skip this interface - it doesn't have the method...@b@ }@b@ if (annotation != null) {@b@ break;@b@ }@b@ }@b@ }@b@ return annotation;@b@ }@b@@b@ private static boolean isInterfaceWithAnnotatedMethods(Class<?> iface) {@b@ synchronized (annotatedInterfaceCache) {@b@ Boolean flag = annotatedInterfaceCache.get(iface);@b@ if (flag != null) {@b@ return flag;@b@ }@b@ boolean found = false;@b@ for (Method ifcMethod : iface.getMethods()) {@b@ if (ifcMethod.getAnnotations().length > 0) {@b@ found = true;@b@ break;@b@ }@b@ }@b@ annotatedInterfaceCache.put(iface, found);@b@ return found;@b@ }@b@ }@b@@b@ @b@ public static <A extends Annotation> A findAnnotation(Class<?> clazz, Class<A> annotationType) {@b@ Assert.notNull(clazz, "Class must not be null");@b@ A annotation = clazz.getAnnotation(annotationType);@b@ if (annotation != null) {@b@ return annotation;@b@ }@b@ for (Class<?> ifc : clazz.getInterfaces()) {@b@ annotation = findAnnotation(ifc, annotationType);@b@ if (annotation != null) {@b@ return annotation;@b@ }@b@ }@b@ if (!Annotation.class.isAssignableFrom(clazz)) {@b@ for (Annotation ann : clazz.getAnnotations()) {@b@ annotation = findAnnotation(ann.annotationType(), annotationType);@b@ if (annotation != null) {@b@ return annotation;@b@ }@b@ }@b@ }@b@ Class<?> superClass = clazz.getSuperclass();@b@ if (superClass == null || superClass == Object.class) {@b@ return null;@b@ }@b@ return findAnnotation(superClass, annotationType);@b@ }@b@@b@ @b@ public static Class<?> findAnnotationDeclaringClass(Class<? extends Annotation> annotationType, Class<?> clazz) {@b@ Assert.notNull(annotationType, "Annotation type must not be null");@b@ if (clazz == null || clazz.equals(Object.class)) {@b@ return null;@b@ }@b@ return (isAnnotationDeclaredLocally(annotationType, clazz)) ? clazz :@b@ findAnnotationDeclaringClass(annotationType, clazz.getSuperclass());@b@ }@b@@b@ @b@ public static boolean isAnnotationDeclaredLocally(Class<? extends Annotation> annotationType, Class<?> clazz) {@b@ Assert.notNull(annotationType, "Annotation type must not be null");@b@ Assert.notNull(clazz, "Class must not be null");@b@ boolean declaredLocally = false;@b@ for (Annotation annotation : Arrays.asList(clazz.getDeclaredAnnotations())) {@b@ if (annotation.annotationType().equals(annotationType)) {@b@ declaredLocally = true;@b@ break;@b@ }@b@ }@b@ return declaredLocally;@b@ }@b@@b@ @b@ public static boolean isAnnotationInherited(Class<? extends Annotation> annotationType, Class<?> clazz) {@b@ Assert.notNull(annotationType, "Annotation type must not be null");@b@ Assert.notNull(clazz, "Class must not be null");@b@ return (clazz.isAnnotationPresent(annotationType) && !isAnnotationDeclaredLocally(annotationType, clazz));@b@ }@b@@b@ @b@ public static Map<String, Object> getAnnotationAttributes(Annotation annotation) {@b@ return getAnnotationAttributes(annotation, false, false);@b@ }@b@@b@ @b@ public static Map<String, Object> getAnnotationAttributes(Annotation annotation, boolean classValuesAsString) {@b@ return getAnnotationAttributes(annotation, classValuesAsString, false);@b@ }@b@@b@ @b@ public static AnnotationAttributes getAnnotationAttributes(@b@ Annotation annotation, boolean classValuesAsString, boolean nestedAnnotationsAsMap) {@b@@b@ AnnotationAttributes attrs = new AnnotationAttributes();@b@ Method[] methods = annotation.annotationType().getDeclaredMethods();@b@ for (Method method : methods) {@b@ if (method.getParameterTypes().length == 0 && method.getReturnType() != void.class) {@b@ try {@b@ Object value = method.invoke(annotation);@b@ if (classValuesAsString) {@b@ if (value instanceof Class) {@b@ value = ((Class<?>) value).getName();@b@ }@b@ else if (value instanceof Class[]) {@b@ Class<?>[] clazzArray = (Class[]) value;@b@ String[] newValue = new String[clazzArray.length];@b@ for (int i = 0; i < clazzArray.length; i++) {@b@ newValue[i] = clazzArray[i].getName();@b@ }@b@ value = newValue;@b@ }@b@ }@b@ if (nestedAnnotationsAsMap && value instanceof Annotation) {@b@ attrs.put(method.getName(), getAnnotationAttributes(@b@ (Annotation)value, classValuesAsString, nestedAnnotationsAsMap));@b@ }@b@ else if (nestedAnnotationsAsMap && value instanceof Annotation[]) {@b@ Annotation[] realAnnotations = (Annotation[])value;@b@ AnnotationAttributes[] mappedAnnotations = new AnnotationAttributes[realAnnotations.length];@b@ for (int i = 0; i < realAnnotations.length; i++) {@b@ mappedAnnotations[i] = getAnnotationAttributes(@b@ realAnnotations[i], classValuesAsString, nestedAnnotationsAsMap);@b@ }@b@ attrs.put(method.getName(), mappedAnnotations);@b@ }@b@ else {@b@ attrs.put(method.getName(), value);@b@ }@b@ }@b@ catch (Exception ex) {@b@ throw new IllegalStateException("Could not obtain annotation attribute values", ex);@b@ }@b@ }@b@ }@b@ return attrs;@b@ }@b@@b@ @b@ public static Object getValue(Annotation annotation) {@b@ return getValue(annotation, VALUE);@b@ }@b@@b@ @b@ public static Object getValue(Annotation annotation, String attributeName) {@b@ try {@b@ Method method = annotation.annotationType().getDeclaredMethod(attributeName, new Class[0]);@b@ return method.invoke(annotation);@b@ }@b@ catch (Exception ex) {@b@ return null;@b@ }@b@ }@b@@b@ @b@ public static Object getDefaultValue(Annotation annotation) {@b@ return getDefaultValue(annotation, VALUE);@b@ }@b@@b@ @b@ public static Object getDefaultValue(Annotation annotation, String attributeName) {@b@ return getDefaultValue(annotation.annotationType(), attributeName);@b@ }@b@@b@ @b@ public static Object getDefaultValue(Class<? extends Annotation> annotationType) {@b@ return getDefaultValue(annotationType, VALUE);@b@ }@b@@b@ @b@ public static Object getDefaultValue(Class<? extends Annotation> annotationType, String attributeName) {@b@ try {@b@ Method method = annotationType.getDeclaredMethod(attributeName, new Class[0]);@b@ return method.getDefaultValue();@b@ }@b@ catch (Exception ex) {@b@ return null;@b@ }@b@ }@b@@b@}