java 如何对特定方法参数应用配方

eblbsuwk  于 2022-12-21  发布在  Java
关注(0)|答案(1)|浏览(147)

在我的方法中,我选择了同时具有NotNullRequestParam注解的方法参数,并且我希望对这些方法参数应用OpenRewrite方法AddOrUpdateAnnotationAttribute,以将RequestParam注解的required属性设置为true。
我正在努力解决如何在一段特定的代码上应用一个方法,而不是在完整的Java类上。
应用我的配方之前的源代码示例:

import org.springframework.web.bind.annotation.RequestParam;
import javax.validation.constraints.NotNull;

class ControllerClass {
    public String sayHello (
        @NotNull @RequestParam(value = "name") String name,
        @RequestParam(value = "lang") String lang
    ) {
       return "Hello";
    }
}

应用我的配方后的预期源代码:

import org.springframework.web.bind.annotation.RequestParam;
import javax.validation.constraints.NotNull;

class ControllerClass {
    public String sayHello (
        @NotNull @RequestParam(required = true, value = "name") String name,
        @RequestParam(value = "lang") String lang
    ) {
       return "Hello";
    }
}

仅需要采用第一个参数name,因为第二个参数没有NotNull注解。
我的(简化)食谱:

public class MandatoryRequestParameter extends Recipe {

    @Override
    public @NotNull String getDisplayName() {
        return "Make RequestParam mandatory";
    }
    
    @Override
    protected @NotNull JavaIsoVisitor<ExecutionContext> getVisitor() {
        return new MandatoryRequestParameterVisitor();
    }
    
    public class MandatoryRequestParameterVisitor extends JavaIsoVisitor<ExecutionContext> {
        @Override
        public @NotNull J.MethodDeclaration visitMethodDeclaration(@NotNull J.MethodDeclaration methodDeclaration, @NotNull ExecutionContext executionContext) {
            J.MethodDeclaration methodDecl = super.visitMethodDeclaration(methodDeclaration, executionContext);
    
            return methodDeclaration.withParameters(ListUtils.map(methodDecl.getParameters(), (i, p) -> makeRequestParamMandatory(p, executionContext)));
        }
    
        private Statement makeRequestParamMandatory(Statement statement, ExecutionContext executionContext) {
            if (!(statement instanceof J.VariableDeclarations methodParameterDeclaration) || methodParameterDeclaration.getLeadingAnnotations().size() < 2) {
                return statement;
            }
    
            AddOrUpdateAnnotationAttribute addOrUpdateAnnotationAttribute = new AddOrUpdateAnnotationAttribute(
                    "org.springframework.web.bind.annotation.RequestParam", "required", "true", false
            );
    
            return (Statement) methodParameterDeclaration.acceptJava(addOrUpdateAnnotationAttribute.getVisitor(), executionContext);
        }
    }

}

当我执行我的配方时,我得到了以下错误,所以我的实现不是应用配方的正确方式。

org.openrewrite.UncaughtVisitorException: java.lang.IllegalStateException: Expected to find a matching parent for Cursor{Annotation-\>root}
at org.openrewrite.TreeVisitor.visit(TreeVisitor.java:253)
at org.openrewrite.TreeVisitor.visit(TreeVisitor.java:145)
at org.openrewrite.java.JavaTemplate.withTemplate(JavaTemplate.java:520)
at org.openrewrite.java.JavaTemplate.withTemplate(JavaTemplate.java:42)
at org.openrewrite.java.tree.J.withTemplate(J.java:87)
at org.openrewrite.java.AddOrUpdateAnnotationAttribute$1.visitAnnotation(AddOrUpdateAnnotationAttribute.java:144)
at org.openrewrite.java.AddOrUpdateAnnotationAttribute$1.visitAnnotation(AddOrUpdateAnnotationAttribute.java:78)
at org.openrewrite.java.tree.J$Annotation.acceptJava(J.java:220)
at org.openrewrite.java.tree.J.accept(J.java:60)
at org.openrewrite.TreeVisitor.visit(TreeVisitor.java:206)
at org.openrewrite.TreeVisitor.visitAndCast(TreeVisitor.java:285)
at org.openrewrite.java.JavaVisitor.lambda$visitVariableDeclarations$23(JavaVisitor.java:873)
at org.openrewrite.internal.ListUtils.lambda$map$0(ListUtils.java:141)
at org.openrewrite.internal.ListUtils.map(ListUtils.java:123)
at org.openrewrite.internal.ListUtils.map(ListUtils.java:141)
at org.openrewrite.java.JavaVisitor.visitVariableDeclarations(JavaVisitor.java:873)
at org.openrewrite.java.JavaIsoVisitor.visitVariableDeclarations(JavaIsoVisitor.java:240)
at org.openrewrite.java.JavaIsoVisitor.visitVariableDeclarations(JavaIsoVisitor.java:31)
at org.openrewrite.java.tree.J$VariableDeclarations.acceptJava(J.java:5149)
at org.springframework.sbm.jee.jaxrs.recipes.MandatoryRequestParameter$MandatoryRequestParameterVisitor.makeRequestParamMandatory(MandatoryRequestParameter.java:45)
at org.springframework.sbm.jee.jaxrs.recipes.MandatoryRequestParameter$MandatoryRequestParameterVisitor.lambda$visitMethodDeclaration$0(MandatoryRequestParameter.java:33)
z0qdvdin

z0qdvdin1#

您可以使用声明式配方来获得所需的结果。您可以在项目的根目录中创建rewrite.yml文件,然后使用rewrite的Maven或Gradle构建插件来应用该配方。

type: specs.openrewrite.org/v1beta/recipe
    name: org.example.MandatoryRequestParameter
    displayName: Make Spring `RequestParam` mandatory
    description: Add `required` attribute to `RequestParam` and set the value to `true`.
    recipeList:
      - org.openrewrite.java.AddOrUpdateAnnotationAttribute:
          annotationType: org.springframework.web.bind.annotation.RequestParam
          attributeName: required
          attributeValue: "true"

如果使用Maven,可以通过将插件添加到pom.xml来激活该配方:

<plugin>
  <groupId>org.openrewrite.maven</groupId>
  <artifactId>rewrite-maven-plugin</artifactId>
  <version>4.38.0</version>
  <configuration>
    <activeRecipes>
      <recipe>org.example.MandatoryRequestParameter</recipe>
    </activeRecipes>
  </configuration>
</plugin>

有关如何使用构建插件的更多详细信息,请参见https://docs.openrewrite.org/getting-started/getting-started
但是,如果您希望将更改限制在特定的参数上,同时仍然使用上面的方法,则可以编写命令式方法。

public class MandatoryRequestParameter extends Recipe {

    private static final String REQUEST_PARAM_FQ_NAME = "org.springframework.web.bind.annotation.RequestParam";

    @Override
    public @NotNull String getDisplayName() {
        return "Make Spring `RequestParam` mandatory";
    }

    @Override
    public String getDescription() {
        return "Add `required` attribute to `RequestParam` and set the value to `true`.";
    }

    @Override
    protected TreeVisitor<?, ExecutionContext> getSingleSourceApplicableTest() {
        // This optimization means that your visitor will only run if the source file
        // has a reference to the annotation.
        return new UsesType<>(REQUEST_PARAM_FQ_NAME);
    }

    @Override
    protected @NotNull JavaVisitor<ExecutionContext> getVisitor() {

        JavaIsoVisitor addAttributeVisitor = new AddOrUpdateAnnotationAttribute(
                REQUEST_PARAM_FQ_NAME, "required", "true", false
        ).getVisitor();

        return new JavaIsoVisitor<ExecutionContext>() {
            @Override
            public J.Annotation visitAnnotation(J.Annotation annotation, ExecutionContext ctx) {
                J.Annotation a = super.visitAnnotation(annotation, ctx);

                if (!TypeUtils.isOfClassType(a.getType(), REQUEST_PARAM_FQ_NAME)) {
                    return a;
                }

                // The visitor provides a cursor via the `getCusor()` method, and we can use that to navigate
                // up to the parent. So, in this case, when we visit the annotation, we can navigate to the
                // variable declaration upon which it is defined
                J.VariableDeclarations variableDeclaration = getCursor().getParent().getValue();

                // This is demonstrating two different ways we might restrict the change: 
                // - If the parameter's type is a java.lang.Number. Note, this will change all parameters that are
                //   subtypes of java.lang.Number
                // - If the parameter name is equal to "fred"
                JavaType paramType = variableDeclaration.getType();
                if (TypeUtils.isAssignableTo("java.lang.Number", paramType) ||
                    variableDeclaration.getVariables().get(0).getSimpleName().equals("fred")) {
                    // If there is a match, we simple delegate to nested visitor.
                    return (J.Annotation) addAttributeVisitor.visit(a, ctx, getCursor());
                }
                return a;
            }

        };
    }
    }
}

下面是一个使用OpenRewrite测试工具的示例:

@Test
    void requiredRequestParam() {

        rewriteRun(
                spec -> spec
                        .recipe(new MandatoryRequestParameter())
                        .parser(JavaParser.fromJavaVersion().classpath("spring-web")),
                java(
                        """
                          import org.springframework.web.bind.annotation.RequestParam;
                          
                          class ControllerClass {
                            public String sayHello (
                              @RequestParam(value = "fred") String fred,
                              @RequestParam(value = "lang") String lang,
                              @RequestParam(value = "aNumber") Long aNumber
                            ) {
                              return "Hello";
                            }
                          }
                        """,
                        """
                          import org.springframework.web.bind.annotation.RequestParam;
                          
                          class ControllerClass {
                            public String sayHello (
                              @RequestParam(required = true, value = "fred") String fred,
                              @RequestParam(value = "lang") String lang,
                              @RequestParam(required = true, value = "aNumber") Long aNumber
                            ) {
                              return "Hello";
                            }
                          }
                        """
                )
        );
    }

希望这能有所帮助!

相关问题