我在这里发现了一些类似的问题,但是不完整的答案没有帮助,并且产生了更多的混淆而不是澄清任何事情,所以这是我尝试提出一个更有条理的问题,并希望获得有助于更多用户的答案。
我的简化示例:我有一个带有两个不同构造函数的Java类
public class ObjectOfInterest {
public ObjectOfInterest(String string, Integer int) { ... }
public ObjectOfInterest(String string1, String string2) { ... }
...
}
我需要对这些构造函数的调用进行一些编译时验证。参数string2
必须是一些文字,我想根据内容将调用标记为警告(即发出警告,当它不是文字或文字格式不正确时)。
不幸的是,使用Eclipse进行Java验证的文档不容易理解,有时已经过时,在我看来似乎不完整,似乎没有一个工作实例可以在教程中使用。 / p>
我的目标: 第一步:我想要一个验证器来标记这两个参数版本的调用并带有警告 - 只是为了开始某个地方并了解基础知识。
到目前为止我发现了:
我见过的几个例子是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
,IValidationContext
和IReporter
- 也许我完全不知道错误的方式,但我得到了什么?如何在验证中找到该构造函数的调用?
在第一步更清楚之后,我会扩展这个问题。 Outlook,我想要为构造函数的两个String版本添加快速修复的可能性,并以这种方式操作代码 - 但这至少要提前两步,详情将在稍后发布。
答案 0 :(得分:3)
首先,我必须说你正在努力超越正常的Java编程。除了使用普通类型可以实现的功能之外,在编译时没有正常的方法进行验证。
您想要做的还超出使用注释处理器可以做的。注释处理器是一种半正常的,因为它们是标准化的并且是Java框架的一部分。它们是在编译期间运行的类,它们将类和方法的签名作为输入,并可用于验证和代码生成。
如果你还在做什么呢有异常的方法,但是:
您似乎正在尝试的解决方案是编写一个Eclipse插件,该插件使用Eclipse Java工具进行验证。这应该有用,我不知道它会有多容易,验证只适用于使用Eclipse的用户。
根据我(有限)知识看起来最好的工具是:
静态分析的 Checker Framework 。
它完全用于您想要做的事情。它似乎有充分的文档记录,易于设置。它用于执行空值分析和正则表达式语法的编译时验证。有tutorial听起来与你的东西很相似。有关creating a new checker的手册中有一章。
使用此解决方案可能需要花费大量时间和精力,但我认为它看起来非常有趣!
答案 1 :(得分:1)
注意:强>
这可能不是确切的解决方案。但我正在努力讨论 解决问题的可能方法。我已经提到了 可能有助于工作的替代方法。请加入 的讨论强>
我想尝试使用JDK-5
中引入的 Java Annotation Processors ,并在JDK-6
规范下JSR-269
标准化。
需要注释应使用自定义注释根据规则验证的文件(java @interfaces
)。如果无法对每个文件进行注释,则必须注释包含要验证的类的包(也可以迭代内部包)。以下示例将演示如何使用带注释的类和注释处理器验证类。我已将示例项目上传到github存储库。请在github上查看项目存储库https://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
类,将编译验证规则引入编译器
处理器类应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);
}
}
步骤03:创建处理服务链接文件
然后需要将名为javax.annotation.processing.Processor
的类添加到项目的资源路径中(/src/main/resources/META-INF/services
)。该文件仅包含Processor
的类名。根据前面的例子,配置文件内容如下。
com.mac.compile_validation.CompileValidationProcessor
以前的方法适用于maven项目。如果需要,可以手动将配置文件注入输出META-INF/services
文件的.jar
文件夹。
步骤04:禁用当前项目的验证
禁用当前项目的注释处理。如果启用它,它将无法构建当前项目,因为编译器尝试找到要验证的Processor
类。但它仍然没有编译。因此,它本身就无法构建项目。将以下代码添加到pom.xml
(内部<build> -> <plugin>
)。
<compilerArgument>-proc:none</compilerArgument>
现在差不多完了。唯一需要做的就是将构建输出
.jar
文件依赖项添加到原始项目中。
是时候测试项目了。使用先前创建的自定义注释(CompileValidation
)注释所需的类。如果无法验证带注释的类,它将显示警告。我的输出如下。
替代解决方案
PMD
,这是一个java源代码扫描程序。它提供了使用xml配置定义规则的方法。Java Instrumentation API
,但只有在违反规则的情况下才会导致应用程序崩溃。这不是一个好习惯。