基于OpenRewrite自定义Recipe无法运行,报错类找不到求助
问题重现
1. 自定义Recipe代码
package com.yourorg; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.EqualsAndHashCode; import lombok.Value; import org.openrewrite.*; import org.openrewrite.internal.lang.NonNull; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.JavaTemplate; import org.openrewrite.java.tree.J; // Making your recipe immutable helps make them idempotent and eliminates categories of possible bugs. // Configuring your recipe in this way also guarantees that basic validation of parameters will be done for you by rewrite. // Also note: All recipes must be serializable. This is verified by RewriteTest.rewriteRun() in your tests. @Value @EqualsAndHashCode(callSuper = false) public class SayHelloRecipe extends Recipe { @Option(displayName = "Fully Qualified Class Name", description = "A fully qualified class name indicating which class to add a hello() method to.", example = "com.yourorg.FooBar") @NonNull String fullyQualifiedClassName; // All recipes must be serializable. This is verified by RewriteTest.rewriteRun() in your tests. @JsonCreator public SayHelloRecipe(@NonNull @JsonProperty("fullyQualifiedClassName") String fullyQualifiedClassName) { this.fullyQualifiedClassName = fullyQualifiedClassName; } @Override public String getDisplayName() { return "Say Hello"; } @Override public String getDescription() { return "Adds a \"hello\" method to the specified class."; } @Override public TreeVisitor<?, ExecutionContext> getVisitor() { // getVisitor() should always return a new instance of the visitor return new SayHelloVisitor(); } public class SayHelloVisitor extends JavaIsoVisitor<ExecutionContext> { private final JavaTemplate helloTemplate = JavaTemplate.builder( "public String hello() { return \"Hello from #{}!\"; }").build(); @Override public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext executionContext) { // Don't make changes to classes that don't match the fully qualified name if (classDecl.getType() == null || !classDecl.getType().getFullyQualifiedName().equals(fullyQualifiedClassName)) { return classDecl; } // Check if the class already has a method named "hello". boolean helloMethodExists = classDecl.getBody().getStatements().stream() .filter(statement -> statement instanceof J.MethodDeclaration) .map(J.MethodDeclaration.class::cast) .anyMatch(methodDeclaration -> methodDeclaration.getName().getSimpleName().equals("hello")); // If the class already has a `hello()` method, don't make any changes to it. if (helloMethodExists) { return classDecl; } // Interpolate the fullyQualifiedClassName into the template and use the resulting LST to update the class body classDecl = classDecl.withBody( helloTemplate.apply(new Cursor(getCursor(), classDecl.getBody()), classDecl.getBody().getCoordinates().lastStatement(), fullyQualifiedClassName )); return classDecl; } } }
2. Maven插件配置
<plugin> <groupId>org.openrewrite.maven</groupId> <artifactId>rewrite-maven-plugin</artifactId> <version>5.20.0</version> <configuration> <activeRecipes> <recipe>com.yourorg.sayHelloToFooBar</recipe> </activeRecipes> </configuration> </plugin>
3. rewrite.yml配置
--- type: specs.openrewrite.org/v1beta/recipe name: com.yourorg.sayHelloToFooBar recipeList: - com.yourorg.SayHelloRecipe: fullyQualifiedClassName: com.yourorg.FooBar
4. 错误日志
[ERROR] Recipe validation error in com.yourorg.SayHelloRecipe: Recipe class com.yourorg.SayHelloRecipe cannot be found
[ERROR] Recipe validation error in com.yourorg.SayHelloRecipe: Recipe class com.yourorg.SayHelloRecipe cannot be found
[ERROR] Recipe validation error in com.yourorg.SayHelloRecipe: Recipe class com.yourorg.SayHelloRecipe cannot be found
[ERROR] Recipe validation errors detected as part of one or more activeRecipe(s). Execution will continue regardless.
排查与解决方法
1. 先编译自定义Recipe代码
OpenRewrite Maven插件只能读取已编译的class文件,直接运行插件会找不到未编译的代码。先执行mvn compile编译你的Recipe类,再运行Rewrite插件。
2. 绑定插件到编译后阶段
修改Maven插件配置,将其绑定到verify阶段(该阶段在compile之后执行),确保代码先编译再执行Rewrite:
<plugin> <groupId>org.openrewrite.maven</groupId> <artifactId>rewrite-maven-plugin</artifactId> <version>5.20.0</version> <executions> <execution> <id>rewrite-run</id> <phase>verify</phase> <goals> <goal>run</goal> </goals> </execution> </executions> <configuration> <activeRecipes> <recipe>com.yourorg.sayHelloToFooBar</recipe> </activeRecipes> </configuration> </plugin>
之后执行mvn verify即可自动完成编译+Rewrite流程。
3. 确认rewrite.yml的路径
rewrite.yml必须放在项目的src/main/resources/META-INF/rewrite/目录下,OpenRewrite会自动扫描该路径下的配置文件,路径错误会导致插件无法加载你的组合Recipe。
4. 检查包名与类名的一致性
确保代码中的包名com.yourorg、类名SayHelloRecipe,与rewrite.yml、Maven配置中的引用完全一致(Java类名大小写敏感)。
5. 排查Lombok编译问题
你的Recipe使用了Lombok注解,需确保Maven已正确配置Lombok插件,否则编译后的class文件会不完整,导致OpenRewrite无法识别为有效Recipe:
<!-- 添加Lombok依赖 --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.30</version> <scope>provided</scope> </dependency> <!-- 添加Lombok Maven插件 --> <plugin> <groupId>org.projectlombok</groupId> <artifactId>lombok-maven-plugin</artifactId> <version>1.18.30.0</version> <executions> <execution> <phase>generate-sources</phase> <goals> <goal>delombok</goal> </goals> </execution> </executions> </plugin>
内容的提问来源于stack exchange,提问作者fan fan

