지난 번글에서 스프링 빈과 스프링 컨테이너를 사용하는 이유, 작동원리에 대해 썼다.

이번 글에선 스프링 빈을 어떤식으로 등록하고 사용하는지 조금 더 자세하게 써보려고 한다.

 

인스턴스를 스프링 빈으로 등록하는 방법

1. 수동 등록

 

원하는 객체 인스턴스를 하나씩 직접 스프링 빈으로 등록하는 방법.

Xml, 지난 번에 사용했던 Configuration 어노테이션을 사용한 방법 등이 있다.

지난번 글에서 Xml 을 사용하는 방법은 최근에는 거의 쓰이지 않고 있으므로 Configuration 을 사용하는 방법만 간단하게 짚고 넘어갔다.

//AppConfig
@Configuration
public class AppConfig{
    // 설정 내용
    @Bean
    public MemberService memberService(){
        return new MemberServiceImpl(memberRepository());
    }
    @Bean
    public MemberRepository memberRepository(){
        return new MemoryMemberRepository();
    }
    .
    .

Config 클래스를 만들고 Configuration 어노테이션을 붙인 다음 Bean 어노테이션과 메소드를 통해 빈을 등록하면 된다.

메소드 이름으로 빈 이름을 정할 수 있고 의존관계 또한 메소드를 그대로 사용하면 된다.

 

단점 : 빈을 개발자가 일일히 등록해야함 -> 규모가 커지면 누락 및 오류 발생 가능성 커짐

 

2. 자동 등록

 

수동 등록의 단점을 해결하기 위해 스프링에서 제공하는 기능이다.

//AutoAppConfig
package project;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import static org.springframework.context.annotation.ComponentScan.*;

@Configuration
@ComponentScan
public class AutoAppConfig {
}

설정 파일 앞에 ComponentScan 어노테이션을 붙여주기만 하면 된다.

AutoAppConfig라는 클래스를 만들고 Configuration과 ComponentScan 어노테이션을 붙여줬다.

컴포넌트 스캔은 이름 그대로 Component 어노테이션이 붙은 클래스를 스캔해서 스프링 빈으로 등록한다

//MemberServiceImpl
package project.member;
import org.springframework.stereotype.Component;

@Component
public  class MemberServiceImpl implements MemberService{
    private final MemberRepository memberRepository;
    
    public MemberServiceImpl(MemberRepository memberRepository){
        this.memberRepository = memberRepository;
    }
    
    public void join(Member member){
        memberRepository.save(member);
    }
    
    public Member findMember(Long id){
        return memberRepository.findById(id);
    }
}
//MemoryMemberRepository
package project.member;

import org.springframework.stereotype.Component;
import java.util.Map;
import java.util.HashMap;

@Component
public class MemoryMemberRepository implements MemberRepository{
    private static Map<Long, Member> store = new HashMap<> ();
    
    @Override
    public void save(Member member){
        store.put(member.getId(), member);
    }
    @Override
    public Member findById(Long id){
        return store.get(id);
    }
}

빈으로 등록하길 원하는 클래스 앞에 Component 어노테이션을 붙여주기만 하면 끝이다.

이러면 스프링이 자동으로 Component 어노테이션이 붙은 클래스들을 찾아서 빈으로 등록한다.

이때 기본 이름은 클래스 이름에서 맨 앞글자만 소문자로 바꾼 형태이며

Component("beanName") 과 같은 방식으로 직접 등록할 수도 있다.

 

자동 등록 시의 의존관계 설정은 @Autowired 어노테이션을 사용한다.

Autowired와 자동 의존관계 설정에 관해선 다음 글에서 좀 더 자세히 다룰 예정이다.

 

스캔 범위 지정하기

모든 자바 클래스를 다 컴포넌트 스캔하면 시간이 오래 걸린다.

그래서 꼭 필요한 위치부터 탐색하도록 시작 위치를 지정할 수 있다.

//basePackages
//탐색할 패기지의 시작 위치 지정. 이 패키지를 포함한 하위 패키지를 모두 탐색
@ComponentScan(basePackages = "project")
//여러 시작 위치 지정도 가능
@ComponentScan(basePackages = {"project.core", "project.service"})

//basePackageClasses
//지정한 클래스의 패키지를 탐색 시작 위치로 지정한다
@ComponentScan(basePackagesClasses = MemberServiceImpl.Class)

만약 시작 위치를 지정하지 않으면 @ComponentScan이 붙은 설정 정보 클래스의 패키지가 시작 위치가 된다.

최근 스프링 부트의 기본 제공 방법은 패키지 위치를 지정하지 않고, 설정 정보 클래스 위치를 프로젝트 최상단에 두는 것이다.

 

필터

includeFilters : 컴포넌트 스캔 대상을 추가로 지정한다.

excludeFilters : 컴포넌트 스캔에서 제외할 대상을 지정한다.

@ComponentScan(
	includeFilters = {
		@Filter(type = FilterType.ANNOTATION, classes = MyIncludeComponent.class),
	},
	excludeFilters = {
		@Filter(type = FilterType.ANNOTATION, classes = MyExcludeComponent.class)
	}
)

FilterType은 5가지 옵션이 존재한다.

ANNOTATION: 기본값, 애노테이션을 인식해서 동작한다.

ASSIGNABLE_TYPE: 지정한 타입과 자식 타입을 인식해서 동작한다.

ASPECTJ: AspectJ 패턴 사용

REGEX: 정규 표현식

CUSTOM: TypeFilter 이라는 인터페이스를 구현해서 처리

 

 

중복 등록과 충돌

만약 컴포넌트 스캔에서 같은 빈 이름을 사용해 두 개 이상의 빈이 등록된다면 어떻게 될까?

1. 자동 빈 등록 방법에서만 같은 빈 이름이 두 개 생성됐을때 -> 스프링이 오류를 띄운다.

2. 수동으로 등록한 빈과 자동으로 등록한 빈의 이름이 겹칠때 -> 수동 등록한 빈이 우선권을 가진다.

그러나 최근에는 그냥 오류를 띄우도록 바뀌었다.

 

 

 

 

 

 

 

 

 

 

+ Recent posts