<탄생배경>
지금까지 스프링 빈을 등록할 때는 자바코드의 @Bean이나 XML의 <bean>을 통해서 설정 정보에 직접 등록할 스프링 빈을 나열했다.
이렇게 등록해야할 스프링 빈이 수십, 수백개가 되면 일일이 등록하기 어려워진다.
→ 스프링은 설정정보가 없어도 자동으로 스프링 빈을 등록하는 @ComponentScan이라는 기능을 제공한다.
+@ 의존관계도 자동으로 주입하는 @Autowired라는 기능도 제공한다.
@ComponentScan이란?
컴포넌트 스캔은 이름 그대로 `@Component` 애노테이션이 붙은 클래스를 스캔해서 스프링 빈으로 등록한다.
이제 각 클래스가 컴포넌트 스캔의 대상이 되도록 `@Component` 애노테이션을 붙여주자.
※예제를 시행할 때, 주의할 점
'excludeFilters'를 통해 컴포넌트 스캔 대상에서 따로 @Configuration 정보를 제거하기.
(안그러면 예제코드 다 다시짜야함... ) - 최대한 @Configuration으로 짜뒀던 예제코드를 지키기 위해...
package hello.core;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
@Configuration
@ComponentScan(
//@ComponentScan으로 다 자동으로 bean을 등록하는데 제외할것을 설정하는 코드
//@Configuration은 우리가 수동으로 bean을 등록하는 곳인데 자동으로 등록되면 큰일남을 이해해야함.
excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Configuration.class)
)
public class AutoAppConfig {
}
다음 예제코드에서
@Configuration을 타고 들어가보면
다음과 같이 @Component 어노테이션을 보유한 것을 확인할 수 있다.
→ @ComponentScan을 사용하면 @Configuration이 붙은 설정 정보도 자동으로 등록되기 때문에, @Configuration에서 우리가 수동으로 설정한 @Bean 설정정보들도 함께 등록되는 대참사가 벌어진다.
결론 : 'excludeFilters'를 통해 컴포넌트 스캔 대상에서 따로 @Configuration 정보를 제거해주는 세심함이 필요하다.
@Autowired
자동의존관계주입 = ac.getBean(MemberRepository memberRepository) 와 같은 효과
설명 : 이전에 AppConfig에서는 `@Bean` 으로 직접 설정 정보를 작성했고, 의존관계도 직접 명시했다. 이제는 이런 설
정 정보 자체가 없기 때문에, 의존관계 주입도 이 클래스 안에서 해결해야 한다.
의존관계 주입(Dependency Injection)은 객체 간의 의존 관계를 코드 내에서 직접 설정하는 것이 아닌 외부에서 객체를 생성하고 주입하는 방식이다. 이는 객체 간의 결합도를 낮추고 유연한 코드를 작성하는 데 도움이 된다.
@Autowired 어노테이션은 스프링 프레임워크에서 제공하는 의존성 주입 기능을 사용할 때 사용하는 어노테이션이다. @Autowired 어노테이션은 생성자, 필드, 메서드 등 다양한 위치에 붙일 수 있다.
따라서 @Autowired 어노테이션은 의존 객체를 주입받을 대상에 붙여주시면 된다.
<예제코드>
package hello.core.member;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class MemberServiceImpl implements MemberService{
private final MemberRepository memberRepository;
//자동의존관계주입(Autowired) = 마치 ac.getBean(MemberRepository memberRepository) 와 같다.
@Autowired
//생성자 -> 여기서는 추상화에만 의존하는 껍데기만 설정 (just 생성자 주입) -> config에서 자세히 설정할 것.
public MemberServiceImpl(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
@Override
public void join(Member member) {
memberRepository.save(member);
}
@Override
public Member findMember(Long memberId) {
return memberRepository.findById(memberId);
}
// 테스트 용도
public MemberRepository getMemberRepository() {
return memberRepository;
}
}
이렇게 @Component가 달린 클래스를 스캔해서 스프링 빈으로 등록하는데
@Autowired를 지정하면, 스프링 컨테이너가 자동으로 해당 스프링 빈을 찾아서 주입한다.
※생성자에 파라미터가 많아도 다 자동으로 찾아서 주입한다.
<예제>
1. AutoAppConfig라는 이름으로 클래스를 만들고 @ComponentScan어노테이션을 붙혀준다.
package hello.core;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
@Configuration
@ComponentScan(
//@ComponentScan으로 다 자동으로 bean을 등록하는데 제외할것을 설정하는 코드
//@Configuration은 우리가 수동으로 bean을 등록하는 곳인데 자동으로 등록되면 큰일남을 이해해야함.
excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Configuration.class)
)
public class AutoAppConfig {
}
2. @Component를 대상 class에 모두 달아서 @ComponentScan대상임을 명시해준다.
3. @Autowired를 통해 의존관계를 자동으로 주입한다.
(예시)
4. 테스트 코드를 실행하여 스프링 컨테이너가 잘 스프링빈을 인식했는지 확인한다.
<실행코드>
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.annotation.AnnotationConfigApplicationContext;
public class AutoAppConfigTest {
@Test
void basicScan() {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AutoAppConfig.class);
MemberService memberService = ac.getBean(MemberService.class);
Assertions.assertThat(memberService).isInstanceOf(MemberService.class);
}
}
<실행결과>
설명하자면 @Component를 달아줬던 autoAppConfig / rateDiscountPolicy / memberServiceImpl / memberMemberRepository 4개의 클래스를 @ComponentScan이 인식하여
→ AutoAppConfig라는 클래스에 싱글톤 bean으로 등록하였다.
※ 이때 스프링 빈의 기본 이름은 클래스명을 사용하되 맨 앞글자만 소문자를 사용한다.
**빈 이름 기본 전략: MemberServiceImpl 클래스는 → memberServiceImpl로
**빈 이름 직접 지정: 만약 스프링 빈의 이름을 직접 지정하고 싶으면 `@Component("memberService2")` 이런식으로 이름을 부여하면 된다.
또한 @Autowired를 달아주었던 MemberServiceImpl과 OrderServiceImpl은 반드시 의존관계를 주입받아야한다.
스프링 컨테이너가 MemberRepository와 DiscountPolicy를 뒤질 것이고 일차적으로 같은 '타입'을 찾아 조회한다.
따라서 MemberRepository의 자식 타입인 memoryMemberRepository와 DiscountPolicy의 자식 타입인 rateDiscountPolicy가 @Autowired에 의해 주입된다.
@ComponentScan의 basePackages 설정
우리는 basePackages를 통해 @ComponentScan범위를 제한할 수 있다.
→ 정말 많은 자바 코드와 라이브러리를 다 뒤지면 속도와 효율성 측면에 위배되기 때문에 적절한 제한은 프로세스에 큰 도움이 된다.
다음과 같이 설정하고 Test코드를 돌리면 탐색할 패키지를 hello.core.member로 지정하고 그 하위패키지를 탐색한다.
다음과 같이 core.member 아래 패키지만 자동스캔이 되는 것을 확인할 수 있다.
@ComponentScan의 basePackageClasses 추가 설정
다음과 같이 추가 코드를 작성하고 테스트 코드를 실행하면 지정한 클래스의 패키지를 탐색 시작 위치로 지정한다.
만약 지정하지 않으면 `@ComponentScan` 이 붙은 설정 정보 클래스의 패키지가 시작 위치가 된다.
적절히 사용하도록 하자.
※ 참고로 스프링 부트를 사용하면 스프링 부트의 대표 시작 정보인 `@SpringBootApplication` 를 이 프로젝트 시작
루트 위치에 두는 것이 관례이다. (그리고 이 설정안에 바로 `@ComponentScan` 이 들어있다!)
<증명>
스프링부트를 처음 생성하면 CoreApplication 클래스가 생기는데 여기에 붙은 @SpringBootApplication 어노테이션을 파고들어가보면
@ComponentScan이 디폴트로 담겨있다. (스프링 부트는 다 계획이 있다... 관례가 괜히 생긴 것이 아님.)
컴포넌트 스캔 대상
컴포넌트 스캔은 @Component뿐만 아니라 다음의 내용도 포함한다.
- @Controller : 스프링 MVC컨트롤러에서 사용
- @Service : 스프링 비즈니스 로직에서 사용
- @Repository : 스프링 데이터 접근 계층에서 사용
- @Configuration : 스프링 설정 정보에서 사용
얘들을 붙혀놓으면 자동으로 @ComponentScan대상이 된다.
@Configuration과 @Component는 이미 이전에 확인했으니 나머지를 확인해보자
(Ctrl + N을 누르면 인텔리제이 內 클래스 검색이 가능하다.)
1. @Controller : Spring MVC컨트롤러임을 확인가능
2. @Service : 특별한 처리없음. 다만 개발자들이 핵심 비즈니스 로직임을 암묵적으로 명시함.
3. @Repository : 데이터 계층 예외를 스프링 예외로 변환해준다.
(ex: oracle쓰다가 mysql로 변경 시 데이터 계층 자체가 흔들릴 수 있는데, 스프링이 이를 막기위해 추상화해서 반환해줌)
'Develop > Spring (이론)' 카테고리의 다른 글
@componentScan 중복 등록과 충돌 (0) | 2023.08.20 |
---|---|
@Configuration과 싱글톤 (0) | 2023.08.17 |
싱글톤(Singleton) 딥다이브 ( Spring 內 싱글톤 컨테이너 비교) (0) | 2023.08.12 |
스프링 빈 설정 메타 정보 _ BeanDefinition (0) | 2023.08.11 |
스프링의 다양한 설정 형식 지원 - 자바 코드 , XM (0) | 2023.08.10 |