首页

关于java自定义元注解Annotations分析&示例

标签:Documented,Inherited,Retention,Target,Retention,Annotation,自定义注解     发布时间:2016-08-03   

一、前言

使用注解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@}