이 글은 Spring Bean Injection 이야기(feat. 모두가 다 알고 있는 스프링빈, 정말 다 알고 있는가?) 를 읽고 발생한 의문점을 해결하기 위해 찾아본 내용을 정리한 글입니다.
앞서 공유드린 카카오페이 테크 블로그의 글을 인용하면 Spring Bean Injection은 다음과 같이 동작합니다.
- @Bean 으로 bean을 생성하게 되면, method name이 bean name으로 생성된다.
- 같은 Type의 bean이 1개만 있다면, bean name과 관련없이 bean을 주입해준다.
- 같은 Type의 bean이 여러 개 있으면, @Qualifier가 없어도 bean name과 field name을 매칭해서 bean을 주입해준다.
- @Primary가 있으면, bean name을 무시하고 Type 기반으로 Primary인 Bean을 주입한다.
- @Qualifier가 있으면, 무조건 bean name 기준으로 주입해준다. (없으면 오류가 발생한다)
- @Qualifier 어노테이션이 @Primary 어노테이션보다 우선하여 적용된다.
기존에는 Spring IoC에서 같은 Type의 Bean이 1개만 있다면 bean name과 관련없이 주입해주며, 2개 이상일 경우 bean name과 field name을 매칭해서 bean을 주입해줬습니다.
spring framework 6.1 issue 를 참고하면 다음과 같은 내용으로 PR이 머지된 것을 확인할 수 있습니다.
또한 이는 6.1 릴리스 노트를 통해 적용이 된 것을 알 수 있습니다.
주요 내용은 다음과 같습니다.
LocalVariableTableParameterNameDiscover는 6.1 부터 사라지며, 스프링 프레임워크는 더이상 바이트코드를 파싱해 파라미터명을 추론하지 않습니다.
동일 타입의 빈이 2개 이상 있을경우 명칭으로 추론하는 과정이 기본적으로 제공되던 내용이 앞으로는 제공되지 않음을 의미합니다.
해당 내용을 동일하게 사용하기 위해서는 -parameters
옵션을 true로 활성화 해야 합니다.
하지만 6.1 전후의 버전을 사용하더라도 사용에 있어 큰 변화를 느끼지 못했습니다.
분명 parameter 옵션을 true로 설정하지 않는다면 주입이 안되어야 할텐데... 이유가 뭘까요?
우선... Autowiring by parameter names를 사용하려면, Spring Framework 4.3 이상 버전이 필요하며, Java 컴파일러의 -parameters 옵션이 활성화되어 있어야 합니다. 이 옵션은 메서드 매개변수의 이름을 .class 파일에 보존하도록 하여, 런타임에 매개변수 이름을 사용할 수 있게 해줍니다. 이 옵션을 활성화하지 않으면, Spring Framework는 매개변수 이름을 알 수 없어, Autowiring by parameter names를 사용할 수 없습니다.
maven-compiler-plugin 페이지를 가면 다음과 같은 내용을 확인하실 수 있습니다.
parameters 값을 정의하지 않으면 false라는 것을 알 수 있습니다.
[DEBUG] Created new class realm plugin>org.apache.maven.plugins:```maven-compiler-plugin:3.8.0```
[DEBUG] Importing foreign packages into class realm plugin>org.apache.maven.plugins:maven-compiler-plugin:3.8.0
[DEBUG] Imported: < maven.api
[DEBUG] Populating class realm plugin>org.apache.maven.plugins:maven-compiler-plugin:3.8.0
[DEBUG] Included: org.apache.maven.plugins:maven-compiler-plugin:jar:3.8.0
[DEBUG] Included: org.sonatype.sisu:sisu-inject-bean:jar:1.4.2
[DEBUG] Included: org.sonatype.sisu:sisu-guice:jar:noaop:2.1.7
[DEBUG] Included: org.codehaus.plexus:plexus-utils:jar:2.0.4
[DEBUG] Included: org.sonatype.aether:aether-util:jar:1.7
[DEBUG] Included: org.codehaus.plexus:plexus-interpolation:jar:1.14
[DEBUG] Included: org.codehaus.plexus:plexus-component-annotations:jar:1.7.1
[DEBUG] Included: org.sonatype.plexus:plexus-sec-dispatcher:jar:1.3
[DEBUG] Included: org.sonatype.plexus:plexus-cipher:jar:1.4
[DEBUG] Included: org.apache.maven.shared:maven-shared-utils:jar:3.2.1
[DEBUG] Included: commons-io:commons-io:jar:2.5
[DEBUG] Included: org.apache.maven.shared:maven-shared-incremental:jar:1.1
[DEBUG] Included: org.codehaus.plexus:plexus-java:jar:0.9.10
[DEBUG] Included: org.ow2.asm:asm:jar:6.2
[DEBUG] Included: com.thoughtworks.qdox:qdox:jar:2.0-M9
[DEBUG] Included: org.codehaus.plexus:plexus-compiler-api:jar:2.8.4
[DEBUG] Included: org.codehaus.plexus:plexus-compiler-manager:jar:2.8.4
[DEBUG] Included: org.codehaus.plexus:plexus-compiler-javac:jar:2.8.4
[DEBUG] Configuring mojo org.apache.maven.plugins:maven-compiler-plugin:3.8.0:compile from plugin realm ClassRealm[plugin>org.apache.maven.plugins:maven-compiler-plugin:3.8.0, parent: jdk.internal.loader.ClassLoaders$AppClassLoader@16f65612]
[DEBUG] Configuring mojo 'org.apache.maven.plugins:maven-compiler-plugin:3.8.0:compile' with basic configurator -->
[DEBUG] (s) groupId = org.mapstruct
[DEBUG] (s) artifactId = mapstruct-processor
[DEBUG] (s) version = 1.3.0.Final
[DEBUG] (s) groupId = org.projectlombok
[DEBUG] (s) artifactId = lombok
[DEBUG] (s) version = 1.18.0
[DEBUG] (f) annotationProcessorPaths = [org.mapstruct:mapstruct-processor:1.3.0.Final.jar, org.projectlombok:lombok:1.18.0.jar]
[DEBUG] (f) basedir = /Users/scott/IdeaProjects/test
[DEBUG] (f) buildDirectory = /Users/scott/IdeaProjects/test/target
[DEBUG] (f) compilePath = [/Users/scott/IdeaProjects/test/target/classes, /Users/scott/.m2/repository/org/springframework/boot/spring-boot-starter/2.7.9/spring-boot-starter-2.7.9.jar, /Users/scott/.m2/repository/org/springframework/boot/spring-boot/2.7.9/spring-boot-2.7.9.jar, /Users/scott/.m2/repository/org/springframework/boot/spring-boot-autoconfigure/2.7.9/spring-boot-autoconfigure-2.7.9.jar, /Users/scott/.m2/repository/org/springframework/boot/spring-boot-starter-logging/2.7.9/spring-boot-starter-logging-2.7.9.jar, /Users/scott/.m2/repository/org/apache/logging/log4j/log4j-to-slf4j/2.17.2/log4j-to-slf4j-2.17.2.jar, /Users/scott/.m2/repository/org/slf4j/jul-to-slf4j/1.7.36/jul-to-slf4j-1.7.36.jar, /Users/scott/.m2/repository/jakarta/annotation/jakarta.annotation-api/1.3.5/jakarta.annotation-api-1.3.5.jar, /Users/scott/.m2/repository/org/springframework/spring-core/5.3.25/spring-core-5.3.25.jar, /Users/scott/.m2/repository/org/springframework/spring-jcl/5.3.25/spring-jcl-5.3.25.jar, /Users/scott/.m2/repository/org/yaml/snakeyaml/1.30/snakeyaml-1.30.jar, /Users/scott/.m2/repository/org/springframework/boot/spring-boot-starter-tomcat/2.7.9/spring-boot-starter-tomcat-2.7.9.jar,
...
[DEBUG] (f) compileSourceRoots = [/Users/scott/IdeaProjects/test/src/main/java]
[DEBUG] (f) compilerId = javac
[DEBUG] (f) debug = true
[DEBUG] (f) encoding = UTF-8
[DEBUG] (f) failOnError = true
[DEBUG] (f) failOnWarning = false
[DEBUG] (f) forceJavacCompilerUse = false
[DEBUG] (f) fork = false
[DEBUG] (f) generatedSourcesDirectory = /Users/scott/IdeaProjects/test/target/generated-sources/annotations
[DEBUG] (f) mojoExecution = org.apache.maven.plugins:maven-compiler-plugin:3.8.0:compile {execution: default-compile}
[DEBUG] (f) optimize = false
[DEBUG] (f) outputDirectory = /Users/scott/IdeaProjects/test/target/classes
[DEBUG] (f) ```parameters = true```
[DEBUG] (f) project = MavenProject: test:0.0.1-SNAPSHOT @ /Users/scott/IdeaProjects/test/pom.xml
[DEBUG] (f) projectArtifact = test:jar:0.0.1-SNAPSHOT
[DEBUG] (f) session = org.apache.maven.execution.MavenSession@6535117e
[DEBUG] (f) showDeprecation = false
[DEBUG] (f) showWarnings = false
[DEBUG] (f) skipMultiThreadWarning = false
[DEBUG] (f) source = 11
[DEBUG] (f) staleMillis = 0
[DEBUG] (s) target = 11
[DEBUG] (f) useIncrementalCompilation = true
[DEBUG] (f) verbose = false
[DEBUG] -- end configuration --
다음의 내용이 눈에 띕니다..
DEBUG
parameters = true
지금껏 변경되었음에도 불구하고 동일하게 사용이 가능했던 이유는 다음 이슈를 통해 확인이 가능했습니다.
parameters
옵션을 기본적으로 true로 설정하여 spring boot starter 의존성을 프로젝트에 설정할 경우 기존과 동일하게 사용할 수 있던 것이었습니다.