【问题标题】:How to use custom type annotations in Java如何在 Java 中使用自定义类型注解
【发布时间】:2014-12-22 16:40:50
【问题描述】:

Java 8 具有称为类型注释 (JSR 308) 的功能。我想将它用于简单的对象到对象映射器框架。我想像这样定义注释@ExpectedType

@Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ExpectedType {
    public Class<?> value();
}

然后像这样在我的代码中使用它:

public class SomeServiceImpl() {
    public @ExpectedType(ObjectA_DTO.class) IObjectA doSomething(@ExpectedType(ObjectA_Entity.class) IObjectA obj) {
        return (ObjectA_Entity) obj; // it's correct
    }
}

IObjectA 是类ObjectA_DTOObjectA_Entity 实现的接口。我想以这种方式使用的服务:

// it's correct
assert someService.doSomething(new ObjectA_DTO()).getClass() == ObjectA_DTO.class;

我想更改 SomeServiceImpl 方法的调用以使用对象映射器。它可以通过使用JSR 269 或AOP 生成代码来实现。

问题是我写了简单的注解处理器,它根本不处理类型注解。简单注释处理器的源码如下所示:

@SupportedAnnotationTypes("*")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class SimpleAnnotationsProcessor extends AbstractProcessor {

    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        Messager messager = processingEnv.getMessager();
        try {
            for (TypeElement e : annotations) {
                messager.printMessage(Diagnostic.Kind.NOTE, e.toString());
                for (Element elem : roundEnv.getElementsAnnotatedWith(e)) {
                    messager.printMessage(Diagnostic.Kind.NOTE, elem.toString());
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return true;
    }
}

任何想法如何使用或如何通过 SimpleAnnotationsProcessor 访问类型注释?对我来说不需要使用 Pluggable Annotation Processing API,我认为它比 Java 反射具有更好的性能。反正我也不知道如何通过 Java Reflection 访问类型注解。

【问题讨论】:

  • 我想我不清楚你想要达到什么目标。如果这是您所期望的,为什么不直接创建方法签名ObjectA_DTO doSomething(ObjectA_Entity)
  • 方法签名不能这样,因为我想使用这样的服务实例someService.doSomething(new ObjectA_DTO())。我想实现对象映射器以将ObjectA_DTO 映射到ObjectA_Entity 并且类型注释@ExpectedType 定义目标类型。这就是为什么签名必须是IObjectA doSomething(IObjectA)
  • 我只是顺便抓住了你的问题......我做了一个关于产品线开发的学期项目,我们在其中处理注释。现在,我将把这个重要的教程留给你,它确实让我继续研究这个主题(你可以跳过关于代码生成的第 3 部分)-deors.wordpress.com/2011/09/26/annotation-types 稍后,我将重新检查这篇文章的进度。
  • 感谢您的链接,我说的是注释为 @Target(ElementType.TYPE_PARAMETER)@Target(ElementType.TYPE_USE) 的注释。它们不会传递给注释处理器的 process 方法。

标签: java java-8 annotation-processing type-annotation


【解决方案1】:

我不确定我是否理解您尝试实现的目标,但这里有一个示例,您可以使用 Java 反射 api 访问您的注释:

package test;

import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.AnnotatedType;
import java.lang.reflect.Method;

public class TypeParameterTest {

    @Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface ExpectedType {
        public Class<?> value();
    }

    public static interface IObjectA {}

    public static class ObjectA_DTO implements IObjectA {}

    public static class ObjectA_Entity implements IObjectA {}

    public static class SomeServiceImpl {
        public @ExpectedType(ObjectA_DTO.class) IObjectA doSomething(@ExpectedType(ObjectA_Entity.class) IObjectA obj) {
            return (ObjectA_Entity) obj;
        }
    }

    public static void main(String[] args) throws NoSuchMethodException, SecurityException {
        Method m = SomeServiceImpl.class.getMethod("doSomething", IObjectA.class);
        AnnotatedType returnType = m.getAnnotatedReturnType();
        Annotation returnTypeAnnotation = returnType.getAnnotation(ExpectedType.class);
        System.out.println(returnTypeAnnotation);

        AnnotatedType[] parameters = m.getAnnotatedParameterTypes();
        for (AnnotatedType p : parameters) {
            Annotation parameterAnnotation = p.getAnnotation(ExpectedType.class);
            System.out.println(parameterAnnotation);
        }
    }
}

输出如下:

@test.TypeParameterTest$ExpectedType(value=class test.TypeParameterTest$ObjectA_DTO)
@test.TypeParameterTest$ExpectedType(value=class test.TypeParameterTest$ObjectA_Entity)

请注意,并非所有可能的类型注释都可以通过反射 api 访问,但如果需要,您始终可以从字节码中读取它们(请参阅我的回答 here)。

【讨论】:

  • 我想使用注释处理器来做,因为它在运行时可能比反射更快,无论如何我接受你的回答。 Checker 框架可以从注解处理器(而不是字节码)读取类型注解。你知道它是怎么做的吗?
  • 对不起,我还没用过 Checker 框架。我不相信注释处理器会比使用反射更快(虽然不确定)。但在遇到实际性能问题之前,我不会考虑任何优化。通常反射足够快......
  • 您知道如何通过反射 API 访问与局部变量一起使用的类型的类型注释吗?在方法体内部有类似@NotNull String str = "";String str = (@Nullable String) null; 的东西吗?我想像这样使用@ExpectedType 注释:ObjectA_DTO aDto = (@ExpectedType ObjectA_DTO) someService.doSomething(...);
  • 无法使用 Java 反射 api 访问在变量声明或强制转换表达式上声明的类型注释。您可以通过使用ASM 等外部库读取方法的字节码来访问它们。我在 this answer 末尾的 hello world 示例中使用了这种方法。以这种方式访问​​类型注释当然是静态代码分析和工具的一种选择,但我不推荐这种方法来运行时访问类型注释......
【解决方案2】:

我认为您将在运行时使用注释与在“编译”时通过各种工具使用相同的注释混为一谈。 Processor 接口用于工具(编译器、javadoc 生成器),而不是运行时代码。

【讨论】:

  • 我想像 MapStruct 或 Lombok 那样生成代码以获得更好的性能,而不是在运行时使用 java 反射。
【解决方案3】:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface SearchDefinition {

    public String identifier() default "";

}

@SearchDefinition - 可以在任何地方使用

【讨论】:

  • 这个答案是完全错误的@Target(ElementType.FIELD)注解不能在任何地方使用。它只能与类字段一起使用。如果 @Target 被省略,它几乎可以在任何地方使用,除了类型转换 ((@SearchDefinition String) "abc") 或泛型类型 (List&lt;@SearchDefinition String&gt;)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-12-12
  • 2014-01-13
  • 1970-01-01
  • 2014-04-09
  • 1970-01-01
  • 2021-11-07
相关资源
最近更新 更多