有没有办法让 Java 自定义注释处理器与 RetentionPolicy SOURCE 一起工作?

问题描述 投票:0回答:1

我创建了一个 Java 自定义注释处理器,以 RetentionPolicy 作为源,为我生成个性化代码,就像 Lombok 的 Getter 和 Setter 一样。

不幸的是,它没有起作用,因为 Lombok 通过与 Java 编译器通信并修改其工作方式来提供此功能,并且还针对每个 IDE 进行了一些修改。

似乎唯一可行的方法就是为 Lombok 项目做出贡献,并且让他们接受代码。

我已尝试以下方法来解决此问题:

  1. 在 Eclipse 和 intellij IDE 中启用注释处理。

  2. 使用相同的JDK版本。

  3. 在pom.xml中配置annotationProcessorPaths。

这里是我需要抽象处理器在 DemoPage.class 中生成的代码:

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 annotations refactoring annotation-processing
1个回答
0
投票

简短回答:不!

更长的答案:目前,Java JDK确实提供了一个API来允许注释处理器修改处理后的源代码。正如你所说,Lombok 使用 ASM 或类似工具,用编译器生成的字节码做了一些奇怪的事情。

但是如果没有这些技巧,注释处理器只能创建一个新的源文件,并在下一个周期中编译为

*.class
文件。

当然,Lombok 开发人员所做的事情也可以由您完成 - 还有其他几个工具也可以按照相同的思路工作。

管道中还有一个新的 API,应该向 JDK 添加操作

*.class
文件的字节码的功能。下一步是通过该功能增强注释处理器的 API。

© www.soinside.com 2019 - 2024. All rights reserved.