编译方法参数的时间验证

out*_*ind 6 java eclipse validation

我在这里发现了一些类似的问题,但是不完整的答案没有帮助,并且产生了更多的混淆而不是澄清任何事情,所以这是我尝试提出一个更有条理的问题并获得有希望的答案,这将有助于更多的用户.

我的简化示例:我有一个带有两个不同构造函数的Java类

public class ObjectOfInterest {
  public ObjectOfInterest(String string, Integer int) { ... }
  public ObjectOfInterest(String string1, String string2) { ... }
  ...
}
Run Code Online (Sandbox Code Playgroud)

我需要对这些构造函数的调用进行一些编译时验证.参数string2必须是一些文字,我想根据内容将调用标记为警告(即发出警告,当它不是文字或文字格式不正确时).

不幸的是,使用Eclipse进行Java验证的文档并不容易理解,有时已经过时,在我看来似乎不完整,并且似乎没有一个工作示例,它足够短,可以在教程中使用.

我的目标:第一步:我想要一个验证器来标记这两个参数版本的调用并带有警告 - 只是为了开始某个地方并了解基础知识.

我已经发现迄今:我见过的几个例子,做一个public class MyValidator implements IValidator, ISourceValidator地方IValidator需要实现的方法public void validate(IValidationContext arg0, IReporter arg1) throws ValidationException,似乎是从旧版本的验证框架的(有时我发现只是评论空方法没用),并且ISourceValidator需要实现一个方法public void validate(IRegion arg0, IValidationContext arg1, IReporter arg2)- 这似乎是更新的版本.

然后你必须为一些添加一些扩展点plugin.xml(我不完全清楚这个plugin.xml的位置).

凡我刺在黑暗中:这是完全不清楚如何一起工作IRegion,IValidationContextIReporter-也许我是在完全错误的方式,但我该怎么来到这里的?如何在验证中找到该构造函数的调用?

在第一步更清楚之后,我会扩展这个问题.Outlook,我想要为构造函数的两个String版本添加快速修复的可能性并以这种方式操作代码 - 但这至少要领先两步,细节将在稍后发布.

Cha*_*uni 1

笔记:

这可能不是确切的解决方案。但我正在尝试讨论 解决该问题的可能方法。我已经提到了 可能有助于工作的替代方法。请大家一起参与讨论

我想尝试使用Java 注释处理器,它在规范中引入JDK-5并标准化。JDK-6JSR-269

需要对文件(java @interfaces)进行注释,该文件应使用自定义注释根据规则进行验证。如果不可能注释每个文件,则必须注释包含要验证的类的包(也可以迭代内部包)。以下示例将演示如何使用带注释的类和注释处理器来验证类。我已将示例项目上传到 github 存储库。请查看 github 上的项目存储库https://github.com/hjchanna/compile_validation_java

STEP 01:创建注释界面(@interface

package com.mac.compile_validation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author hjchanna
 */
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.TYPE)
public @interface CompileValidation {

}
Run Code Online (Sandbox Code Playgroud)

STEP 02:创建一个Processor类,向编译器引入编译验证规则

处理器类应该extend来自javax.annotation.processing.AbstractProcessor并且应该用@SupportedAnnotationTypes@SupportedSourceVersion注释进行注释。CompileValidationProcessor请根据具体要求或验证规则修改类。

package com.mac.compile_validation;

import java.util.List;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.ElementFilter;
import javax.tools.Diagnostic;

/**
 *
 * @author hjchanna
 */
@SupportedAnnotationTypes("com.mac.compile_validation.CompileValidation")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class CompileValidationProcessor extends AbstractProcessor {

    /**
     * Processes a set of annotation types on type elements originating from the
     * prior round and returns whether or not these annotation types are claimed
     * by this processor. If {@code
     * true} is returned, the annotation types are claimed and subsequent
     * processors will not be asked to process them; if {@code false} is
     * returned, the annotation types are unclaimed and subsequent processors
     * may be asked to process them. A processor may always return the same
     * boolean value or may vary the result based on chosen criteria.
     *
     * The input set will be empty if the processor supports {@code
     * "*"} and the root elements have no annotations. A {@code
     * Processor} must gracefully handle an empty set of annotations.
     *
     * @param annotations the annotation types requested to be processed
     * @param roundEnv environment for information about the current and prior
     * round
     * @return whether or not the set of annotation types are claimed by this
     * processor
     */
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        //Iterate through compiling files which annotated with @CompileValidation
        for (Element elem : roundEnv.getElementsAnnotatedWith(CompileValidation.class)) {
            //find type element for element
            TypeElement typeElement = findEnclosingTypeElement(elem);

            //required parameter types
            TypeElement stringType = processingEnv.getElementUtils().getTypeElement("java.lang.String");
            TypeElement integerType = processingEnv.getElementUtils().getTypeElement("java.lang.Integer");

            //find construtors according to your scenario
            ExecutableElement conA = findConstructor(typeElement, stringType.asType(), integerType.asType());
            ExecutableElement conB = findConstructor(typeElement, stringType.asType(), stringType.asType());

            //check availability of constructors, if not available it should show a warning message in compile time
            if (conA == null || conB == null) {
                String message = "Type " + typeElement + " has malformed constructors.";
                processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING, message);
            }

        }
        return true; // no further processing of this annotation type
    }

    /**
     * Returns a constructor which have two parameters and parameter types equal
     * to paramA and paramB. Return null if required constructor is not
     * available.
     *
     * @param typeElement like the class which may constructors encapsulated
     * @param paramA first parameter of required constructor
     * @param paramB second parameter of required constructor
     * @return constructor which have required parameters
     */
    private static ExecutableElement findConstructor(TypeElement typeElement, TypeMirror paramA, TypeMirror paramB) {
        List<ExecutableElement> executableElements = ElementFilter.constructorsIn(typeElement.getEnclosedElements());

        for (ExecutableElement executableElement : executableElements) {
            List<VariableElement> variableElements = (List<VariableElement>) executableElement.getParameters();

            //match constructor params and length
            if (variableElements.size() == 2
                    && variableElements.get(0).asType().equals(paramA)
                    && variableElements.get(1).asType().equals(paramB)) {
                return executableElement;
            }
        }

        return null;
    }

    /**
     * Returns the TypeElement of element e.
     *
     * @param e Element which contain TypeElement
     * @return Type element
     */
    public static TypeElement findEnclosingTypeElement(Element e) {
        while (e != null && !(e instanceof TypeElement)) {
            e = e.getEnclosingElement();
        }

        return TypeElement.class.cast(e);
    }
}
Run Code Online (Sandbox Code Playgroud)

STEP 03:创建处理服务链接文件

然后需要将一个具有名称的类添加javax.annotation.processing.Processor到项目的资源路径中(/src/main/resources/META-INF/services)。该文件仅包含Processor. 根据前面的例子配置文件内容如下。

com.mac.compile_validation.CompileValidationProcessor
Run Code Online (Sandbox Code Playgroud)

之前的方法适用于maven项目。如果需要,可以将配置文件手动注入到META-INF/services输出文件的文件夹中。.jar

步骤 04:禁用当前项目的验证

禁用当前项目的注释处理。如果启用它,它将无法构建当前项目,因为编译器会尝试找到Processor要验证的类。但它仍然没有编译。所以它会因为自身原因而无法构建该项目。将以下代码添加到pom.xml(内部<build> -> <plugin>)。

<compilerArgument>-proc:none</compilerArgument>
Run Code Online (Sandbox Code Playgroud)

现在已经快完成了。唯一需要做的就是将构建输出.jar文件依赖项添加到原始项目中。

是时候测试该项目了。使用之前创建的自定义注释来注释所需的类 ( CompileValidation)。如果无法验证带注释的类,它将显示警告。我的输出如下。

构建输出

替代解决方案

  • 可以使用PMDJava 源代码扫描器。它提供了使用 xml 配置定义规则的方法。
  • 尝试在启动时使用 java 反射来验证类。(这不是你问的。但在开始工作之前,像 spring、hibernate 和其他知名框架那样验证内容是一个很好的做法。)
  • 尝试一下Java Instrumentation API,但如果违反了规则,唯一可能的反应是使应用程序崩溃。这不是一个好的做法。