12

After migrating a project from Spring Boot v2.7 to v3.0 (and thus from Spring Integration v5.5 to v6.0), the following warnings are printed out:

WARN 22084 --- [  restartedMain] ocalVariableTableParameterNameDiscoverer : Using deprecated '-debug' fallback for parameter name resolution. Compile the affected code with '-parameters' instead or avoid its introspection: com.foobar.MyClassA
WARN 22084 --- [  restartedMain] ocalVariableTableParameterNameDiscoverer : Using deprecated '-debug' fallback for parameter name resolution. Compile the affected code with '-parameters' instead or avoid its introspection: com.foobar.MyClassB
WARN 22084 --- [  restartedMain] ocalVariableTableParameterNameDiscoverer : Using deprecated '-debug' fallback for parameter name resolution. Compile the affected code with '-parameters' instead or avoid its introspection: com.foobar.MyClassC
WARN 22084 --- [  restartedMain] ocalVariableTableParameterNameDiscoverer : Using deprecated '-debug' fallback for parameter name resolution. Compile the affected code with '-parameters' instead or avoid its introspection: com.foobar.MyClassD

MyClassA extends IntegrationFlowAdapter, and is annotated with @Component:

package com.foobar;

@Component
class MyClassA extends IntegrationFlowAdapter {
  // …
}

MyClassB is annotated with @ConfigurationProperties:

package com.foobar;

@ConfigurationProperties("my-config")
class MyClassB {
  // …
}

MyClassC is annotated with @Configuration:

package com.foobar;

@Configuration
class MyClassC {
  // …
}

And this particular one not even extending anything, nor annotated:

package com.foobar;

class MyClassD {
  // …
}

I didn’t see any relevant information in the Spring Boot and Spring Integration migration guides. There is a section about name resolution in the Spring Boot migration guide, but it is related to Gradle, and I’m using Maven. I’m not even sure what this name resolution is all about.

I’m puzzled with the class LocalVariableTableParameterNameDiscoverer, and I’m not sure what migration task I’m supposed to do.

Artem Bilan
  • 113,505
  • 11
  • 91
  • 118
Morgan Courbet
  • 772
  • 1
  • 8
  • 18
  • 1
    What you need to do is in the message. There is more documentation then just Spring Boot and Spring Integration. Spring Boot is build on top of Spring Framework, which has this change (and also the documentation for it). https://github.com/spring-projects/spring-framework/wiki/Upgrading-to-Spring-Framework-6.x (Core Container). – M. Deinum Nov 28 '22 at 13:27

4 Answers4

8

As pointed by @m-deinum in the question’s comments, the problem is not related to Spring Boot or Spring Integration, but to Spring Framework itself. I needed to add the -parameters option to javac.

If you are using Maven, adding the following in the pom.xml should solve the problem:

<project>
  
  <!-- … -->

  <build>
    <pluginManagement>
      <plugins>
        <plugin>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>3.10.1</version>
          <configuration>
            <compilerArgs>
              <arg>-parameters</arg>
            </compilerArgs>
          </configuration>
        </plugin>
      </plugins>
    </pluginManagement>
  </build>

  <!-- … -->

</project>
Morgan Courbet
  • 772
  • 1
  • 8
  • 18
  • setup for intellij: https://stackoverflow.com/questions/39217830/how-to-use-parameters-javac-option-in-intellij – cherish sham Dec 05 '22 at 13:04
  • 1
    If I use `spring-boot-maven-plugin`, not `maven-compiler-plugin` , How to config? – Vy Do Dec 19 '22 at 01:31
  • @RaphaëlColantonio You’re already using `maven-compiler-plugin` when the `compile` phase is invoked. This snippet tells Maven to use the `-parameters` flag when it compiles your sources. – Morgan Courbet Dec 19 '22 at 16:02
  • 1
    You can even replace `-parameters` by `true`. https://maven.apache.org/plugins/maven-compiler-plugin/compile-mojo.html#parameters – nyg Jun 09 '23 at 21:38
3

Apparently, this is seen as a bug and will be fixed in Spring Framework 6.0.3.

It looks like the core bean factory is unnecessarily retrieving constructor parameter names during the constructor autowiring algorithm, despite no constructor argument names having been specified. That's a bug we're going to fix in 6.0.3. [...] The warnings should disappear when running that same code on 6.0.3 then.

See https://github.com/spring-projects/spring-framework/issues/29612#issuecomment-1333705627

Fable
  • 371
  • 1
  • 4
  • 11
3

Morgan's answer provides the correct fix, but I want to show some code to explain why this happens.

Given the following @Configuration class:

@Configuration
public class AppConfig {

    @Bean
    public SomeBean someBean(FooBar foo, FooBar bar) {
        return new SomeBean(foo, bar);
    }

    @Bean
    public FooBar foo() {
        return new FooBar();
    }

    @Bean
    public FooBar bar() {
        return new FooBar();
    }
}

When creating the bean someBean, Spring must know which beans to use for each of the two parameters of the someBean() method. It does this by looking at the parameter names of the method (i.e. foo and bar) and compares it with the name of existing beans. The deprecated LocalVariableTableParameterNameDiscoverer is used to resolve the parameter names of the method.

However, the parameter names are only kept after the compilation of the Java classes if the -debug flag is passed to the javac compiler. By default, the Maven compiler plugin does pass this flag to javac. That's why LocalVariableTableParameterNameDiscoverer can do its job and why the warning is emitted.

With the code above, one can try to change the configuration of the Maven compiler plugin not to pass the debug flag to javac:

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>${maven-compiler-plugin.version}</version>
            <configuration>
                <debug>false</debug>
            </configuration>
        </plugin>
    </plugins>
</build>

After compiling and executing the app, the warning will be replaced by the error:

Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'edu.self.nyg.example.app.FooBar' available: expected single matching bean but found 2: foo,bar

For parameters names to be retained after compilation without the use of the debug flag, the -parameters flag must be used. This will allow Spring's StandardReflectionParameterNameDiscoverer to replace LocalVariableTableParameterNameDiscoverer and correctly find the proper beans for the foo and bar parameters of someBean().

The use of -parameters seems to be recommended, but to get rid of the warning one can also do the following if needed:

@Bean
public SomeBean someBean(
        @Qualifier("foo") FooBar foo,
        @Qualifier("bar") FooBar bar) {
    return new SomeBean(foo, bar);
}

Note that it's ok to have both debug and parameters flags set to true, Spring will use StandardReflectionParameterNameDiscoverer and won't emit a warning.

nyg
  • 2,380
  • 3
  • 25
  • 40
  • 1
    This is a much more complete answer! TBH, I just applied what the Spring framework told me in the logs without understanding what was going on. I was not even fully satisfied with my own answer, and that's the reason why I didn't accepted it. I will accept yours. Thanks! – Morgan Courbet Jun 11 '23 at 07:06
2

This is somewhat irrelevant to Spring Integration, neither Spring Boot. The change has happened in Spring Framework by itself: https://github.com/spring-projects/spring-framework/issues/29563.

Yes, Spring Integration uses that LocalVariableTableParameterNameDiscoverer in the MessagePublishingInterceptor for @Publisher to have method arguments as an PublisherMetadataSource.ARGUMENT_MAP_VARIABLE_NAME variable in a SpEL EvaluationContext: https://docs.spring.io/spring-integration/docs/current/reference/html/message-publishing.html#message-publishing.

Another one is a @MessagingGateway where we try to resolve header names from @Header method params if you don't provide a name attribute for that annotation: https://docs.spring.io/spring-integration/docs/current/reference/html/messaging-endpoints.html#mapping-method-arguments

Similar logic is present in the MessagingMethodInvokerHelper to resolve header names for POJO service methods: https://docs.spring.io/spring-integration/docs/current/reference/html/configuration.html#annotations

So, we need more info from your Spring Integration configuration to determine the place where you possible just would need to add a name attribute to the @Header annotation on your method param.

I also see that this one LocalVariableTableParameterNameDiscoverer was deprecated in 6.0.1. Please, raise a GH issue against Spring Integration, so we will fix it in favor of the mentioned StandardReflectionParameterNameDiscoverer.

Artem Bilan
  • 113,505
  • 11
  • 91
  • 118
  • Whoops. Seems we crossposted. Do you mean adding the `-parameters` to `javac` is hiding a problem? Not adding a `name` attribute to the `@Header` may expose my application to future issues? – Morgan Courbet Nov 28 '22 at 14:26
  • Not sure why issues. The point is that without `name` you have to compile your project with that `-parameters`: it cannot be without both. Although since it doesn't fail for you, probably your warning is not related to Spring Integration. Nevertheless it is better to fix Spring Integration anyway. So, open a GH issue to follow up. You may scan your code for `@Header` or so, but now I doubt you have the problem there... – Artem Bilan Nov 28 '22 at 14:30
  • 1
    Fixed with this: https://github.com/spring-projects/spring-integration/pull/3956 – Artem Bilan Nov 28 '22 at 21:52