9

Suppose I have the following code snippet:

@RequestMapping(method = RequestMethod.GET)
public List<Article> getArticles(@RequestParam int offset,
                                 @RequestParam int limit) {
  ...
}

How can Spring match the HTTP query parameters to the right formal parameters, when the parameter name is not explicitly stated as an annotation parameter?

Does it suppose the formal parameter names are always present in bytecode?

As I understand, it does not always have to be so. The formal parameter names can be retrieved from bytecode only when:

a) the class file has been compiled with -parameters javac option

b) the class file has been compiled with -g (or -g:vars) javac option, which adds debug information including the true variable names (since the formal parameters are stored as the first local variables of method)

Slava Vedenin
  • 58,326
  • 13
  • 40
  • 59
gabi197
  • 103
  • 5
  • Does your spring version not require Java 8 perhaps? – ernest_k Jun 03 '18 at 09:25
  • it does.. I know Java 8 Reflection API provides possibility to retrieve method parameter names, but I thought one of the requirements above still has to be met.. when I compile Java source file with javac 1.8 with default options, the formal param names are not present in the class file – gabi197 Jun 03 '18 at 09:42
  • Your understanding is mostly correct. Method parameter names are only present in the bytecode, if either of these two conditions holds. But since you have annotated the parameters, there is a third option. An annotation processor integrated into the build process may get the parameter names from the compiler through the annotation processor API. At this point, the names are extracted from the source code. The annotation processor may generate additional code based on that knowledge. – Holger Jun 03 '18 at 10:29
  • 1
    I don't know Spring well enough to even guess what source code to read. But what I found here (https://github.com/exacode/spring-webmvc-processor/blob/master/spring-webmvc-processor/src/main/java/net/exacode/spring/web/processor/MetaControllerGenerator.java) seems to depend on reflection API, reinforcing your question. But it seems Sprint has other utilities to pull param names from code not compiled with `-parameters` (check https://www.beyondjava.net/blog/reading-java-8-method-parameter-named-reflection) – ernest_k Jun 03 '18 at 10:30
  • The way I'd go about finding out: run the application in debug mode, then have a breakpoint on something in the body of `java.lang.reflect.Parameter#getName`. That can lead to classes building mapping metadata, and eventually to the real answer. – ernest_k Jun 03 '18 at 10:33
  • Some useful answers that might help: [answer 1](https://stackoverflow.com/questions/6891539/internal-working-of-springss-requestparam-annotation#answer-6891840), [answer 2](https://stackoverflow.com/questions/5030638/spring-aop-get-access-to-argument-names/5030999#5030999), [answer 3](https://stackoverflow.com/questions/36466913/how-spring-gets-parameter-names-without-debug-information#answer-36962917). – Oleksandr Pyrohov Jun 03 '18 at 11:25
  • @Holger OK I'm hooked. Is it really an option? Because AFAIK spring relies on the fact that you will compile your code with default settings where `-g` (at least) would be provided – Eugene Jun 03 '18 at 20:28
  • @Oleksandr all of the 3 links rely on the same thing that either `-g` or `-parameters` is there. – Eugene Jun 03 '18 at 20:39
  • Unfortunately I was not able to find any additional information to confirm @Holger's hypothesis concerning the annotation processor. On the other hand, I have noticed that all my applications which are built either by Maven's install goal (e.g. Spring Boot app) or from IDE (I think it was Eclipse, old native app built under Java 7).. DO contain the debug information.. and I don't know how it got there, because I did not specify the debug option (obviously some higher powers did).. so - could it really be possible that Spring relies on the -g:vars option? – gabi197 Jun 03 '18 at 21:14
  • 1
    @Eugene my comment was not Spring specific, but just naming the options to get to the names, to acknowledge the OP’s understanding that without using one of these options, the names are not available. – Holger Jun 04 '18 at 07:40
  • @Holger mine was not about Spring either, we rely on the fact that `-g` is enabled also in our code for some stuff... My point was that you are saying that there is a third option if you annotate your parameters, which I am not aware of. Can you provide some hints where should I be looking for this please? – Eugene Jun 04 '18 at 09:06
  • @Eugene As said, this requires an annotation processor integrated into the build process which then fetches the parameter names from the compiler through [the annotation processor API](https://docs.oracle.com/javase/9/docs/api/javax/annotation/processing/RoundEnvironment.html#getElementsAnnotatedWith-javax.lang.model.element.TypeElement-). – Holger Jun 04 '18 at 13:57

1 Answers1

6

To find the answer to your question, I built my Spring REST app with maven and using the following command:

mvn clean install -X

My build.log contained the following debug information:

[DEBUG] Command line options:

... -g -nowarn -target 1.8 -source 1.8 -encoding UTF-8

As we can see, the  -g  option is present by default, which generates debug information, including local variables.

To prove otherwise, I updated the maven-compiler-plugin config with the following option:

-g:none

Config example:

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.5.1</version>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
                <compilerArgument>-g:none</compilerArgument>
            </configuration>
        </plugin>
    </plugins>
</build>

After that, my web service started to throw an error:

java.lang.IllegalArgumentException: Name for argument type [java.lang.String] not
available, and parameter name information not found in class file either.

Seems like the maven-compiler-plugin includes the -g option by default. And this allows Spring to retrieve parameter names at runtime.

Oleksandr Pyrohov
  • 14,685
  • 6
  • 61
  • 90