SpringBootを3.2.0に上げた際に、
同じ型のBeanを定義していて、名前による(Qualifierではない)Autowireが失敗するようになってしまったので、それの備忘録
とりあえず複数のBeanがマッチするところまでの話は以下の記事に書いてるので端折る
DependencyDescriptor#getDependencyName
が呼ばれる
https://github.com/spring-projects/spring-framework/blob/v6.1.2/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java#L1728
MethodParameter#getParameterName
が呼ばれる
https://github.com/spring-projects/spring-framework/blob/v6.1.2/spring-beans/src/main/java/org/springframework/beans/factory/config/DependencyDescriptor.java#L356
ここでパラメータ名を解決するわけだが、
参照している parameterNameDiscoverer
が
KotlinReflectionParameterNameDiscoverer
StandardReflectionParameterNameDiscoverer
LocalVariableTableParameterNameDiscoverer
の順番でパラメータ名の解決を行うようになっていたのが、削除されたことによって失敗するようになっていた
(元のバージョンでも上2つのdiscovererは最終的にnullを返していたので、そのままでは解決できなくなった)
https://github.com/spring-projects/spring-framework/blob/v6.1.2/spring-core/src/main/java/org/springframework/core/MethodParameter.java#L714
で、困ったことに自分たちのコードはKotlinで書いているが、
外部ライブラリのstarterがJavaで書かれており、wikiの対応がまだされていなかったので、自分たちのSpringBootのバージョンをあげたら壊れてしまったという話だった
(自分たちのコードで、Kotlin内で同じ型のBeanが複数構成されていた場合の名前による解決はうまく動いたので、少なくともこのパターンだけは対応しないとうまく動かないとかな気がする)
↓はPoC
jmodule
は外部ライブラリをイメージして書いていて、
src
配下は自分たちのコードをイメージして書いている
org.springframework.boot
が 3.1.7
であれば
jmodule
側の build.gradle
に↓を書かなくても問題なく動くが、
tasks.withType(JavaCompile).configureEach {
options.compilerArgs.add("-parameters")
}
3.2.1
など spring-core
が 6.1
系になると失敗するようになる
最初出会したときは、自分たちの build.gradle.kts
だけ対応すればいけるのかと思っていたが、
そんなことはなかったのでバージョンを上げる際は注意したほうが良さそう
一応コンパイルオプションつけたときの ResolverAutoConfiguration.java
のclassファイルの違いもみてみた
無効
public class org.example.resolver.ResolverAutoConfiguration minor version: 0 major version: 61 flags: (0x0021) ACC_PUBLIC, ACC_SUPER this_class: #12 // org/example/resolver/ResolverAutoConfiguration super_class: #2 // java/lang/Object interfaces: 0, fields: 0, methods: 2, attributes: 2 Constant pool: #1 = Methodref #2.#3 // java/lang/Object."<init>":()V #2 = Class #4 // java/lang/Object #3 = NameAndType #5:#6 // "<init>":()V #4 = Utf8 java/lang/Object #5 = Utf8 <init> #6 = Utf8 ()V #7 = Class #8 // org/example/resolver/Resolver #8 = Utf8 org/example/resolver/Resolver #9 = Methodref #7.#10 // org/example/resolver/Resolver."<init>":(Lorg/example/resolver/ParameterNameResolver;)V #10 = NameAndType #5:#11 // "<init>":(Lorg/example/resolver/ParameterNameResolver;)V #11 = Utf8 (Lorg/example/resolver/ParameterNameResolver;)V #12 = Class #13 // org/example/resolver/ResolverAutoConfiguration #13 = Utf8 org/example/resolver/ResolverAutoConfiguration #14 = Utf8 Code #15 = Utf8 LineNumberTable #16 = Utf8 LocalVariableTable #17 = Utf8 this #18 = Utf8 Lorg/example/resolver/ResolverAutoConfiguration; #19 = Utf8 resolver #20 = Utf8 (Lorg/example/resolver/ParameterNameResolver;)Lorg/example/resolver/Resolver; #21 = Utf8 parameterNameResolver #22 = Utf8 Lorg/example/resolver/ParameterNameResolver; #23 = Utf8 RuntimeVisibleAnnotations #24 = Utf8 Lorg/springframework/context/annotation/Bean; #25 = Utf8 SourceFile #26 = Utf8 ResolverAutoConfiguration.java #27 = Utf8 Lorg/springframework/context/annotation/Configuration; { public org.example.resolver.ResolverAutoConfiguration(); descriptor: ()V flags: (0x0001) ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return LineNumberTable: line 7: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this Lorg/example/resolver/ResolverAutoConfiguration; public org.example.resolver.Resolver resolver(org.example.resolver.ParameterNameResolver); descriptor: (Lorg/example/resolver/ParameterNameResolver;)Lorg/example/resolver/Resolver; flags: (0x0001) ACC_PUBLIC Code: stack=3, locals=2, args_size=2 0: new #7 // class org/example/resolver/Resolver 3: dup 4: aload_1 5: invokespecial #9 // Method org/example/resolver/Resolver."<init>":(Lorg/example/resolver/ParameterNameResolver;)V 8: areturn LineNumberTable: line 10: 0 LocalVariableTable: Start Length Slot Name Signature 0 9 0 this Lorg/example/resolver/ResolverAutoConfiguration; 0 9 1 parameterNameResolver Lorg/example/resolver/ParameterNameResolver; RuntimeVisibleAnnotations: 0: #24() org.springframework.context.annotation.Bean } SourceFile: "ResolverAutoConfiguration.java" RuntimeVisibleAnnotations: 0: #27() org.springframework.context.annotation.Configuration
有効
public class org.example.resolver.ResolverAutoConfiguration minor version: 0 major version: 61 flags: (0x0021) ACC_PUBLIC, ACC_SUPER this_class: #12 // org/example/resolver/ResolverAutoConfiguration super_class: #2 // java/lang/Object interfaces: 0, fields: 0, methods: 2, attributes: 2 Constant pool: #1 = Methodref #2.#3 // java/lang/Object."<init>":()V #2 = Class #4 // java/lang/Object #3 = NameAndType #5:#6 // "<init>":()V #4 = Utf8 java/lang/Object #5 = Utf8 <init> #6 = Utf8 ()V #7 = Class #8 // org/example/resolver/Resolver #8 = Utf8 org/example/resolver/Resolver #9 = Methodref #7.#10 // org/example/resolver/Resolver."<init>":(Lorg/example/resolver/ParameterNameResolver;)V #10 = NameAndType #5:#11 // "<init>":(Lorg/example/resolver/ParameterNameResolver;)V #11 = Utf8 (Lorg/example/resolver/ParameterNameResolver;)V #12 = Class #13 // org/example/resolver/ResolverAutoConfiguration #13 = Utf8 org/example/resolver/ResolverAutoConfiguration #14 = Utf8 Code #15 = Utf8 LineNumberTable #16 = Utf8 LocalVariableTable #17 = Utf8 this #18 = Utf8 Lorg/example/resolver/ResolverAutoConfiguration; #19 = Utf8 resolver #20 = Utf8 (Lorg/example/resolver/ParameterNameResolver;)Lorg/example/resolver/Resolver; #21 = Utf8 parameterNameResolver #22 = Utf8 Lorg/example/resolver/ParameterNameResolver; #23 = Utf8 MethodParameters #24 = Utf8 RuntimeVisibleAnnotations #25 = Utf8 Lorg/springframework/context/annotation/Bean; #26 = Utf8 SourceFile #27 = Utf8 ResolverAutoConfiguration.java #28 = Utf8 Lorg/springframework/context/annotation/Configuration; { public org.example.resolver.ResolverAutoConfiguration(); descriptor: ()V flags: (0x0001) ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return LineNumberTable: line 7: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this Lorg/example/resolver/ResolverAutoConfiguration; public org.example.resolver.Resolver resolver(org.example.resolver.ParameterNameResolver); descriptor: (Lorg/example/resolver/ParameterNameResolver;)Lorg/example/resolver/Resolver; flags: (0x0001) ACC_PUBLIC Code: stack=3, locals=2, args_size=2 0: new #7 // class org/example/resolver/Resolver 3: dup 4: aload_1 5: invokespecial #9 // Method org/example/resolver/Resolver."<init>":(Lorg/example/resolver/ParameterNameResolver;)V 8: areturn LineNumberTable: line 10: 0 LocalVariableTable: Start Length Slot Name Signature 0 9 0 this Lorg/example/resolver/ResolverAutoConfiguration; 0 9 1 parameterNameResolver Lorg/example/resolver/ParameterNameResolver; MethodParameters: Name Flags parameterNameResolver RuntimeVisibleAnnotations: 0: #25() org.springframework.context.annotation.Bean } SourceFile: "ResolverAutoConfiguration.java" RuntimeVisibleAnnotations: 0: #28() org.springframework.context.annotation.Configuration
MethodParameters
を見れば分かる通り、オプションによって有無の違いが見て取れる