springgroovyspring-aopjep-118

Problems accessing method parameter names of Groovy classes in Spring AOP 6.1+


Since 6.1 "Parameter Name Retention

LocalVariableTableParameterNameDiscoverer has been removed in 6.1."

even with -parameters in the java compiler, we are no longer able to do things like this

@CustomAnnotation(projectIdParameter = "projectId", userIdParameter = "#userEntity.id")
public ProjectEntity getProject(UserEntity userEntity, String projectId)

Because in the @Before passing in the JoinPoint, we used to be able to do

joinPoint.getSignature().getParameterNames()

However getParameterNames() is no longer available, and if you do cast getSignature to MethodSignature, getParameterNames returns null now.

What we are left to do is putting the functionality of the aspect into each method that was annotated before. We like using AOP for these security type issues, but not sure how to get it to work with the changes in Spring.

Is there anyway we can keep our AOP approach?

UPDATE: This is still an issue, and extremely frustrating in that it works then it doesn't work. And the only way to fix it is to run mvn clean install many times till one of them works.

Here is a screenshot of the last thing I needed to do in IntelliJ to make it work when running the server through services locally.

enter image description here


Solution

  • even with -parameters in the java compiler, we are no longer able to do things like...

    Then you are doing something wrong. This works just fine, both from Maven and when auto-importing the Maven project and running from IntelliJ IDEA:

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
      <modelVersion>4.0.0</modelVersion>
    
      <groupId>com.example</groupId>
      <artifactId>spring-boot-sample</artifactId>
      <version>1.0-SNAPSHOT</version>
    
      <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <groovy.version>4.0.19</groovy.version>
      </properties>
    
      <build>
        <plugins>
          <plugin>
            <groupId>org.codehaus.gmavenplus</groupId>
            <artifactId>gmavenplus-plugin</artifactId>
            <version>3.0.2</version>
            <executions>
              <execution>
                <goals>
                  <goal>execute</goal>
                  <goal>addSources</goal>
                  <goal>addTestSources</goal>
                  <goal>generateStubs</goal>
                  <goal>compile</goal>
                  <goal>generateTestStubs</goal>
                  <goal>compileTests</goal>
                  <goal>removeStubs</goal>
                  <goal>removeTestStubs</goal>
                </goals>
              </execution>
            </executions>
            <configuration>
              <parameters>true</parameters>
              <verbose>true</verbose>
            </configuration>
          </plugin>
          <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.12.1</version>
            <configuration>
              <compilerArgs>
                <arg>-parameters</arg>
              </compilerArgs>
            </configuration>
          </plugin>
          <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>exec-maven-plugin</artifactId>
            <version>3.2.0</version>
            <executions>
              <execution>
                <phase>verify</phase>
                <goals>
                  <goal>java</goal>
                </goals>
              </execution>
            </executions>
            <configuration>
              <mainClass>com.example.Application</mainClass>
            </configuration>
          </plugin>
        </plugins>
      </build>
    
      <dependencies>
        <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter</artifactId>
          <version>3.2.3</version>
        </dependency>
        <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-aop</artifactId>
          <version>3.2.3</version>
        </dependency>
        <dependency>
          <groupId>org.apache.groovy</groupId>
          <artifactId>groovy</artifactId>
          <version>${groovy.version}</version>
        </dependency>
      </dependencies>
    
    </project>
    
    package com.example;
    
    public @interface CustomAnnotation {
      String projectIdParameter();
      String userIdParameter();
    }
    
    package com.example;
    
    import org.springframework.stereotype.Service;
    
    @Service
    public class SampleService {
      @CustomAnnotation(projectIdParameter = "projectId", userIdParameter = "#userEntity.id")
      public void doSomething(int foo, String bar, Object zot) {
        System.out.println("Doing something in service method");
      }
    }
    
    package com.example
    
    import org.springframework.stereotype.Service
    
    @Service
    class GroovyService {
      @CustomAnnotation(projectIdParameter = "projectId", userIdParameter = "#userEntity.id")
      void doSomething(int foo, String bar, Object zot) {
        System.out.println("Doing something in Groovy service method")
      }
    }
    
    package com.example;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.context.ConfigurableApplicationContext;
    
    @SpringBootApplication
    public class Application {
    
      public static void main(String[] args) {
        try (ConfigurableApplicationContext applicationContext = SpringApplication.run(Application.class, args)) {
          applicationContext.getBean(SampleService.class).doSomething(11, "dummy", new Integer(42));
          applicationContext.getBean(GroovyService.class).doSomething(11, "dummy", new Integer(42));
        }
      }
    }
    
    package com.example;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.reflect.MethodSignature;
    import org.springframework.stereotype.Component;
    
    import java.util.Arrays;
    
    @Aspect
    @Component
    public class SampleAspect {
      @Before("@annotation(CustomAnnotation)")
      public void beforeServiceMethodExecution(JoinPoint joinPoint) {
        System.out.println(joinPoint);
        System.out.println(
          Arrays.toString(
            ((MethodSignature) joinPoint.getSignature()).getParameterNames()
          )
        );
      }
    }
    

    Console log:

      .   ____          _            __ _ _
     /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
    ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
     \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
      '  |____| .__|_| |_|_| |_\__, | / / / /
     =========|_|==============|___/=/_/_/_/
     :: Spring Boot ::                (v3.2.3)
    
    2024-03-09T14:53:30.327+01:00  INFO 14188 --- [           main] com.example.Application                  : Starting Application using Java 21 with PID 14188 (C:\Users\Alexander\Documents\java-src\SO_AJ_Spring61ParameterNames_78129715\target\classes started by Alexander in C:\Users\Alexander\Documents\java-src\SO_AJ_Spring61ParameterNames_78129715)
    2024-03-09T14:53:30.329+01:00  INFO 14188 --- [           main] com.example.Application                  : No active profile set, falling back to 1 default profile: "default"
    2024-03-09T14:53:31.201+01:00  INFO 14188 --- [           main] com.example.Application                  : Started Application in 1.193 seconds (process running for 1.734)
    execution(void com.example.SampleService.doSomething(int,String,Object))
    [foo, bar, zot]
    Doing something in service method
    execution(void com.example.GroovyService.doSomething(int,String,Object))
    [foo, bar, zot]
    Doing something in Groovy service method
    

    Spring Boot 3.2.3, Spring 6.1.4, JDK 21, Groovy 4.0.19.


    Update: