[Spring] 컴포넌트 스캔 (@Component, @ComponentScan)
2023. 8. 27.
반응형

이전 포스팅에서는 자동 주입(@Autowired)을 다뤘습니다. 자동 주입이란 빈에 의존객체를 자동으로 주입하는 것을 의미합니다.

 

그렇다면 자동 주입처럼 빈도 자동으로 스캔해주는 기능이 있겠죠?

바로 그 역할을 해주는 어노테이션이 바로 @Component 입니다.

 

이제까지는 @Bean을 사용해 수동으로 스프링에 빈을 등록했다면,

@Component를 클래스에 붙이면 그 클래스를 스캔해 설정코드 없이 자동으로 클래스를 빈으로 등록할 수 있습니다.

 

 

 

 

반응형

 

 

@Component("infoPrinter")
public class MemberInfoPrinter {

	private MemberDao memDao;
	private MemberPrinter printer;

	public void printMemberInfo(String email) {
		Member member = memDao.selectByEmail(email);
		if (member == null) {
			System.out.println("데이터 없음\n");
			return;
		}
		printer.print(member);
		System.out.println();
	}

	@Autowired
	public void setMemberDao(MemberDao memberDao) {
		this.memDao = memberDao;
	}

	@Autowired
	public void setPrinter(MemberPrinter printer) {
		this.printer = printer;
	}
}

 

 

위의 코드처럼 어노테이션을 붙여서 사용하며 속성값도 따로 줄 수 있습니다.

만약 속성값을 주지 않았다면 빈의 이름은 'MemberInfoPrinter'가 되고 예시처럼 값을 준 경우에는 'infoPrinter'가 됩니다.

 

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

 

 

 

반응형

 

 

 

ex1) @Component, @ComponentScan 적용 전

 

 

@Configuration
public class AppCtx {
	@Bean
	public MemberInfoPrinter infoPrinter() {
		MemberInfoPrinter infoPrinter = new MemberInfoPrinter();
		return infoPrinter;
	}
}

 

 

 

ex2) @Component, @ComponentScan 적용 후

 

 

@Configuration
@ComponentScan(basePackages = {"spring"}) //코드를 작성하고 있는 패키지
public class AppCtx {
	...
}

 

위의 예시 코드에서 @Component를 사용해 infoPrinter로 자동으로 빈을 등록하게 했기 때문에 설정 코드에는 아무것도 작성하지 않아도 자동으로 빈이 등록됩니다.

 

예시코드처럼 @ComponentScan을 사용하게 되면 basePackages를 설정해 줘야 하는데 위와 같이 설정하면 spring 패키지 하위에 속한 클래스를 스캔 대상으로 설정합니다.

 

 

 

반응형

 

 

 

excludeFilters 속성을 사용하면 스캔할 때 특정 대상을 자동 등록 대상에서 제외할 수 있습니다.

 

@Configuration
@ComponentScan(basePackages = {"spring"}, 
	excludeFilters = @Filter(type = FilterType.REGEX, pattern = "spring\\..*Dao")) //코드를 작성하고 있는 패키지
public class AppCtx {
	...
}

 

위의 예시처럼 사용할 수 있으며 @Filter 어노테이션을 사용해 속성으로 type, pattern을 지정할 수 있습니다.

FilterType.REGEX정규표현식을 사용한다는 의미이고, spring.으로 시작하고 ~Dao라고 끝나는 정규표현식을 지정해 이와 관련된 이름의 클래스들을 컴포넌트 스캔 대상에서 제외합니다.

 

이외에도 @Filter 어노테이션을 통해 특정 어노테이션을 붙인 타입을 컴포넌트 스캔 대상에서 제외할 수도 있습니다.

 

@Configuration
@ComponentScan(basePackages = {"spring"}, 
	excludeFilters = {@Filter(type = FilterType.REGEX, pattern = "spring\\..*"),
    			  @Filter(type = FilterType.ANNOTATION, classes = "MaunalBean.class")})
public class AppCtx {
	...
}

 

이렇게 작성하면 spring패키지에 @ManualBean 어노테이션이 적힌 클래스가 컴포넌트 스캔 대상에서 제외됩니다.

 

 

 

 

반응형

 

 

 

 

특정 클래스를 컴포넌트 스캔대상에서 제외하고 싶으면 어떻게 해야 할까요?

바로 ASSIGNABLE_TYPE을 사용하면 됩니다.

 

 

@Configuration
@ComponentScan(basePackages = {"spring"}, 
	excludeFilters = {@Filter(type = FilterType.REGEX, pattern = "spring\\..*"),
    			  @Filter(type = FilterType.ASSIGNABLE_TYPE, classes = "MemberDao.class")})
public class AppCtx {
	...
}

 

이처럼 직접 지정해 컴포넌트 스캔대상에서 제외할 수 있습니다.

 

 

REGEX 대신 AspectJ 패턴을 사용해서 대상을 지정할 수도 있습니다.

 

@Configuration
@ComponentScan(basePackages = {"spring"}, 
	excludeFilters = @Filter(type = FilterType.ASPECTJ, pattern = "spring.*Dao")) //코드를 작성하고 있는 패키지
public class AppCtx {
	...
}

 

이처럼 간결하게 표현식 패턴을 적을 수 있고 이에 대해서는 나중에 자세히 다루도록 하겠습니다.

AspectJ패턴을 사용하기 위해서는 의존 대상에 aspectjweaver 모듈을 추가해야 합니다.

 

 

Maven ver

 

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.20</version>
    <scope>runtime</scope>
</dependency>

 

Gradle ver

 

 

runtimeOnly 'org.aspectj:aspectjweaver:1.9.20'

 

 

 

 

반응형

 

 

 

 

@ComponentScan을 통해 컴포넌트 스캔에 포함되는 대상은 @Component 뿐만이 아닙니다.

 

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

 

이처럼 다양한 어노테이션이 컴포넌트 스캔 대상인데 대부분은 웹 MVC와 관련이 있고 @Repository는 DB와 관련이 있는데 이는 나중에 자세히 살펴보도록 하겠습니다.

 

@ComponentScan을 통해 자동으로 컴포넌트를 스캔하면 충돌이 발생할 수 있습니다. 이를 어떻게 처리하면 좋을까요?

 

case 1) 빈 이름이 충돌한 경우

 

예를 들어 두 가지 패키지를 스캔하는 데 각 패키지에 같은 이름의 클래스가 있다면 어떻게 될까요?

당연히 같은 클래스가 두 개 이기 때문에 익셉션이 발생합니다. 둘 중에 뭘 선택해야 할지 모르기 때문입니다.

=> 둘 중 하나를 명시적으로 빈 이름을 지정해 충돌 피하기

 

case 2) 수동 등록한 빈과의 충돌이 일어난 경우

 

@Component로 자동 등록 후 설정 파일에서 @Bean을 붙여 수동 등록한 경우 어떻게 될까요?

이 경우 수동 등록이 우선하기 때문에 수동 등록된 빈 하나만 등록되게 됩니다.

만약 같은 클래스라 하더라도 이름을 다르게 등록한다면 각각 두 개의 빈이 생성됩니다.

=> @Qualifier 어노테이션 사용 통해 알맞은 빈 선택해서 사용

 

이런 케이스들에 따라 컴포넌트 스캔에 따른 충돌을 처리할 수 있습니다.

 

 

 

출처: 초보 웹 개발자를 위한 스프링 5 프로그래밍 입문

 

 

반응형
myoskin