Classpath Scanning and Managed Components(한글)

이 장의 대부분의 예제에서는 XML을 사용하여 Spring 컨테이너 내에서 각 BeanDefinition 을 생성하는 구성 메타데이터를 지정합니다. 이전 섹션(주석 기반 컨테이너 구성)에서는 소스 수준 주석을 통해 많은 구성 메타데이터를 제공하는 방법을 보여줬습니다. 그러나 이러한 예시에서도 "기본" Bean 정의는 XML 파일에 명시적으로 정의되어 있고, 어노테이션은 종속성 주입만 구동합니다. 이 섹션에서는 클래스 경로를 스캔하여 후보 컴포넌트를 암시적으로 감지하는 옵션에 대해 설명합니다. 후보 컴포넌트는 필터 기준과 일치하고 컨테이너에 등록된 해당 Bean 정의가 있는 클래스입니다. 이렇게 하면 XML을 사용하여 Bean 등록을 수행할 필요가 없습니다. 대신 어노테이션(예: @Component), AspectJ 유형 표현식 또는 사용자 정의 필터 기준을 사용하여 컨테이너에 등록된 Bean 정의가 있는 클래스를 선택할 수 있습니다.

XML 파일 대신 Java를 사용하여 Bean을 정의할 수 있습니다. 이러한 기능을 사용하는 방법에 대한 예는 @Configuration, @Bean, @Import@DependsOn 어노테이션을 참조하세요.

@Component 및 추가 스테레오타입 어노테이션(@Component and Further Stereotype Annotations)

@Repository 어노테이션은 리포지토리(데이터 액세스 객체 또는 DAO라고도 함)의 역할을 수행하는 모든 클래스에 대한 마커입니다. 이 마커의 용도 중에는 Exception Translation에 설명된 대로 예외의 자동 번역이 있습니다.

Spring은 추가적인 스테레오타입 주석을 제공합니다: @Component, @Service, @Controller. @Component 는 Spring이 관리하는 모든 컴포넌트에 대한 일반적인 스테레오타입입니다. @Repository, @Service, @Controller 는 각각 지속성, 서비스, 프레젠테이션 레이어에서 보다 구체적인 사용 사례를 위한 @Component`의 전문화입니다. 따라서 컴포넌트 클래스에 `@Component`로 주석을 달 수 있지만, 대신 `@Repository, @Service 또는 @Controller`로 주석을 달면 도구로 처리하거나 aspects와 연결하는 데 더 적합하게 클래스를 사용할 수 있습니다. 예를 들어, 이러한 스테레오타입 어노테이션은 포인트컷의 이상적인 대상이 됩니다. `@Repository, @Service, @Controller`는 Spring 프레임워크의 향후 릴리스에서 추가적인 의미를 가질 수도 있습니다. 따라서 서비스 레이어에 `@Component`와 `@Service 중 하나를 선택해야 한다면 @Service 가 더 나은 선택입니다. 마찬가지로, 앞서 설명한 것처럼 @Repository 는 이미 지속성 계층에서 자동 예외 변환을 위한 마커로 지원되고 있습니다.

Using Meta-annotations and Composed Annotations

Spring에서 제공하는 많은 어노테이션을 자신의 코드에서 메타 어노테이션으로 사용할 수 있습니다. 메타 어노테이션은 다른 어노테이션에 적용할 수 있는 어노테이션입니다. 예를 들어, 다음 예제에서 볼 수 있듯이 이전에서 언급한 @Service 어노테이션은 @Component 로 메타 어노테이션이 되어 있습니다:

  • Java

  • Kotlin

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component (1)
public @interface Service {

	// ...
}
1 @ Component@ Service@ Component 와 같은 방식으로 취급하게 합니다.
@Target(AnnotationTarget.TYPE)
@Retention(AnnotationRetention.RUNTIME)
@MustBeDocumented
@Component (1)
annotation class Service {

	// ...
}
1 The @Component causes @Service to be treated in the same way as @Component.

메타 어노테이션을 결합하여 "`구성된 어노테이션`"을 만들 수도 있습니다. 예를 들어 Spring MVC의 @RestController 어노테이션은 @Controller@ResponseBody 로 구성됩니다.

또한 구성된 어노테이션은 선택적으로 메타 어노테이션에서 어트리뷰트를 다시 선언하여 사용자 정의할 수 있습니다. 이는 메타 어노테이션 속성의 하위 집합만 노출하려는 경우에 특히 유용할 수 있습니다. 예를 들어 Spring의 @SessionScope 어노테이션은 범위 이름을 session`으로 하드코딩하지만 여전히 `proxyMode 의 사용자 정의는 허용합니다. 다음 목록은 SessionScope 어노테이션의 정의를 보여줍니다:

  • Java

  • Kotlin

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Scope(WebApplicationContext.SCOPE_SESSION)
public @interface SessionScope {

	/**
	 * Alias for {@link Scope#proxyMode}.
	 * <p>Defaults to {@link ScopedProxyMode#TARGET_CLASS}.
	 */
	@AliasFor(annotation = Scope.class)
	ScopedProxyMode proxyMode() default ScopedProxyMode.TARGET_CLASS;

}
@Target(AnnotationTarget.TYPE, AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
@MustBeDocumented
@Scope(WebApplicationContext.SCOPE_SESSION)
annotation class SessionScope(
		@get:AliasFor(annotation = Scope::class)
		val proxyMode: ScopedProxyMode = ScopedProxyMode.TARGET_CLASS
)

그런 다음 다음과 같이 proxyMode 를 선언하지 않고 @SessionScope 를 사용할 수 있습니다:

  • Java

  • Kotlin

@Service
@SessionScope
public class SessionScopedService {
	// ...
}
@Service
@SessionScope
class SessionScopedService {
	// ...
}

다음 예시와 같이 proxyMode 의 값을 재정의할 수도 있습니다:

  • Java

  • Kotlin

@Service
@SessionScope(proxyMode = ScopedProxyMode.INTERFACES)
public class SessionScopedUserService implements UserService {
	// ...
}
@Service
@SessionScope(proxyMode = ScopedProxyMode.INTERFACES)
class SessionScopedUserService : UserService {
	// ...
}

자세한 내용은 {스프링 프레임워크 위키}/Spring-Annotation-Programming-Model[Spring Annotation Programming Model] 위키 페이지를 참조하세요.

클래스 자동 감지 및 Bean 정의 등록하기(Automatically Detecting Classes and Registering Bean Definitions)

Spring은 정형화된 클래스를 자동으로 감지하고 그에 해당하는 BeanDefinition 인스턴스를 ApplicationContext 에 등록할 수 있습니다. 예를 들어, 다음 두 클래스는 이러한 자동 감지를 사용할 수 있습니다:

  • Java

  • Kotlin

@Service
public class SimpleMovieLister {

	private MovieFinder movieFinder;

	public SimpleMovieLister(MovieFinder movieFinder) {
		this.movieFinder = movieFinder;
	}
}
@Service
class SimpleMovieLister(private val movieFinder: MovieFinder)
  • Java

  • Kotlin

@Repository
public class JpaMovieFinder implements MovieFinder {
	// implementation elided for clarity
}
@Repository
class JpaMovieFinder : MovieFinder {
	// implementation elided for clarity
}

이러한 클래스를 자동 감지하고 해당 Bean을 등록하려면 @Configuration 클래스에 @ComponentScan 을 추가해야 하며, 여기서 basePackages 속성은 두 클래스의 공통 부모 패키지입니다. (또는 각 클래스의 부모 패키지를 포함하는 쉼표, 세미콜론 또는 공백으로 구분된 목록을 지정할 수 있습니다.)

  • Java

  • Kotlin

@Configuration
@ComponentScan(basePackages = "org.example")
public class AppConfig  {
	// ...
}
@Configuration
@ComponentScan(basePackages = ["org.example"])
class AppConfig  {
	// ...
}
간결성을 위해 앞의 예제에서는 어노테이션의 value 속성(즉, @ComponentScan("org.example") 을 사용했을 수 있습니다.)

다음 대안은 XML을 사용합니다:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
		https://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context
		https://www.springframework.org/schema/context/spring-context.xsd">

	<context:component-scan base-package="org.example"/>

</beans>
<context:component-scan> 을 사용하면 <context:annotation-config> 의 기능이 암시적으로 활성화됩니다. 일반적으로 <context:component-scan> 을 사용할 때는 <context:annotation-config> 요소를 포함할 필요가 없습니다.

클래스 경로 패키지를 검사하려면 클래스 경로에 해당 디렉터리 항목이 있어야 합니다. Ant로 JAR을 빌드할 때 JAR 작업의 파일 전용 스위치를 활성화하지 않았는지 확인하세요. 또한 일부 환경에서는 보안 정책에 따라 클래스 경로 디렉터리가 노출되지 않을 수 있습니다. — 예를 들어 JDK 1.7.0_45 이상의 독립 실행형 앱(매니페스트에서 'Trusted-Library' 설정이 필요함) — 스택오버플로우-질문}/19394570/java-jre-7u45-breaks-classloader-getresources 참조).

JDK 9의 모듈 경로(Jigsaw)에서 Spring의 클래스 경로 검색은 일반적으로 예상대로 작동합니다. 그러나 컴포넌트 클래스가 module-info 디스크립터에 내보내져 있는지 확인하세요. Spring이 클래스의 비공개 멤버를 호출할 것으로 예상되는 경우, 해당 클래스가 '열린' 상태인지(즉, module-info 설명자에서 exports 선언 대신 opens 선언을 사용하는지) 확인해야 합니다.

또한, component-scan 요소를 사용할 때 AutowiredAnnotationBeanPostProcessorCommonAnnotationBeanPostProcessor 가 모두 암시적으로 포함됩니다. 즉, 두 구성 요소는 XML로 제공된 Bean 구성 메타데이터 없이도 자동 감지되어 함께 연결됩니다. NOTE: 값이 falseannotation-config 속성을 포함하면 AutowiredAnnotationBeanPostProcessorCommonAnnotationBeanPostProcessor 의 등록을 비활성화할 수 있습니다.

필터를 사용하여 스캔 사용자 지정(Using Filters to Customize Scanning)

기본적으로 @Component, @Repository, @Service, @Controller, @Configuration`으로 주석이 달린 클래스 또는 `@Component`로 주석이 달린 사용자 정의 어노테이션만 감지되는 후보 컴포넌트입니다. 그러나 사용자 정의 필터를 적용하여 이 동작을 수정하고 확장할 수 있습니다. 사용자 정의 필터를 `@ComponentScan 어노테이션의 includeFilters 또는 excludeFilters 속성으로 추가합니다(또는 XML 구성에서 <context:component-scan> 요소의 <context:include-filter /> 또는 <context:exclude-filter /> 하위 요소로 추가합니다). 각 필터 요소에는 typeexpression 속성이 필요합니다. 다음 표에서는 필터링 옵션에 대해 설명합니다:

Table 1. Filter Types
Filter Type Example Expression Description

annotation (default)

org.example.SomeAnnotation

대상 컴포넌트의 유형 수준에서 present 또는 _meta-present_가 될 어노테이션입니다.

assignable

org.example.SomeClass

대상 컴포넌트가 할당(확장 또는 구현)할 수 있는 클래스(또는 인터페이스)입니다.

aspectj

org.example..*Service+

타깃 컴포넌트에 의해 일치시킬 AspectJ 타입 표현식입니다.

regex

org\.example\.Default.*

대상 컴포넌트의 클래스 이름과 일치시킬 정규식 표현식입니다.

custom

org.example.MyTypeFilter

org.springframework.core.type.TypeFilter 인터페이스의 사용자 정의 구현입니다.

다음 예는 모든 @Repository 어노테이션을 무시하고 대신 “stub” 리포지토리를 사용하는 구성을 보여줍니다:

  • Java

  • Kotlin

@Configuration
@ComponentScan(basePackages = "org.example",
		includeFilters = @Filter(type = FilterType.REGEX, pattern = ".*Stub.*Repository"),
		excludeFilters = @Filter(Repository.class))
public class AppConfig {
	// ...
}
@Configuration
@ComponentScan(basePackages = ["org.example"],
		includeFilters = [Filter(type = FilterType.REGEX, pattern = [".*Stub.*Repository"])],
		excludeFilters = [Filter(Repository::class)])
class AppConfig {
	// ...
}

다음 목록은 이에 해당하는 XML을 보여줍니다:

<beans>
	<context:component-scan base-package="org.example">
		<context:include-filter type="regex"
				expression=".*Stub.*Repository"/>
		<context:exclude-filter type="annotation"
				expression="org.springframework.stereotype.Repository"/>
	</context:component-scan>
</beans>
어노테이션에 useDefaultFilters=false`를 설정하거나 `<component-scan/> 요소의 속성으로 use-default-filters="false" 를 제공하여 기본 필터를 비활성화할 수도 있습니다. 이렇게 하면 @Component, @Repository, @Service, @Controller, @RestController 또는 @Configuration 으로 어노테이션되거나 메타 어노테이션된 클래스의 자동 감지를 효과적으로 비활성화할 수 있습니다.

컴포넌트 내에서 Bean 메타데이터 정의하기

Spring 컴포넌트는 컨테이너에 Bean 정의 메타데이터를 제공할 수도 있습니다. 이 작업은 @Configuration 어노테이션 클래스 내에서 Bean 메타데이터를 정의하는 데 사용되는 것과 동일한 @Bean 어노테이션을 사용하여 수행할 수 있습니다. 다음 예제는 그 방법을 보여줍니다:

  • Java

  • Kotlin

@Component
public class FactoryMethodComponent {

	@Bean
	@Qualifier("public")
	public TestBean publicInstance() {
		return new TestBean("publicInstance");
	}

	public void doWork() {
		// Component method implementation omitted
	}
}
@Component
class FactoryMethodComponent {

	@Bean
	@Qualifier("public")
	fun publicInstance() = TestBean("publicInstance")

	fun doWork() {
		// Component method implementation omitted
	}
}

앞의 클래스는 doWork() 메서드에 애플리케이션별 코드가 있는 Spring 컴포넌트입니다. 그러나 이 컴포넌트는 publicInstance() 메서드를 참조하는 팩토리 메서드가 있는 Bean 정의도 제공합니다. '@Bean` 어노테이션은 팩토리 메서드와 @Qualifier 어노테이션을 통해 한정자 값과 같은 기타 Bean 정의 속성을 식별합니다. 지정할 수 있는 다른 메서드 수준 어노테이션으로는 @Scope, @Lazy 및 사용자 지정 한정자 어노테이션이 있습니다.

컴포넌트 초기화를 위한 역할 외에도 @Autowired 또는 @Inject 로 표시된 주입 지점에 @Lazy 어노테이션을 배치할 수도 있습니다. 이러한 맥락에서는 지연 해결 프록시를 주입하게 됩니다. 그러나 이러한 프록시 접근 방식은 다소 제한적입니다. 특히 선택적 종속성과 결합된 정교한 지연 상호 작용의 경우, 대신 ObjectProvider<MyTargetBean> 을 사용하는 것이 좋습니다.

앞서 설명한 대로 자동 와이어링된 필드와 메서드는 지원되며, @Bean 메서드의 autowiring도 추가로 지원됩니다. 다음 예제에서는 이를 수행하는 방법을 보여줍니다:

  • Java

  • Kotlin

@Component
public class FactoryMethodComponent {

	private static int i;

	@Bean
	@Qualifier("public")
	public TestBean publicInstance() {
		return new TestBean("publicInstance");
	}

	// use of a custom qualifier and autowiring of method parameters
	@Bean
	protected TestBean protectedInstance(
			@Qualifier("public") TestBean spouse,
			@Value("#{privateInstance.age}") String country) {
		TestBean tb = new TestBean("protectedInstance", 1);
		tb.setSpouse(spouse);
		tb.setCountry(country);
		return tb;
	}

	@Bean
	private TestBean privateInstance() {
		return new TestBean("privateInstance", i++);
	}

	@Bean
	@RequestScope
	public TestBean requestScopedInstance() {
		return new TestBean("requestScopedInstance", 3);
	}
}
@Component
class FactoryMethodComponent {

	companion object {
		private var i: Int = 0
	}

	@Bean
	@Qualifier("public")
	fun publicInstance() = TestBean("publicInstance")

	// use of a custom qualifier and autowiring of method parameters
	@Bean
	protected fun protectedInstance(
			@Qualifier("public") spouse: TestBean,
			@Value("#{privateInstance.age}") country: String) = TestBean("protectedInstance", 1).apply {
		this.spouse = spouse
		this.country = country
	}

	@Bean
	private fun privateInstance() = TestBean("privateInstance", i++)

	@Bean
	@RequestScope
	fun requestScopedInstance() = TestBean("requestScopedInstance", 3)
}

이 예제에서는 String 메서드 매개변수 country`를 `privateInstance 라는 다른 Bean의 age 속성 값에 autowiring합니다. Spring 표현식 언어 요소는 #{ <표현식> } 표기법을 통해 속성 값을 정의합니다. @Value 어노테이션의 경우, 표현식 해석기는 표현식 텍스트를 해석할 때 Bean 이름을 찾도록 미리 구성되어 있습니다.

Spring 프레임워크 4.3부터는 InjectionPoint (또는 보다 구체적인 하위 클래스인 DependencyDescriptor) 유형의 팩토리 메서드 파라미터를 선언하여 현재 Bean의 생성을 트리거하는 요청하는 주입 지점에 액세스할 수도 있습니다. 이는 기존 인스턴스의 주입이 아닌 실제 Bean 인스턴스 생성에만 적용된다는 점에 유의하세요. 따라서 이 기능은 프로토타입 범위의 Bean에 가장 적합합니다. 다른 범위의 경우, 팩토리 메서드는 주어진 범위에서 새 Bean 인스턴스 생성을 트리거한 주입 지점(예: 지연 싱글톤 Bean 생성을 트리거한 종속성)만 볼 수 있습니다. 이러한 시나리오에서는 제공된 주입 지점 메타데이터를 의미론적으로 주의하여 사용할 수 있습니다. 다음 예제는 InjectionPoint 를 사용하는 방법을 보여줍니다:

  • Java

  • Kotlin

@Component
public class FactoryMethodComponent {

	@Bean @Scope("prototype")
	public TestBean prototypeInstance(InjectionPoint injectionPoint) {
		return new TestBean("prototypeInstance for " + injectionPoint.getMember());
	}
}
@Component
class FactoryMethodComponent {

	@Bean
	@Scope("prototype")
	fun prototypeInstance(injectionPoint: InjectionPoint) =
			TestBean("prototypeInstance for ${injectionPoint.member}")
}

일반 Spring 컴포넌트의 @Bean 메서드는 Spring @Configuration 클래스 내부의 메서드와는 다르게 처리됩니다. 차이점은 @Component 클래스가 메서드와 필드 호출을 가로채기 위해 CGLIB로 향상되지 않았다는 점입니다. CGLIB 프록시는 @Configuration 클래스의 @Bean 메서드 내에서 메서드나 필드를 호출하면 협력하는 객체에 대한 Bean 메타데이터 참조를 생성하는 수단입니다. 이러한 메서드는 일반적인 Java 시맨틱으로 호출되는 것이 아니라 @Bean 메서드에 대한 프로그래밍 호출을 통해 다른 Bean을 참조할 때에도 일반적인 수명 주기 관리 및 Spring Bean의 프록시를 제공하기 위해 컨테이너를 거치게 됩니다. 이와 대조적으로, 일반 @Component 클래스 내의 @Bean 메서드에서 메서드나 필드를 호출하는 것은 특별한 CGLIB 처리나 기타 제약 조건이 적용되지 않는 표준 Java 시맨틱을 갖습니다.

Translated with www.DeepL.com/Translator (free version)

static 으로 @Bean 메서드를 선언하면 해당 메서드를 포함하는 구성 클래스를 인스턴스로 생성하지 않고도 호출할 수 있습니다. 이는 Post-processor Bean을 정의할 때(예: BeanFactoryPostProcessor 또는 BeanPostProcessor 유형) 컨테이너 라이프사이클 초기에 초기화되므로 해당 시점에서 구성의 다른 부분이 트리거되지 않도록 해야 하므로 특히 의미가 있습니다.

정적 @Bean 메서드에 대한 호출은 기술적 제한으로 인해 컨테이너가 절대로 가로채지 않으며, 심지어 @Configuration 클래스 내에서도(이 섹션의 앞부분에서 설명한 대로) 절대로 가로채지 않습니다: CGLIB 서브클래싱은 정적이 아닌 메서드만 재정의할 수 있습니다. 결과적으로, 다른 @Bean 메서드에 대한 직접 호출은 표준 Java 시맨틱을 가지므로 팩토리 메서드 자체에서 바로 독립적인 인스턴스가 반환됩니다.

Java 언어의 @Bean 메서드 가시성은 Spring 컨테이너의 결과 Bean 정의에 즉각적인 영향을 미치지 않습니다. 비`@Configuration` 클래스는 물론 정적 메서드에 대해서도 어디서나 자유롭게 팩토리 메서드를 선언할 수 있습니다. 그러나 @Configuration 클래스의 일반 @Bean 메서드는 오버라이드할 수 있어야 합니다. — 즉, private 또는 final 로 선언되어서는 안 됩니다.

@Bean 메서드는 지정된 컴포넌트나 구성 클래스의 베이스 클래스뿐만 아니라 컴포넌트나 구성 클래스가 구현한 인터페이스에 선언된 Java 8 기본 메서드에서도 검색됩니다. 이를 통해 복잡한 구성 배열을 유연하게 구성할 수 있으며, Spring 4.2부터는 Java 8 기본 메서드를 통해 다중 상속도 가능합니다.

마지막으로, 단일 클래스는 런타임에 사용 가능한 종속성에 따라 사용할 여러 팩토리 메서드의 배열로 동일한 Bean에 대해 여러 개의 @Bean 메서드를 보유할 수 있습니다. 이는 다른 구성 시나리오에서 "가장 탐욕스러운`" 생성자 또는 팩토리 메서드를 선택하는 것과 동일한 알고리즘입니다: 컨테이너가 여러 개의 `@Autowired 생성자 중에서 선택하는 방식과 유사하게 구성 시점에 만족스러운 종속성 수가 가장 많은 변형이 선택됩니다.

자동 감지된 컴포넌트 이름 지정(Naming Autodetected Components)

스캔 프로세스의 일부로 컴포넌트가 자동 감지되면 해당 스캐너에 알려진 BeanNameGenerator 전략에 의해 Bean 이름이 생성됩니다.

기본적으로 AnnotationBeanNameGenerator 가 사용됩니다. Spring stereotype annotations의 경우, 어노테이션의 value 속성을 통해 이름을 제공하면 해당 이름이 해당 Bean 정의의 이름으로 사용됩니다. 이 규칙은 Spring 스테레오타입 어노테이션 대신 다음 JSR-250 및 JSR-330 어노테이션을 사용하는 경우에도 적용됩니다: jakarta.annotation.ManagedBean`, @javax.annotation.ManagedBean, @jakarta.inject.Named, @javax.inject.Named.

Spring Framework 6.1부터 Bean 이름을 지정하는 데 사용되는 어노테이션 속성의 이름은 더 이상 value 가 아니어도 됩니다. 사용자 정의 스테레오타입 어노테이션은 다른 이름(예: name)으로 어노테이션을 선언하고 해당 어노테이션에 @AliasFor(annotation = Component.class, attribute = "value")`로 주석을 달 수 있습니다. 구체적인 예는 `ControllerAdvice#name() 의 소스 코드 선언을 참조하세요.

스프링 프레임워크 6.1부터, 규칙 기반 스테레오타입 이름에 대한 지원은 더 이상 사용되지 않으며 향후 버전의 프레임워크에서 제거될 예정입니다. 따라서 사용자 정의 스테레오타입 어노테이션은 @AliasFor`를 사용하여 `@Component`의 `value 속성에 대한 명시적 별칭을 선언해야 합니다. 구체적인 예는 Repository#value() 및 `ControllerAdvice#name()`의 소스 코드 선언을 참조하세요.

이러한 어노테이션이나 기타 감지된 구성 요소(예: 사용자 정의 필터로 검색된 것)에서 명시적인 Bean 이름을 도출할 수 없는 경우 기본 Bean 이름 생성기는 대문자로 구분되지 않은 정규화되지 않은 클래스 이름을 반환합니다. 예를 들어, 다음 컴포넌트 클래스가 감지된 경우 이름은 myMovieLister 및 `movieFinderImpl`이 됩니다.

  • Java

  • Kotlin

@Service("myMovieLister")
public class SimpleMovieLister {
	// ...
}
@Service("myMovieLister")
class SimpleMovieLister {
	// ...
}
  • Java

  • Kotlin

@Repository
public class MovieFinderImpl implements MovieFinder {
	// ...
}
@Repository
class MovieFinderImpl : MovieFinder {
	// ...
}

기본 Bean 명명 전략에 의존하지 않으려면 사용자 지정 Bean 명명 전략을 제공할 수 있습니다. 먼저 BeanNameGenerator 인터페이스를 구현하고 기본 no-arg 생성자를 반드시 포함하세요. 그런 다음 스캐너를 구성할 때 다음 예제 어노테이션과 Bean 정의에 표시된 것처럼 정규화된 클래스 이름을 제공하세요.

동일한 정규화되지 않은 클래스 이름을 가진 자동 감지된 여러 구성 요소(즉, 이름은 동일하지만 다른 패키지에 있는 클래스)로 인해 명명 충돌이 발생하는 경우, 생성된 Bean 이름에 대해 정규화된 클래스 이름을 기본값으로 하는 BeanNameGenerator 를 구성해야 할 수 있습니다. 스프링 프레임워크 5.2.3부터 org.springframework.context.annotation 패키지에 있는 FullyQualifiedAnnotationBeanNameGenerator 를 이러한 용도로 사용할 수 있습니다.
  • Java

  • Kotlin

@Configuration
@ComponentScan(basePackages = "org.example", nameGenerator = MyNameGenerator.class)
public class AppConfig {
	// ...
}
@Configuration
@ComponentScan(basePackages = ["org.example"], nameGenerator = MyNameGenerator::class)
class AppConfig {
	// ...
}
<beans>
	<context:component-scan base-package="org.example"
		name-generator="org.example.MyNameGenerator" />
</beans>

일반적으로 다른 컴포넌트가 해당 이름을 명시적으로 참조할 수 있는 경우에는 어노테이션으로 이름을 지정하는 것이 좋습니다. 반면에 컨테이너가 와이어링을 담당하는 경우에는 자동 생성된 이름을 사용하는 것이 적절합니다.

자동 감지된 구성 요소에 대한 Scope 제공(Providing a Scope for Autodetected Components)

일반적으로 Spring이 관리하는 컴포넌트와 마찬가지로, 자동 감지된 컴포넌트의 기본적이고 가장 일반적인 범위는 싱글톤`입니다. 그러나 때때로 다른 범위가 필요할 수 있으며, 이는 `@Scope 어노테이션으로 지정할 수 있습니다. 다음 예시와 같이 어노테이션 내에 범위의 이름을 지정할 수 있습니다:

  • Java

  • Kotlin

@Scope("prototype")
@Repository
public class MovieFinderImpl implements MovieFinder {
	// ...
}
@Scope("prototype")
@Repository
class MovieFinderImpl : MovieFinder {
	// ...
}
@Scope 어노테이션은 구체적인 Bean 클래스(어노테이션된 컴포넌트의 경우) 또는 팩토리 메서드(@Bean 메서드의 경우)에 대해서만 내재화됩니다. XML Bean 정의와 달리, Bean 정의 상속이라는 개념이 없으며 클래스 수준에서의 상속 계층 구조는 메타데이터 목적과 관련이 없습니다.

Spring 컨텍스트에서 “request” 또는 “session” 과 같은 웹 특정 Scope에 대한 자세한 내용은 요청, 세션, 애플리케이션 및 웹소켓 스코프를 참조하세요. 이러한 범위에 대해 미리 빌드된 어노테이션과 마찬가지로, Spring의 메타 어노테이션 접근 방식을 사용하여 자체 스코핑 어노테이션을 작성할 수도 있습니다(예: @Scope("프로토타입") 으로 메타 어노테이션된 사용자 정의 어노테이션, 사용자 정의 스코프-프록시 모드 선언도 가능).

어노테이션 기반 접근 방식에 의존하지 않고 범위 확인을 위한 사용자 정의 전략을 제공하려면 ScopeMetadataResolver 인터페이스를 구현할 수 있습니다. 기본 no-arg 생성자를 포함해야 합니다. 그런 다음 스캐너를 구성할 때 다음 어노테이션과 Bean 정의의 예에서 볼 수 있듯이 정규화된 클래스 이름을 제공할 수 있습니다:
  • Java

  • Kotlin

@Configuration
@ComponentScan(basePackages = "org.example", scopeResolver = MyScopeResolver.class)
public class AppConfig {
	// ...
}
@Configuration
@ComponentScan(basePackages = ["org.example"], scopeResolver = MyScopeResolver::class)
class AppConfig {
	// ...
}
<beans>
	<context:component-scan base-package="org.example" scope-resolver="org.example.MyScopeResolver"/>
</beans>

특정 싱글톤 아닌 스코프를 사용하는 경우, 스코프된 객체에 대한 프록시를 생성해야 할 수 있습니다. 그 이유는 Scoped Bean as Dependencies에 설명되어 있습니다. 이를 위해 구성 요소 스캔 요소에서 Scope-proxy 속성을 사용할 수 있습니다. 가능한 세 가지 값은 다음과 같습니다: no, interfaces, targetClass 입니다. 예를 들어, 다음 구성은 표준 JDK 동적 프록시를 생성합니다:

  • Java

  • Kotlin

@Configuration
@ComponentScan(basePackages = "org.example", scopedProxy = ScopedProxyMode.INTERFACES)
public class AppConfig {
	// ...
}
@Configuration
@ComponentScan(basePackages = ["org.example"], scopedProxy = ScopedProxyMode.INTERFACES)
class AppConfig {
	// ...
}
<beans>
	<context:component-scan base-package="org.example" scoped-proxy="interfaces"/>
</beans>

주석이 포함된 한정자 메타데이터 제공하기(Providing Qualifier Metadata with Annotations)

@Qualifier 어노테이션은 한정자를 사용하여 어노테이션 기반 Autowiring 미세 조정하기에서 설명합니다. 이 섹션의 예제에서는 @Qualifier 어노테이션과 사용자 정의 한정자 어노테이션을 사용하여 Autowiring 후보를 해결할 때 세분화된 제어를 제공하는 방법을 보여줍니다. 이러한 예제는 XML Bean 정의를 기반으로 했기 때문에, 한정자 메타데이터는 XML의 bean 요소의 qualifier 또는 meta 하위 요소를 사용하여 후보 Bean 정의에 제공되었습니다. 구성 요소의 자동 감지를 위해 클래스 경로 검색에 의존하는 경우 한정자 메타데이터에 후보 클래스에 대한 유형 수준 주석을 제공할 수 있습니다. 다음 세 가지 예는 이 기술을 보여줍니다:

  • Java

  • Kotlin

@Component
@Qualifier("Action")
public class ActionMovieCatalog implements MovieCatalog {
	// ...
}
@Component
@Qualifier("Action")
class ActionMovieCatalog : MovieCatalog
  • Java

  • Kotlin

@Component
@Genre("Action")
public class ActionMovieCatalog implements MovieCatalog {
	// ...
}
@Component
@Genre("Action")
class ActionMovieCatalog : MovieCatalog {
	// ...
}
  • Java

  • Kotlin

@Component
@Offline
public class CachingMovieCatalog implements MovieCatalog {
	// ...
}
@Component
@Offline
class CachingMovieCatalog : MovieCatalog {
	// ...
}
대부분의 어노테이션 기반 대안과 마찬가지로 어노테이션 메타데이터는 클래스 정의 자체에 바인딩되는 반면, XML을 사용하면 메타데이터가 클래스가 아닌 인스턴스별로 제공되므로 동일한 유형의 여러 Bean이 한정자 메타데이터에 변형을 제공할 수 있다는 점을 명심하세요.