Java注释-限制具有相同字段元素值的注释的基数(出现)

标签 java java-8 annotations java-annotations

我有以下条件,需要具有特定字段值的Java注释在类的任何字段中恰好出现一次。 Java 8 可以吗?

我的注释

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD) //can use with fields only.
public @interface TestAnnotation{

    public String id();
}

使用注释的类就像

@TestAnnotation(id="test")
private String testString;

@TestAnnotation(id="test1")
private String test1String;

@TestAnnotation(id="test2")
private String test2String;

我希望阻止程序员做类似的事情

@TestAnnotation(id="test2")
private String test2String;

@TestAnnotation(id="test2")
private String test3String;

具有特定 id 的相同注释 @TestAnnotation(id="test2") 不能在字段上使用两次。至少 id="..." 在类中的 @TestAnnotation 应用字段中应该是唯一的。

最佳答案

正如您所发现的,这可以通过注释处理器来实现。

这是一个例子:

package mcve.proc;

import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface IDExample {
    String id();
}
package mcve.proc;

import javax.annotation.processing.*;
import javax.lang.model.*;
import javax.lang.model.element.*;
import javax.lang.model.type.*;
import javax.lang.model.util.*;
import javax.tools.*;
import java.util.*;

@SupportedAnnotationTypes("mcve.proc.IDExample")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class UniqueIDProcessor extends AbstractProcessor {
    @Override
    public boolean process(Set<? extends TypeElement> annotations,
                           RoundEnvironment roundEnv) {
        Elements elements = processingEnv.getElementUtils();
        Types    types    = processingEnv.getTypeUtils();
        Map<TypeElement, Set<VariableElement>> map = new HashMap<>();
        // Find each of the fields annotated with @IDExample.
        for (Element elem : roundEnv.getElementsAnnotatedWith(IDExample.class)) {
            if (elem.getKind() == ElementKind.FIELD) {
                VariableElement var  = (VariableElement) elem;
                TypeElement     decl = (TypeElement) var.getEnclosingElement();
                // Group them by declaring class.
                map.computeIfAbsent(decl, key -> new HashSet<>()).add(var);
            }
        }
        // Now for each set of fields annotated with @IDExample...
        for (Set<VariableElement> fields : map.values()) {
            Map<String, Set<VariableElement>> fieldsByID = new HashMap<>();
            // Group them by ID.
            for (VariableElement field : fields) {
                String id = field.getAnnotation(IDExample.class).id();
                fieldsByID.computeIfAbsent(id, key -> new HashSet<>()).add(field);
            }
            fieldsByID.forEach((String id, Set<VariableElement> fieldsWithID) -> {
                // For each set of fields which have duplicate IDs,
                // cause a compilation error on each annotation.
                if (fieldsWithID.size() > 1) {
                    for (VariableElement field : fieldsWithID) {
                        // This is all just finding the annotation mirror so
                        // the compilation error appears in the right place.
                        TypeMirror idExampleMirror =
                            elements.getTypeElement(IDExample.class.getName()).asType();
                        AnnotationMirror annotation =
                            field.getAnnotationMirrors().stream()
                                 .filter(mirror -> types.isSameType(idExampleMirror, mirror.getAnnotationType()))
                                 .findFirst().get();
                        AnnotationValue value =
                            annotation.getElementValues().entrySet().stream()
                                .filter(e -> e.getKey().getSimpleName().contentEquals("id"))
                                .map(e -> e.getValue())
                                .findFirst().get();
                        // Actually cause the compilation error.
                        String errorMessage = String.format("\"%s\" is a duplicate ID.", id);
                        processingEnv.getMessager()
                                     .printMessage(Diagnostic.Kind.ERROR,
                                                   errorMessage,
                                                   field,
                                                   annotation,
                                                   value);
                    }
                }
            });
        }
        return false;
    }
}

有一个关于如何使注释处理工作的教程 here 。例如,要使上面的示例处理器正常工作,您大致需要执行以下操作(我认为这取决于您的 IDE):

  • 为两个类创建一个项目/单独的 jar mcve.proc.IDExamplemcve.proc.UniqueIDProcessor .
  • 在该 jar 中,创建一个目录 META-INF/services .
  • 在该目录中,创建一个名为 javax.annotation.processing.Processor 的文本文件。 (无文件扩展名)其内容是注释处理器的完全限定名称 mcve.proc.UniqueIDProcessor .
  • 将该项目/jar 作为库导入到您的主项目中。
  • 可以添加mcve.proc.UniqueIDProcessor作为注释处理器,例如您的项目属性(如果存在/有必要)。我知道 Netbeans 就是这样做的。我不知道其他 IDE 的情况。

关于Java注释-限制具有相同字段元素值的注释的基数(出现),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44464081/

相关文章:

java - git : Run dos2unix commands only on modified files using git status command

java - Mysql:输入文本,使用哪种类型?

java - 有没有一种方法可以使用 "groupingBy"为嵌套结构中的多个元素收集 map ?

php - 在 ZF2 中使用表单注释和 DoctrineORM 在 composedObject 中呈现值

java - 无需编译即可处理java文件中的注释

Java:它一直说 "variable yn might not have been initialized"

java - 卸载通过 RMI 传递的类

java - 如何使用 TLS1.0/TLS1.1 强制加载 Java 小程序

java - 如何在 Java 8 Stream Filter 中基于子文档过滤 Mongo 文档

unit-testing - Mockito 单元测试 lombok.extern.slf4j @Slf4j 注释日志?