您的位置:首页 > 编程语言 > Java开发

Java 之 Annotation

2016-05-06 15:59 543 查看
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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: