이전 포스팅에서는 자동 주입(@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 프로그래밍 입문
'Spring' 카테고리의 다른 글
[Spring] AOP 프로그래밍 (@Aspect, @Pointcut , @Advice) (0) | 2023.09.03 |
---|---|
[Spring] 빈(Bean) 라이프사이클과 범위 (0) | 2023.08.27 |
[Spring] 스프링 의존 자동 주입이란? (@Autowired) (0) | 2023.08.13 |
[Spring] 스프링 DI 설정 방식 (생성자, 세터 방식) (0) | 2023.07.28 |
[Spring] 스프링 DI란? (Dependency Injection, 의존성 주입) (feat. 객체 조립기) (0) | 2023.07.23 |