Java 之 Annotation

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. 注解应用实例


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值