我创建了一个 Java 自定义注释处理器,以 RetentionPolicy 作为源,为我生成个性化代码,就像 Lombok 的 Getter 和 Setter 一样。
不幸的是,它没有起作用,因为 Lombok 通过与 Java 编译器通信并修改其工作方式来提供此功能,并且还针对每个 IDE 进行了一些修改。
似乎唯一可行的方法就是为 Lombok 项目做出贡献,并且让他们接受代码。
我已尝试以下方法来解决此问题:
在 Eclipse 和 intellij IDE 中启用注释处理。
使用相同的JDK版本。
在pom.xml中配置annotationProcessorPaths。
public void clickGoogleSearchBtn() {
googleSearchBtn.click();
}
package com.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
//Annotation to generate click method for WebElement fields
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.FIELD)
public @interface ClickElement {
}
package com.annotations.processors;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
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.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.tools.Diagnostic;
import com.annotations.ClickElement;
import com.google.auto.service.AutoService;
import com.sun.tools.javac.api.JavacTrees;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.code.TypeTag;
import com.sun.tools.javac.processing.JavacProcessingEnvironment;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.Name;
import com.sun.tools.javac.util.Names;
@SupportedAnnotationTypes("com.automationdemo1.annotations.ClickElement")
@SupportedSourceVersion(SourceVersion.RELEASE_21)
@AutoService(Processor.class)
public class ClickMethodProcessor extends AbstractProcessor {
private Messager messager;
private JavacTrees javacTrees;
private TreeMaker treeMaker;
private Names names;
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
messager.printMessage(Diagnostic.Kind.NOTE, "Hello");
// Pass the wrapped ProcessingEnvironment object to the super method.
super.init(processingEnv);
// If you need access to the original ProcessingEnvironment for JavacTrees
if (processingEnv instanceof JavacProcessingEnvironment) {
JavacProcessingEnvironment javacProcessingEnv = (JavacProcessingEnvironment) processingEnv;
this.javacTrees = JavacTrees.instance(javacProcessingEnv);
} else {
// Fallback for non-Javac environments
this.javacTrees = JavacTrees.instance(processingEnv);
}
Context context = ((JavacProcessingEnvironment) processingEnv).getContext();
this.treeMaker = TreeMaker.instance(context);
this.names = Names.instance(context);
this.messager = processingEnv.getMessager();
messager.printMessage(Diagnostic.Kind.NOTE, "Annotation processor initialized with JavacTrees.");
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
for (Element element : roundEnv.getElementsAnnotatedWith(ClickElement.class)) {
if (element instanceof VariableElement && isWebElement(element)) {
generateClickMethod((VariableElement) element);
}
}
return true;
}
private boolean isWebElement(Element element) {
return element.asType().toString().equals("org.openqa.selenium.WebElement");
}
private void generateClickMethod(VariableElement field) {
TypeElement enclosingClass = (TypeElement) field.getEnclosingElement();
String fieldName = field.getSimpleName().toString();
Name methodName = names.fromString("click" + capitalize(fieldName));
JCTree.JCClassDecl classTree = (JCTree.JCClassDecl) javacTrees.getTree(enclosingClass);
JCTree.JCMethodDecl clickMethod = createClickMethod(methodName, fieldName);
classTree.defs = classTree.defs.prepend(clickMethod);
messager.printMessage(Diagnostic.Kind.NOTE, "Generated click method: " + methodName);
}
private JCTree.JCMethodDecl createClickMethod(Name methodName, String fieldName) {
JCTree.JCExpression returnType = treeMaker.TypeIdent(TypeTag.VOID);
JCTree.JCExpressionStatement clickStatement = treeMaker.Exec(
treeMaker.Apply(
List.nil(),
treeMaker.Select(
treeMaker.Ident(names.fromString(fieldName)),
names.fromString("click")
),
List.nil()
)
);
JCTree.JCBlock methodBody = treeMaker.Block(0, List.of(clickStatement));
return treeMaker.MethodDef(
treeMaker.Modifiers(Flags.PUBLIC),
methodName,
returnType,
List.nil(),
List.nil(),
List.nil(),
methodBody,
null
);
}
private String capitalize(String str) {
return Character.toUpperCase(str.charAt(0)) + str.substring(1);
}
}
package com.annotations.pages;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import com.annotations.ClickElement;
import com.annotations.base.BasePage;
public class DemoPage extends BasePage {
@ClickElement
@FindBy(xpath="(//input[@name='btnK'])[2]")
public WebElement googleSearchBtn;
}
简短回答:不!
更长的答案:目前,Java JDK确实提供了一个API来允许注释处理器修改处理后的源代码。正如你所说,Lombok 使用 ASM 或类似工具,用编译器生成的字节码做了一些奇怪的事情。
但是如果没有这些技巧,注释处理器只能创建一个新的源文件,并在下一个周期中编译为
*.class
文件。
当然,Lombok 开发人员所做的事情也可以由您完成 - 还有其他几个工具也可以按照相同的思路工作。
管道中还有一个新的 API,应该向 JDK 添加操作
*.class
文件的字节码的功能。下一步是通过该功能增强注释处理器的 API。