有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

方法参数的java编译时验证

我在这里发现了一些类似的问题,但是不完整的答案没有帮助,并且产生了比澄清任何问题更多的混乱,因此我尝试给出一个更结构化的问题,并希望得到能够帮助更多用户的答案

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

public class ObjectOfInterest {
  public ObjectOfInterest(String string, Integer int) { ... }
  public ObjectOfInterest(String string1, String string2) { ... }
  ...
}

我需要对这些构造函数的调用进行编译时验证。参数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在哪里)

我在黑暗中刺向何处:完全不清楚如何使用IRegionIValidationContextIReporter-也许我走错了路,但我在这里得到了什么?如何在验证中找到该构造函数的调用

在第一步变得更清楚之后,我会扩展这个问题。Outlook,我想为构造函数的两个字符串版本添加快速修复的可能性,并以这种方式操作代码-但这至少提前了两步,详细信息将在后面介绍


共 (2) 个答案

  1. # 1 楼答案

    NOTE:

    This may not the exact solution. But I am trying to discuss possible methods to work around the problem. I have mentioned alternative methods which may help to work. Please join for the discussion.

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

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

    步骤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 {
    
    }
    

    步骤02:创建Processor类,该类将编译验证规则引入编译器

    处理器类应该从javax.annotation.processing.AbstractProcessor开始extend,并且应该用@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);
        }
    }
    

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

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

    com.mac.compile_validation.CompileValidationProcessor
    

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

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

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

    <compilerArgument>-proc:none</compilerArgument>
    

    It's now almost finished. Only thing have to do further is adding build output .jar file dependency into the original project.

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

    build output

    ALTERNATIVE SOLUTIONS

    • 可以使用PMD,这是一个java源代码扫描程序。它提供了使用xml配置定义规则的方法
    • 当java反射启动时,尝试使用java反射验证类。(这不是您所要求的。但是,在开始像spring、hibernate和其他著名框架那样工作之前验证内容是一个很好的做法。)
    • 请尝试Java Instrumentation API,但如果应用程序违反规则,唯一可能的反应是使其崩溃。这不是一个好的做法
  2. # 2 楼答案

    首先,我必须说,您试图的内容超出了普通Java编程的范围。除了使用普通类型可以实现的功能外,在编译时没有正常的方法进行验证

    您想要做的事情也超出了使用注释处理器所能做的事情。注释处理器是半正常的,因为它们是标准化的,并且是Java框架的一部分。它们是在编译过程中运行的类,它们获得类和方法的签名作为输入,并可用于验证和代码生成

    如果仍要执行此操作,则会出现异常方法,但是:


    Eclipse插件解决方案

    您似乎正在尝试的解决方案是编写一个使用EclipseJava工具进行验证的Eclipse插件。这应该是可行的,我不知道这有多容易,验证只对使用Eclipse的用户有效


    检查器解决方案

    根据我(有限)的知识,似乎最好的工具是:

    用于静态分析的Checker Framework

    它被用来做你想做的事情。它似乎有很好的文档记录,并且易于设置。例如,它用于对正则表达式语法进行空性分析和编译时验证。有一个tutorial听起来很像你的东西。手册中有一章是关于creating a new checker

    使用它可能需要花费大量的时间和精力来制定一个解决方案,但我也认为它看起来非常有趣