Spring Bean Injection 방식이 바뀌었다?

겔로그·2024년 4월 21일
0

이 글은 Spring Bean Injection 이야기(feat. 모두가 다 알고 있는 스프링빈, 정말 다 알고 있는가?) 를 읽고 발생한 의문점을 해결하기 위해 찾아본 내용을 정리한 글입니다.

Spring Bean Injection 6줄 요약

앞서 공유드린 카카오페이 테크 블로그의 글을 인용하면 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을 주입해줬습니다.

Injection이 Spring 6.1 이후로는 변한다?

spring framework 6.1 issue 를 참고하면 다음과 같은 내용으로 PR이 머지된 것을 확인할 수 있습니다.

또한 이는 6.1 릴리스 노트를 통해 적용이 된 것을 알 수 있습니다.

주요 내용은 다음과 같습니다.

LocalVariableTableParameterNameDiscover는 6.1 부터 사라지며, 스프링 프레임워크는 더이상 바이트코드를 파싱해 파라미터명을 추론하지 않습니다.

동일 타입의 빈이 2개 이상 있을경우 명칭으로 추론하는 과정이 기본적으로 제공되던 내용이 앞으로는 제공되지 않음을 의미합니다.

해당 내용을 동일하게 사용하기 위해서는 -parameters 옵션을 true로 활성화 해야 합니다.

그런데... 왜 난 몰랐지?

하지만 6.1 전후의 버전을 사용하더라도 사용에 있어 큰 변화를 느끼지 못했습니다.
분명 parameter 옵션을 true로 설정하지 않는다면 주입이 안되어야 할텐데... 이유가 뭘까요?

Autowiring by parameter names 사용 요구 사항

우선... Autowiring by parameter names를 사용하려면, Spring Framework 4.3 이상 버전이 필요하며, Java 컴파일러의 -parameters 옵션이 활성화되어 있어야 합니다. 이 옵션은 메서드 매개변수의 이름을 .class 파일에 보존하도록 하여, 런타임에 매개변수 이름을 사용할 수 있게 해줍니다. 이 옵션을 활성화하지 않으면, Spring Framework는 매개변수 이름을 알 수 없어, Autowiring by parameter names를 사용할 수 없습니다.

maven-compiler-plugin Option

maven-compiler-plugin 페이지를 가면 다음과 같은 내용을 확인하실 수 있습니다.

parameters 값을 정의하지 않으면 false라는 것을 알 수 있습니다.

하지만... build하면?

[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

원인

지금껏 변경되었음에도 불구하고 동일하게 사용이 가능했던 이유는 다음 이슈를 통해 확인이 가능했습니다.

결론

  • Spring Framework의 Spring Injection 방식이 6.1 전후로 다르다.
  • 6.1 이후로는 파라미터 명으로 빈 추론시 maven-compile-plugin을 통해 parameters 옵션을 true로 활성화 해야 한다.
  • spring boot starter를 사용하고 있는 경우 maven-compile-plugin가 기본적으로 true 옵션으로 설정되어 있어 이를 상속해 사용하기 때문에 변경 사항이 시스템에 큰 영향을 미치지 못한다.

Reference

profile
Gelog 나쁜 것만 드려요~

0개의 댓글