Java 之 Annotation

来源:互联网 发布:网络杀手是什么意思啊 编辑:程序博客网 时间:2024/06/13 20:44

Annotation 是 Java 5.0 开始引入的新特征。注解是附加在代码中的一些元信息,用于一些工具在编译、运行时进行解析和使用,起到说明、配置的功能。注解不会影响代码的实际逻辑,仅仅起到辅助性的作用。包含在 java.lang.annotation 包中。


1、元注解

元注解是指注解的注解,包括  @Retention @Target @Document @Inherited 四种。


1.1、@Retention

定义注解的保留策略,指示注释类型的注释要保留多久。如果注释类型声明中不存在 Retention 注释,则保留策略默认为 RetentionPolicy.CLASS。

@Retention(RetentionPolicy.SOURCE)
@Retention(RetentionPolicy.CLASS)
@Retention(RetentionPolicy.RUNTIME)

public enum RetentionPolicy {    /**     * Annotations are to be discarded by the compiler.     */    SOURCE,    /**     * Annotations are to be recorded in the class file by the compiler     * but need not be retained by the VM at run time.  This is the default     * behavior.     */    CLASS,    /**     * Annotations are to be recorded in the class file by the compiler and     * retained by the VM at run time, so they may be read reflectively.     *     * @see java.lang.reflect.AnnotatedElement     */    RUNTIME}


1.2、@Target

定义注解的作用目标,指示注释类型所适用的程序元素的种类。如果注释类型声明中不存在Target元注释,则声明的类型可以用在任一程序元素上。如果存在这样的元注释,则编译器强制实施指定的使用限制。

@Target(ElementType.TYPE)
@Target(ElementType.FIELD)
@Target(ElementType.METHOD)
@Target(ElementType.PARAMETER)
@Target(ElementType.CONSTRUCTOR)
@Target(ElementType.LOCAL_VARIABLE)
@Target(ElementType.ANNOTATION_TYPE)
@Target(ElementType.PACKAGE)

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}


1.3、@Document

说明该注解将被包含在JavaDoc中


1.4、@Inherited

说明子类可以继承父类中的该注解,默认情况下注解并不会被继承到子类中,可以在自定义注解时加上@Inherited注解声明使用继承。


2、自定义注解

2.1. 定义注解

2.1.1 @Handler
import java.lang.annotation.*;@Inherited@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)public @interface Handler{    String[] value() default {};}
2.1.2 @SetHandler
import java.lang.annotation.*;@Inherited@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public @interface SetHandler{}
2.1.3 @GetHandler
import java.lang.annotation.*;@Inherited@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public @interface GetHandler{}

2.2. 使用注解

import java.util.*;@Handler("HandlerMethod")public class HandlerTest{    public int setHandler(String method, HashMap<String, String> properties) {        return 0;    }    @SetHandler    public int setHandler(String method, Map<String, String> properties) {        return 0;    }    @GetHandler    public String getHandler(String method, Map<String, String> properties) {        return null;    }}

2.3. 处理注解

import java.lang.reflect.*;import java.util.*;public class Main{    public static void main(String[] args) throws Exception {        Map<String, String> prop = new HashMap<String, String>();        prop.put("0", "Property0");        prop.put("1", "Property0");        Class clazz = loadClass("HandlerTest");        doHandler(clazz, "Good luck !!!", prop);    }    // load class by name and find annotation    private static Class loadClass(String name) throws ClassNotFoundException {        Class clazz = Class.forName(name);        if (clazz.isAnnotationPresent(Handler.class)) {            Handler handler = (Handler) clazz.getAnnotation(Handler.class);            System.out.println("!!! " + clazz + " for " + Arrays.toString(handler.value()));        }        return clazz;    }        // find annotated method and perform    private static void doHandler(Class clazz, String arg, Map<String, String> prop)            throws IllegalAccessException, InstantiationException {        Object object = clazz.newInstance();        for (Method method : clazz.getMethods()) {            if (method.isAnnotationPresent(SetHandler.class)) {                invoke(object, method, arg, prop);            } else if (method.isAnnotationPresent(GetHandler.class)) {                invoke(object, method, arg, prop);            }        }    }    // invoke method    private static Object invoke(Object object, Method method, String arg, Map<String, String> properties) {        Class<?>[] type = method.getParameterTypes();        Map<String, String> prop = null;        Class clazz0 = type[1];        Object retval = null;        if (properties != null) {            Class clazz1 = properties.getClass();            if (clazz0 == clazz1) {                System.out.println("!!! " + clazz0 + " == " + clazz1);            } else if (clazz1.isAssignableFrom(clazz0)) {                System.out.println("!!! " + clazz0 + " <= " + clazz1);            } else if (clazz0.isAssignableFrom(clazz1)) {                System.out.println("!!! " + clazz0 + " => " + clazz1);            } else {                System.out.println("!!! " + clazz0 + " != " + clazz1);            }            try {                if (clazz0.isAssignableFrom(clazz1)) {                    prop = (Map<String, String>) clazz1.newInstance();                } else {                    prop = (Map<String, String>) clazz0.newInstance();                }            } catch (Exception e) {                e.printStackTrace();            }            if (prop != null) {                for (Map.Entry<String, String> entry : properties.entrySet()) {                    prop.put(entry.getKey(), entry.getValue());                }            }        }        try {            retval = method.invoke(object, new Object[]{arg, prop});        } catch (Exception e) {            e.printStackTrace();        }        return retval;    }}

3. 注解处理器

如上面的例子中 @SetHandler 和 @GetHandler 被用来注解方法,但是怎么样来保证被注解的方法有一个统一的声明原型呢。这里可以让Java的编译器来帮忙在编译阶段检查被注解的方法的原型,需要客制化一个注解处理器的插件来让Java编译器处理。为Java的编译器写插件就需要将注解处理器继承自 javax.annotation.processing.AbstractProcessor 且实现一些必要的方法来让其协同工作。
怎么样让编译器找到和使用插件呢?这里必须提供一个jar文件,将注解处理器打包在此jar中,并且还需要打包一个特定的文件 javax.annotation.processing.Processor 到 META-INF/services 路径下,其内容是注解处理器的合法的全名列表。


import javax.annotation.processing.*;import javax.lang.model.SourceVersion;import javax.lang.model.element.*;import javax.lang.model.type.*;import java.util.*;public class MyProcessor extends AbstractProcessor{    private static final String OBJ_TYPE = "java.lang.Object";    private static final String STR_TYPE = "java.lang.String";    private static final String MAP_TYPE = "java.util.Map<?,?>";    @Override    public synchronized void init(ProcessingEnvironment processingEnv) {        super.init(processingEnv);    }    @Override    public Set<String> getSupportedAnnotationTypes() {        Set<String> types = new LinkedHashSet<String>();        types.add(SetHandler.class.getCanonicalName());        types.add(GetHandler.class.getCanonicalName());        return types;    }    @Override    public SourceVersion getSupportedSourceVersion() {        return SourceVersion.latestSupported();    }    @Override    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {        for (TypeElement annotation : annotations) {            for (Element element : roundEnv.getElementsAnnotatedWith(annotation)) {                TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();                if (!(element instanceof ExecutableElement) || element.getKind() != ElementKind.METHOD) {                    error(element, "@%s.%s annotation must be on a method.",                        enclosingElement.getQualifiedName(), element.toString());                    return false;                }                Set<Modifier> modifiers = element.getModifiers();                if (modifiers.contains(Modifier.PRIVATE)                    || modifiers.contains(Modifier.STATIC)                    || modifiers.contains(Modifier.PROTECTED)) {                    error(element, "@%s.%s method must not be protected, private or static.",                        enclosingElement.getQualifiedName(), element.toString());                    return false;                }                ExecutableElement executableElement = (ExecutableElement) element;                List<? extends VariableElement> parameters = executableElement.getParameters();                if (parameters.size() != 2) {                    error(element, "@%s.%s methods must have 2 parameters.",                        enclosingElement.getQualifiedName(), element.toString());                    return false;                }                VariableElement parameter0 = parameters.get(0);                TypeMirror typeMirror0 = parameter0.asType();                if (!isSubtypeOfType(typeMirror0, STR_TYPE)) {                    error(element, "The 1st parameter of methods @%s.%s must be 'String'.",                        enclosingElement.getQualifiedName(), element.toString());                    return false;                }                VariableElement parameter1 = parameters.get(1);                TypeMirror typeMirror1 = parameter1.asType();                if (!isSubtypeOfType(typeMirror1, MAP_TYPE, STR_TYPE, STR_TYPE)) {                    error(element, "The 2nd parameter of methods @%s.%s should assignable from 'Map<String, String>'.",                        enclosingElement.getQualifiedName(), element.toString());                    return false;                }            }        }        for (Element element : roundEnv.getElementsAnnotatedWith(SetHandler.class)) {            TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();            ExecutableElement executableElement = (ExecutableElement) element;            TypeMirror returnType = executableElement.getReturnType();            if (!isSubtypeOfType(returnType, "int")) {                error(element, "@%s.%s methods must have a 'int' return type.",                    enclosingElement.getQualifiedName(), element.toString());                return false;            }        }        for (Element element : roundEnv.getElementsAnnotatedWith(GetHandler.class)) {            TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();            ExecutableElement executableElement = (ExecutableElement) element;            TypeMirror returnType = executableElement.getReturnType();            if (!isSubtypeOfType(returnType, OBJ_TYPE)) {                error(element, "@%s.%s methods should have a 'Object' return type.",                    enclosingElement.getQualifiedName(), element.toString());                return false;            }        }        return true;    }    private boolean isSubtypeOfType(TypeMirror typeMirror, String otherType, String... genericTypes) {        if (otherType.equals(typeMirror.toString())) {            return true;        }        if (typeMirror.getKind() != TypeKind.DECLARED) {            return false;        }        DeclaredType declaredType = (DeclaredType) typeMirror;        List<? extends TypeMirror> typeArguments = declaredType.getTypeArguments();        if (typeArguments.size() > 0) {            StringBuilder typeString = new StringBuilder(declaredType.asElement().toString());            typeString.append('<');            for (int i = 0; i < typeArguments.size(); i++) {                if (i > 0) {                    typeString.append(',');                }                typeString.append('?');                if (genericTypes != null && genericTypes.length > i) {                    if (!isSubtypeOfType(typeArguments.get(i), genericTypes[i])) {                        return false;                    }                }            }            typeString.append('>');            if (typeString.toString().equals(otherType)) {                return true;            }        }        Element element = declaredType.asElement();        if (!(element instanceof TypeElement)) {            return false;        }        TypeElement typeElement = (TypeElement) element;        TypeMirror superType = typeElement.getSuperclass();        if (isSubtypeOfType(superType, otherType)) {            return true;        }        for (TypeMirror interfaceType : typeElement.getInterfaces()) {            if (isSubtypeOfType(interfaceType, otherType)) {                return true;            }        }        return false;    }    private void error(Element element, String message, Object... args) {        if (args.length > 0) {            message = String.format(message, args);        }        processingEnv.getMessager().printMessage(javax.tools.Diagnostic.Kind.ERROR, message, element);    }}

4. 注解应用实例

  • butterknife


0 0
原创粉丝点击