[Spring5입문] 컴포넌트 스캔

2021. 8. 8. 02:16CSE/Spring

- 컴포넌트 스캔 : 스프링이 직접 클래스를 검색해서 빈으로 등록해주는 기능

=> 설정 클래스에 빈으로 등록하지 않아도 빈 등록이 가능하므로 설정 코드가 크게 줄어드는 이점이 있다.

 

1. @Component 애노테이션으로 스캔 대상 지정

- @Component : 해당 클래스를 스캔 대상으로 표시한다.

@Component
public class 클래스이름{
	...
}

- @Component 애노테이션에 값을 주었는지에 따라 빈으로 등록할 때 사용할 이름이 결정된다.

=> 값을 주지 않았을 때 : 클래스 이름의 첫 글자를 소문자로 바꾼 이름이 빈 이름으로 사용된다

=> 값을 주었을 때 : 그 값을 빈 이름으로 사용한다

@Component
public class ExampleClass{
	...
}
//이 경우 exampleClass1이 빈 이름이 된다
@Component("firstBean")
public class ExampleClass{
	...
}
//이 경우 firstBean이 빈 이름이 된다.

- @Component 애노테이션을 사용했을 때도 스프링 컨테이너 사용 방식은 동일하다

=> 값을 주지 않았을 때 : 스프링컨테이너.getBean(클래스이름.class) 또는 스프링컨테이너.getBean("빈 이름", 클래스이름.class)

=> 값을 주었을 때 : 스프링컨테이너.getBean("지정한 빈 이름", 클래스이름.class)

 

2. @ComponentScan 애노테이션으로 스캔 설정

- @Component 애노테이션을 붙인 클래스를 스캔해서 스프링 빈으로 등록하려면 설정 클래스에 @ComponentScan 애노테이션을 적용해야한다.

@Configuration
@ComponentScan(basePackages = {"spring"})
public class AppCtx{
	...
}

- basePackages의 속성 값은 스캔 대상 패키지 목록을 지정한다.

 

3. 스캔 대상에서 제외하거나 포함하기

(1) excludeFilters으로 제외하기

- excludeFilters 속성을 @ComponentScan에 추가하면, 스캔할 때, 특정 대상을 자동 등록 대상에서 제외할 수 있다.

@Configuration
@ComponentScan(basePackages = {"spring"}, 
	excludeFilters = @Filter(type = FilterType.REGEX, pattern = "spring\\..*Dao"))
public class AppCtxWithExclude{
	...
}

- FilterType.REGEX : 정규 표현식을 사용해서 제외 대상을 지정한다는 의미

- pattern 속성은 String[] 타입이므로 배열을 이용해서 패턴을 한 개 이상 지정할 수 있다.

- AspectJ 패턴으로 제외 대상을 지정하기 위해 FilterType을 ASPECTJ로 설정할 수도 있다.

=> AspectJ 패턴이 동작하려면 의존 대상에 aspectjweaver 모듈을 추가해야한다.

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjwewaver</artifactId>
    <version>1.8.13</version>
</dependency>

 

(2) 특정 애노테이션으로 제외하기

- @NoProduct나 @ManualBean 애노테이션을 붙인 클래스는 컴포넌트 스캔 대상에서 제외하려면 다음과 같이 FilterType 중 ANNOTATION을 사용하면 된다.

@Configuration
@ComponentScan(basePackages = {"spring"}, 
	excludeFilters = @Filter(type = FilterType.ANNOTATION, 
    				classes = {NoProduct.class, ManualBean.class}))
public class AppCtxWithExclude{
	...
}

 

(3) Filter.ASSIGNABLE_TYPE을 이용해서 제외하기

- 특정 타입이나 그 하위 타입을 컴포넌트 스캔 대상에서 제외하려면 ASSIGNABLE_TYPE을 사용한다.

@Configuration
@ComponentScan(basePackages = {"spring"}, 
	excludeFilters = @Filter(type = FilterType.ASSIGNABLE_TYPE, classes = MemberDao.class))
public class AppCtxWithExclude{
	...
}

=> 위의 경우 MemberDao 클래스와 이를 상속하는 클래스들을 스캔 대상에서 제외하게 된다.

 

(4) 설정할 필터가 두 개 이상인 경우

- excludeFilters 속성에 배열을 사용해서 @Filter 목록을 전달하면 된다.

 

4. 기본 스캔 대상

- @Component 뿐만 아니라 다음 애노테이션을 붙인 클래스도 컴포넌트 스캔 대상에 포함된다.

  • @Component
  • @Controller
  • @Service
  • @Repository
  • @Aspect
  • @Configuration

=> 이 중 @Aspect 애노테이션을 제외한 나머지 애노테이션은 @Component 애노테이션에 대한 특수 애노테이션이다.

=> 이들은 컴포넌트 스캔 대상이 될뿐만 아니라 스프링 프레임워크에서 특별한 기능과 연관되어있다.

=> ex) @Controller : 웹 MVC, @Repository : DB 연동

 

5. 컴포넌트 스캔에 따른 충돌 처리

(1) 빈 이름 충돌

- A 패키지와 B 패키지에 동일한 이름(MemberDao)을 가진 두 개의 클래스를 모두 빈으로 자동 등록하면 어떻게 될까?

=> A.MemberDao와 memberDao 빈의 타입이 일치하지 않는다는 에러가 발생한다.

=> 둘 중에 하나에 명시적으로 빈 이름을 지정해야한다.

 

(2) 수동 등록한 빈과 충돌

- 한 클래스를 자동으로 빈 등록을 해놓고, 자동으로 등록된 빈의 이름과 동일한 빈 이름을 갖도록 수동으로 빈 등록을 하면 어떻게 될까?

=> 수동 등록한 빈이 우선되어서 자동 등록된 빈은 무시된다.

- 다른 이름을 사용하면?

=> 수동 등록과 자동 등록 빈이 둘 다 존재하기 때문에 @Qualifier 애노테이션을 사용해서 알맞은 빈을 선택해야한다.