
이 글은 김영한 님의 스프링 핵심 원리 - 기본편(https://www.inflearn.com/스프링-핵심-원리-기본편/dashboard)을 수강하며 학습한 내용을 정리한 글입니다. 모든 출처는 해당 강의에 있습니다.
@Bean이나 XML의 <bean>등을 통해 설정 정보에 스프링 빈을 직접 나열하여 등록함@Autowired 기능을 제공함@Autowired : 의존관계 자동 주입AutoAppConfig.java기존의 AppConfig.java는 그대로 두고 AutoAppConfig.java를 새로 생성한다.
[src/main/java/hello/core/AutoAppConfig.java]
package hello.core;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
@Configuration
@ComponentScan( //컴포넌트 스캔 사용
//@Component가 적용된 클래스를 스캔하여 스프링 빈으로 등록
//컴포넌트 스캔 대상에서 제외 할 설정 정보 지정
excludeFilters = @Filter(type = FilterType.ANNOTATION, classes = Configuration.class)
)
public class AutoAppConfig {
}
AppConfig와는 다르게 @Bean으로 등록한 클래스가 존재하지 않음💡 참고
- 컴포넌트 스캔 사용 시
@Configuration이 붙은 설정 정보도 자동으로 등록됨
- 앞서 생성한 설정 정보(
AppConfig,TestConfig등)도 함께 등록되어 실행 됨- 내부적으로
@Component가 적용되어 있으므로 수동 등록과 같음=>
excludeFilters적용하여 제외
- 일반적으로 설정 정보는 컴포넌트 스캔 대상에서 제외하지 않음
@Component
public class MemoryMemberRepository implements MemberRepository { ... }
@Component
public class RateDiscountPolicy implements DiscountPolicy { ... }
@Component
public class MemberServiceImpl implements MemberService {
private final MemberRepository memberRepository;
@Autowired //ac.getBean(MemberRepository.class)와 같음
public MemberServiceImpl(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
...
}
AutoAppConfig에 설정 정보가 없으므로, 이 클래스 내에서 의존관계 주입 필요@Autowired로 해결@Component
public class OrderServiceImpl implements OrderService {
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
@Autowired
public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
...
}
@Autowired 사용 시 여러 의존관계 한 번에 주입 가능[src/test/java/hello/core/scan/AutoAppConfigTest.java]
package hello.core.scan;
import hello.core.AutoAppConfig;
import hello.core.member.MemberService;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
public class AutoAppConfigTest {
@Test
void basicScan(){
ApplicationContext ac = new AnnotationConfigApplicationContext(AutoAppConfig.class);
MemberService memberService = ac.getBean(MemberService.class);
assertThat(memberService).isInstanceOf(MemberService.class);
}
}
...
ClassPathBeanDefinitionScanner - Identified candidate component class:
... RateDiscountPolicy.class
... MemberServiceImpl.class
... MemoryMemberRepository.class
... OrderServiceImpl.class
...
Autowiring by type from bean name 'memberServiceImpl' via constructor to bean named 'memoryMemberRepository'
...
... 'orderServiceImpl' via constructor to bean named 'memoryMemberRepository'
... 'orderServiceImpl' via constructor to bean named 'rateDiscountPolicy'
...
@ComponentScan@Component가 적용된 모든 클래스를 스프링 빈으로 등록@Component("원하는 이름")@Component("memberService2")@Autowired : 의존관계 자동 주입getBean(MemberRepository.class)와 동일한 의미@ComponentScan(
basePackages = "hello.core",
...
)
basePackagesbasePackages = {"hello.core", "hello.service"}basePackageClassed@ComponentScan이 적용된 설정 정보 클래스의 패키지(기본) ★com.hello → 프로젝트 시작 루트AppConfig@ComponentScan 설정basePackages 생략com.hello.servicecom.hello.repository@SpringBootApplication(내부적으로@CompoentScan 포함)을 프로젝트 시작 루트 위치에 둠| 항목 | 설명 | 부가 기능 |
|---|---|---|
@Component | 컴포넌트 스캔에서 사용 | |
@Controller | 스프링 MVC 컨트롤러에서 사용 | 스프링 MVC 컨트롤러로 인식 |
@Service | 스프링 비즈니스 로직에서 사용 | ◾ 특별한 처리 x ◾ 개발자들의 비즈니스 계층 인식에 도움 |
@Repository | 스프링 데이터 접근 계층에서 사용 ex) JPA, JDBC | ◾ 스프링 데이터 접근 계층으로 인식 ◾ 데이터 계층의 예외를 스프링 예외로 변환 ex) 다른 DB로 변경하는 경우, 예외 발생 시 다른 DB의 예외가 발생 → 서비스 및 다른 계층의 코드에도 영향 ∴ 스프링이 예외를 추상화하여 반환 |
@Configuration | 스프링 설정 정보에서 사용 | ◾ 스프링 설정 정보로 인식 ◾ 스프링 빈의 싱글톤 유지를 위한 추가 처리 수행 |
내부적으로 @Component를 포함한다.
@Component
public @interface Controller {
}
@Component
public @interface Service {
}
@Component
public @interface Configuration {
}
💡 애노테이션에는 상속관계가 존재하지 않는다. 스프링에서 지원하는 기능을 통해 애노테이션에 적용된 특정 애노테이션이 인식 가능한 것이다.
💡
userDefaultFilters
- 유효한 패키지 경로에서 스테레오 타입 애노테이션이 붙은 클래스를 빈으로 등록하는 속성 ex)
@Bean,@Component- 기본값은
truefalse로 지정시 기본 스캔 대상들 제외
includeFilters : 컴포넌트 스캔 대상을 추가로 지정excludeFilters : 컴포넌트 스캔에서 제외할 대상 지정Annotation으로 클래스 생성한다.
[src/test/java/hello/core/scan/filter/MyIncludeComponent.java]
package hello.core.scan.filter;
import java.lang.annotation.*;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyIncludeComponent {
}
[src/test/java/hello/core/scan/filter/MyExcludeComponent.java]
package hello.core.scan.filter;
import java.lang.annotation.*;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyExcludeComponent {
}
[src/test/java/hello/core/scan/filter/BeanA.java]
package hello.core.scan.filter;
@MyIncludeComponent
public class BeanA {
}
[src/test/java/hello/core/scan/filter/BeanB.java]
package hello.core.scan.filter;
@MyExcludeComponent
public class BeanB {
}
[src/test/java/hello/core/scan/filter/ComponentFilterAppConfigTest.java]
package hello.core.scan.filter;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
public class ComponentFilterAppConfigTest {
@Test
void filterScan() {
ApplicationContext ac = new AnnotationConfigApplicationContext(ComponentfilerAppConfig.class);
BeanA beanA = ac.getBean("beanA", BeanA.class);
assertThat(beanA).isNotNull();
//ac.getBean("beanB", BeanB.class); → 에러 발생
assertThrows(
NoSuchBeanDefinitionException.class,
() -> ac.getBean("beanB", BeanB.class));
}
@Configuration
@ComponentScan(
//BeanA 스프링 등록
includeFilters = @Filter(type = FilterType.ANNOTATION, classes = MyIncludeComponent.class),
//BeanB 스프링 등록 제외
excludeFilters = @Filter(type = FilterType.ANNOTATION, classes = MyExcludeComponent.class)
)
static class ComponentfilerAppConfig {
}
}
| 항목 | 설명 |
|---|---|
ANNOTATION | ◾ 기본값 → 생략 가능 ◾ 애노테이션을 인식하여 동작 ex) org.example.SomeAnnotation |
ASSIGNABLE_TYPE | 지정한 타입과 자식 타입 인식하여 동작 → 클래스 직접 지정 ex) org.example.SomeClass |
ASPECTJ | AspectJ 패턴 사용 ex) org.example..*Service+ |
REGEX | 정규 표현식 ex) org\.exampe\.Default.* |
CUSTOM | TypeFilter 인터페이스 구현하여 처리ex) org.example.MyTypeFilter |
BeanA 제외하고 싶은 경우
@ComponentScan(
includeFilters = {
@Filter(type = FilterType.ANNOTATION, classes = MyIncludeComponent.class)
},
excludeFilters = {
@Filter(type = FilterType.ANNOTATION, classes = MyExcludeComponent.class),
@Filter(type = FilterType.ASSIGNABLE_TYPE, classes = BeanA.class)
}
)
💡 참고
includeFilters는 거의 사용되지 않음excludeFilters는 간혹 사용될 때가 있지만 많지 않음- 스프링 부트에서 컴포넌트 스캔을 기본으로 제공
→ 스프링의 기본 설정에 최대한 맞추어 사용하는 것을 권장
컴포넌트 스캔에서 같은 빈 이름을 등록하면 어떻게 될까?
ConflictingBeanDefinitionException 예외 발생@Component //기본 이름 → memoryMemberRepository
public class MemoryMemberRepository implements MemberRepository {}
@Configuration
@ComponentScan {
excludeFilters = @Filter(type = FilterType.ANNOTATION, classes = Configuration.class)
}
public class AutoAppConfig {
@Bean(name = "memoryMemberRepository")
pubic MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
}
Overrriding bean definition for bean 'memoryMemberRepository'
with a different definition: replacing
false로 변경함CoreApplication을 실행하면 다음과 같은 오류가 발생함Consider renaming one of the beans or enabling overriding by setting
spring.main.allow-bean-definition-overriding=true
application.properties에 등록하면 됨
📖 참고