Classpath Scanning and Managed Components

Dev.Hammy·2024년 2월 11일
0

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

[Note]
XML 파일을 사용하는 대신 Java를 사용하여 Bean을 정의할 수 있습니다. 이러한 기능을 사용하는 방법에 대한 예를 보려면 @Configuration, @Bean, @Import@DependsOn 주석을 살펴보세요.

@Component and Further Stereotype Annotations

@Repository 어노테이션은 저장소(데이터 액세스 객체 또는 DAO라고도 함)의 역할이나 stereotype을 충족하는 모든 클래스에 대한 마커입니다. 이 마커의 용도 중에는 예외 번역(translation)에 설명된 대로 예외 자동 translation이 있습니다.

Spring은 @Component, @Service@Controller와 같은 추가 스테레오타입 어노테이션을 제공합니다. @Component는 Spring 관리 컴포넌트에 대한 일반적인 스테레오타입입니다. @Repository, @Service@Controller는 보다 구체적인 사용 사례(각각 persistence, 서비스 및 프레젠테이션 계층)를 위한 @Component의 특수화입니다. 따라서 @Component로 컴포넌트 클래스에 어노테이션을 달 수 있지만 대신 @Repository, @Service 또는 @Controller로 어노테이션을 달면 클래스가 도구로 처리하거나 측면과 연결하는 데 더 적합합니다. 예를 들어 이러한 스테레오타입 어노테이션은 포인트컷의 이상적인 대상이 됩니다. @Repository, @Service@Controller는 Spring Framework의 향후 릴리스에서 추가 의미(semantics)를 전달할 수도 있습니다. 따라서 서비스 계층에 @Component 또는 @Service 중 하나를 선택하는 경우 @Service가 더 나은 선택입니다. 마찬가지로 앞서 설명한 대로 @Repository는 지속성 레이어에서 자동 예외 변환을 위한 마커로 이미 지원됩니다.

Using Meta-annotations and Composed Annotations

Spring이 제공하는 많은 어노테이션은 자신의 코드에서 메타 어노테이션으로 사용될 수 있습니다. 메타 어노테이션은 다른 어노테이션에 적용할 수 있는 어노테이션입니다. 예를 들어 앞서 언급한 @Service 주석은 다음 예제와 같이 @Component로 메타 어노테이션이 붙습니다.

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

	// ...
}

(1) @Component를 사용하면 @Service@Component와 동일한 방식으로 처리됩니다.

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

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

@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;

}

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

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

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

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

자세한 내용은 Spring 어노테이션 프로그래밍 모델 위키 페이지를 참조하세요.

Automatically Detecting Classes and Registering Bean Definitions

Spring은 자동으로 스테레오타입 클래스를 감지하고 해당 BeanDefinition 인스턴스를 ApplicationContext에 등록할 수 있습니다. 예를 들어 다음 두 클래스는 이러한 자동 감지에 적합(eligible)합니다.

@Service
public class SimpleMovieLister {

	private MovieFinder movieFinder;

	public SimpleMovieLister(MovieFinder movieFinder) {
		this.movieFinder = movieFinder;
	}
}
@Repository
public class JpaMovieFinder implements MovieFinder {
	// implementation elided for clarity
}

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

@Configuration
@ComponentScan(basePackages = "org.example")
public class AppConfig  {
	// ...
}

[Note]
간결하게 하기 위해 앞의 예에서는 어노테이션의 value 속성(attribute)(즉, @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>

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

[Note]
클래스패스 패키지를 스캐닝하려면 클래스패스에 해당 디렉토리 항목(entry)이 있어야 합니다. Ant로 JAR을 빌드할 때, JAR 작업의 파일 전용(files-only) 스위치를 활성화하지 않도록 하십시오. 또한 일부 환경에서는 보안 정책에 따라 클래스패스 디렉토리가 노출되지 않을 수 있습니다. 예를 들어 JDK 1.7.0_45 이상의 독립 실행형 앱(매니페스트에 '신뢰할 수 있는 라이브러리' 설정이 필요함)—'Stackoverflow.com/questions/19394570/ java-jre-7u45-breaks-classloader-getresources 참조 ).

JDK 9의 모듈 경로(Jigsaw)에서 Spring의 클래스패스 검색은 일반적으로 예상대로 작동합니다. 그러나 컴포넌트 클래스가 module-info 설명자(descriptors)로 내보내졌는지 확인하세요. Spring이 클래스의 non-public 멤버를 호출할 것으로 예상한다면 해당 멤버가 '열려 있는지' 확인하세요(즉, module-info 설명자에서 exports 선언 대신 opens 선언을 사용하는지 확인하세요).

또한, component-scan 요소를 사용할 때 AutowiredAnnotationBeanPostProcessorCommonAnnotationBeanPostProcessor가 모두 암시적으로 포함됩니다. 이는 XML로 제공되는 Bean 구성 메타데이터 없이 두 컴포넌트가 자동 감지되어 함께 연결된다는 의미입니다.

[Note]
false 값으로 annotation-config 속성(attribute)을 포함하여 AutowiredAnnotationBeanPostProcessorCommonAnnotationBeanPostProcessor의 등록을 비활성화할 수 있습니다.

Using Filters to Customize Scanning

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

표 1. 필터 유형
필터 type 예시 expression 설명

annotation (기본값)

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" 저장소를 사용하는 구성을 보여줍니다

@Configuration
@ComponentScan(basePackages = "org.example",
		includeFilters = @Filter(type = FilterType.REGEX, pattern = ".*Stub.*Repository"),
		excludeFilters = @Filter(Repository.class))
public 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>

[Note]
어노테이션에서 useDefaultFilters=false를 설정하거나 <component-scan/> 요소의 속성(attribute)으로 use-default-filters="false"를 제공하여 기본 필터를 비활성화할 수도 있습니다. 이는 @Component, @Repository, @Service, @Controller, @RestController 또는 @Configuration으로 어노테이션이 달렸거나 메타 어노테이션이 달린 클래스의 자동 감지를 효과적으로 비활성화합니다.

Defining Bean Metadata within Components

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

@Component
public class FactoryMethodComponent {

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

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

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

[Tip]
구성 요소 초기화 역할 외에도 @Autowired 또는 @Inject로 표시된 주입 지점에 @Lazy 어노테이션을 배치할 수도 있습니다. 이러한 context에서 이는 lazy-resolution 프록시의 주입으로 이어집니다. 그러나 이러한 프록시 접근 방식은 다소 제한적입니다. 특히 선택적 종속성과 결합된 정교한 지연 상호작용의 경우 대신 ObjectProvider<MyTargetBean>을 권장합니다.

@Bean 메소드의 자동 연결에 대한 추가 지원과 함께 이전에 논의한 대로 자동 연결 필드 및 메소드가 지원됩니다. 다음 예에서는 그 방법을 보여줍니다.

@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);
	}
}

예제에서는 String 메소드 매개변수 countryprivateInstance라는 다른 Bean의 age 속성(property) 값에 자동 연결합니다. Spring Expression Language 요소는 #{ <expression> } 표기법을 통해 속성(property) 값을 정의합니다. @Value 어노테이션의 경우 표현식(expression) 텍스트를 분석할 때 Bean 이름을 찾도록 표현식 resolver가 사전 구성(preconfigured)되어 있습니다.

Spring Framework 4.3부터는 현재 Bean 생성을 트리거하는 타입 주입 지점에 액세스하기 위해 InjectionPoint 타입(또는 더 구체적인 하위 클래스인 DependencyDescriptor)의 팩토리 메서드 매개 변수를 선언할 수도 있습니다. 이는 기존 인스턴스 주입에는 적용되지 않고 실제 Bean 인스턴스 생성에만 적용된다는 점에 유의하세요. 결과적으로 이 기능은 프로토타입 범위의 Bean에 가장 적합합니다. 다른 범위의 경우 팩토리 메소드는 지정된 범위에서 새 Bean 인스턴스 생성을 트리거한 주입 지점만 봅니다(예: lazy 싱글톤 Bean 생성을 트리거한 종속성). 이러한 시나리오에서는 의미론적으로 주의하여 제공된 주입 지점 메타데이터를 사용할 수 있습니다. 다음 예에서는 InjectionPoint를 사용하는 방법을 보여줍니다.

@Component
public class FactoryMethodComponent {

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

일반(regular) Spring 컴포넌트의 @Bean 메소드는 Spring @Configuration 클래스 내부의 메소드와 다르게 처리됩니다. 차이점은 메소드와 필드의 호출을 가로채기 위해 @Component 클래스가 CGLIB로 향상(enhance)되지 않는다는 것입니다. CGLIB 프록시는 @Configuration 클래스의 @Bean 메소드 내에서 메소드나 필드를 호출하여 공동 객체(collaborating object)에 대한 Bean 메타데이터 참조를 생성하는 수단입니다. 이러한 메소드는 일반적인 Java semantics으로 호출되지 않고, @Bean 메소드에 대한 프로그래밍 방식 호출을 통해 다른 Bean을 참조하는 경우에도, 일반적인 수명주기 관리 및 Spring Bean의 프록시를 제공하기 위해 컨테이너를 통과(go through)합니다. 대조적으로, 일반 @Component 클래스 내의 @Bean 메소드에서 메소드나 필드를 호출하는 것은 특별한 CGLIB 처리나 다른 제약 조건이 적용되지 않는 표준 Java semantics를 갖습니다.

[Note]
@Bean 메소드를 static으로 선언하면 포함하는 구성(configuration) 클래스를 인스턴스로 생성하지 않고도 호출할 수 있습니다. 이는 사후 프로세서(post-processor) Bean(예: BeanFactoryPostProcessor 또는 BeanPostProcessor 타입)을 정의할 때 특히 의미가 있습니다. 이러한 Bean은 컨테이너 수명주기 초기에 초기화되고 해당 시점에서 구성의 다른 부분을 트리거하지 않아야 하기 때문입니다.

기술적인 제한으로 인해 @Configuration 클래스(이 섹션 앞부분에서 설명) 내에서도 정적 @Bean 메서드에 대한 호출은 컨테이너에 의해 차단되지 않습니다. CGLIB 서브클래싱은 non-static 메서드만 재정의(override)할 수 있습니다. 결과적으로 다른 @Bean 메소드에 대한 직접 호출은 표준 Java semantics를 가지며, 결과적으로 팩토리 메소드 자체에서 직접 반환되는 독립 인스턴스가 됩니다.

@Bean 메소드의 Java 언어 가시성은 Spring 컨테이너의 결과 Bean 정의에 즉각적인 영향을 미치지 않습니다. @Configuration이 아닌 클래스와 어디에서나 정적 메서드에 적합하다고 판단되는 대로 팩토리 메서드를 자유롭게 선언할 수 있습니다. 그러나 @Configuration 클래스의 일반(regular) @Bean 메서드는 재정의 가능해야 합니다. 즉, private 또는 final로 선언하면 안 됩니다.

@Bean 메소드는 지정된 컴포넌트 또는 구성(configuration) 클래스의 base 클래스뿐만 아니라 컴포넌트 또는 구성(configuration) 클래스에 의해 구현된 인터페이스에 선언된 Java 8 default 메소드에서도 발견됩니다. 이는 복잡한 구성(configuration) arrangement을 구성하는 데 많은 유연성을 허용하며 Spring 4.2부터 Java 8 default 메소드를 통해 다중 상속도 가능합니다.

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

Naming Autodetected Components

스캐닝 프로세스의 일부로 구성 요소가 자동 감지되면 해당 스캐너에 알려진 BeanNameGenerator 전략(strategy)에 의해 해당 Bean 이름이 생성됩니다.

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

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

[Warning]
Spring Framework 6.1부터 convention-based 스테레오타입 이름에 대한 지원은 더 이상 사용되지 않으며 프레임워크의 향후 버전에서 제거될 예정입니다. 결과적으로 사용자 정의 스테레오타입 어노테이션은 @AliasFor를 사용하여 @Componentvalue 속성(attribute)에 대한 명시적 별칭을 선언해야 합니다. 구체적인 예는 Repository#value()ControllerAdvice#name()의 소스 코드 선언을 참조하세요.

그러한 어노테이션이나 다른 감지된 컴포넌트(예: 사용자 정의 필터에 의해 발견된 컴포넌트)에서 명시적인 Bean 이름을 파생할 수 없는 경우 기본 Bean 이름 생성기는 대문자로 표시되지 않고 정규화되지 않은 클래스 이름을 반환합니다. 예를 들어, 다음 컴포넌트 클래스가 감지된 경우 이름은 myMovieListermovieFinderImpl이 됩니다.

@Service("myMovieLister")
public class SimpleMovieLister {
	// ...
}
@Repository
public class MovieFinderImpl implements MovieFinder {
	// ...
}

기본 빈 이름 지정 전략을 사용하지 않으려면 사용자 정의 빈 이름 지정 전략을 제공할 수 있습니다. 먼저 BeanNameGenerator 인터페이스를 구현하고 인수가 없는 기본 생성자(no-arg constructor)를 포함해야 합니다. 그런 다음, 다음 예제 주석 및 Bean 정의에 표시된 대로 스캐너를 구성할 때 완전한 클래스 이름(fully qualifued name)을 제공하십시오.

[Tip]
동일한 정규화되지 않은 클래스 이름을 가진 여러 자동 감지된 컴포넌트로 인해 이름 지정 충돌이 발생하는 경우(즉, 이름은 동일하지만 다른 패키지에 있는 클래스), 생성된 빈 이름에 대한 완전히 정규화된 클래스 이름을 default 로 제공하는 BeanNameGenerator를 구성해야 할 수도 있습니다. Spring Framework 5.2.3부터는 org.springframework.context.annotation 패키지에 위치한 FullyQualifiedAnnotationBeanNameGenerator를 이러한 목적으로 사용할 수 있습니다.

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

일반적으로 다른 구성요소가 이를 명시적으로 참조할 수 있을 때에는 어노테이션으로 이름을 지정하는 것을 고려하십시오. 반면, 컨테이너가 연결을 담당할 때에는 자동 생성된 이름이 적합합니다.

Providing a Scope for Autodetected Components

일반적으로 Spring 관리 구성 요소와 마찬가지로 자동 감지 구성 요소의 기본이자 가장 일반적인 범위는 singleton입니다. 그러나 @Scope 어노테이션으로 지정할 수 있는 다른 범위가 필요한 경우도 있습니다. 다음 예시와 같이 어노테이션 내에 범위 이름을 제공할 수 있습니다.

@Scope("prototype")
@Repository
public class MovieFinderImpl implements MovieFinder {
	// ...
}

[Note]
@Scope 어노테이션은 구체적인 Bean 클래스(어노테이션이 달린 컴포넌트의 경우) 또는 팩토리 메서드(@Bean 메서드의 경우)에서만 검사(introspected)됩니다. XML bean 정의와 대조적으로 bean 정의 상속 개념이 없으며 클래스 수준의 상속 계층 구조는 메타데이터 목적과 관련이 없습니다.

Spring 컨텍스트의 "request" 또는 "session"과 같은 웹 특정 범위에 대한 자세한 내용은 request, session, application 및 websocket 범위를 참조하세요. 해당 범위에 대해 사전 빌드된 어노테이션과 마찬가지로 Spring의 메타 어노테이션 접근 방식을 사용하여 자신만의 범위 지정 어노테이션을 작성할 수도 있습니다. 예를 들어 @Scope("prototype")로 메타 어노테이션이 달린 사용자 정의 프록시 모드 범위 어노테이션도 선언 가능할 수 있습니다.

[Note]
어노테이션 기반 접근 방식에 의존하지 않고 범위 resolution을 위한 사용자 지정 전략을 제공하려면 ScopeMetadataResolver 인터페이스를 구현할 수 있습니다. 인수가 없는 기본 생성자(no-arg constructor)를 포함해야 합니다. 그런 다음 어노테이션과 Bean 정의에 대한 다음 예제와 같이 스캐너를 구성할 때 정규화된 클래스 이름을 제공할 수 있습니다.

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

특정 non-singleton 범위를 사용하는 경우 범위가 지정된 개체에 대한 프록시를 생성해야 할 수도 있습니다. 추론은 종속성으로서의 범위 지정 빈에 설명되어 있습니다. 이를 위해 컴포넌트-스캔 요소에서 scoped-proxy 속성(attribute)을 사용할 수 있습니다. 가능한 세 가지 값은 no, interfacestargetClass입니다. 예를 들어, 다음 구성은 표준 JDK 동적 프록시를 생성합니다.

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

Providing Qualifier Metadata with Annotations

@Qualifier 어노테이션은 한정자를 사용한 어노테이션 기반 autowiring 미세 조정에서 논의됩니다. 해당 섹션의 예제에서는 자동 연결 후보를 resolve할 때 세부적인 제어를 제공하기 위해 @Qualifier 어노테이션과 사용자 정의 한정자 어노테이션을 사용하는 방법을 보여줍니다. 이러한 예제는 XML Bean 정의를 기반으로 했기 때문에, XML에 있는 bean 요소의 qualifier 또는 meta 하위 요소를 사용하여 후보 Bean 정의에 한정자 메타데이터가 제공되었습니다. 컴포넌트 자동 감지를 위해 클래스패스 검색을 사용하는 경우 후보 클래스에 대한 type-level 어노테이션이 포함된 한정자 메타데이터를 제공할 수 있습니다. 다음 세 가지 예에서는 이 기술을 보여줍니다.

@Component
@Qualifier("Action")
public class ActionMovieCatalog implements MovieCatalog {
	// ...
}
@Component
@Genre("Action")
public class ActionMovieCatalog implements MovieCatalog {
	// ...
}
@Component
@Offline
public class CachingMovieCatalog implements MovieCatalog {
	// ...
}

[Note]
대부분의 어노테이션 기반 대안과 마찬가지로 어노테이션 메타데이터는 클래스 정의 자체에 바인딩되는 반면, XML을 사용하면 해당 메타데이터가 클래스별이 아닌 인스턴스별로 제공되기 때문에 한정자 메타데이터의 변형을 제공하기 위해 동일한 유형의 여러 Bean이 허용된다는 점을 명심하세요.

0개의 댓글