본문 바로가기
공부/springboot

(6)빈 자동 등록과 자동 주입

by 샤샤샤샤 2023. 5. 15.

@ComponentScan 과 @Autowired

 개발자의 편의를 위해 @Configuration 클래스 내에서 @Bean을 통해 수동으로 등록하지 않고 자동으로 등록하는 방법이 존재한다.

@ComponentScan 어노테이션을 사용하면, @Component 어노테이션이 붙은 클래스가 자동으로 빈으로 등록된다. 빈 이름은 첫글자가 소문자인 클래스 이름 그대로 설정된다. 

 

#AutoAppConfig

@Configuration
@ComponentScan(excludeFilters = @Filter(type = FilterType.ANNOTATION, classes = Configuration.class))
public class AutoAppConfig {
}

// 기존에 만든 Config 클래스를 제외하기 위한 excludeFilters 설정이 추가되었다.
// 보통은 설정 파일이 하나이기 때문에 따로 설정해주지 않고 @ComponentScan만 적어줘도 된다.

#A

@Component
public class A implements Alphabet {

    @Override
    public void className() {
        System.out.println("A");
    }
}

AutoAppConfig를 스프링 컨테이너에 등록할 경우, A클래스가 빈으로 등록된다. 이렇게 등록된 빈은 @Autowired 어노테이션을 통해 주입받을수 있다.

 

#OneAlphabet

@Component
public class OneAlphabet {
    private Alphabet a;

    @Autowired
    public OneAlphabet(Alphabet a) {
        this.a = a;
    }
}

#테스트 코드

	@Test
	void autoAppConfigTest(){
		ApplicationContext ac = new AnnotationConfigApplicationContext(AutoAppConfig.class);
		Alphabet a = ac.getBean("a", Alphabet.class);
		a.className();
	}
   // 출력값: A

**만약 빈 이름을 직접 등록하고 싶으면 @Component("빈 이름") 처럼 하면 된다.

** 스프링 파일의 경우 자동적으로 @SpringbootTest 어노테이션이 붙은 테스트 파일이 생성된다. 이곳에서 위의 테스트 코드를 실행할시 오류가 발생한다. @SpringbootTest 어노테이션은 @SpringBootApplication 어노테이션의 테스트 버전이라고 볼 수 있는데, 상속받는 부모 클래스에 이미 @ComponentScan이 포함되 있어 이중으로 빈에 등록하게 되기 때문이다.

@ComponentScan 이 이미 포함되어 있다.

** @Autowired를 사용하면 파라미터가 많더라도 전부 컨테이너에서 찾아서 자동으로 주입한다.

 

 

@ComponentScan의 탐색 위치 지정

모든 자바 클래스를 전부 스캔하면 시간이 오래 걸린다. 따라서 탐색 시작 위치를 지정할수 있다.

@ComponentScan( basePackages = "패키지 이름",}

basePackages : 해당 패키지를 포함하여 하위 패키지를 전부 스캔 대상에 포함시킨다.

basePackageClasses : 지정된 클래스의 패키지를 시작 위치로 정한다.

기본값 : 지정되지 않았다면 @ComponentScan이 붙은 설정 정보 패키지가 시작 위치가 된다.

프로젝트 최상단에 설정 클래스를 두고 스캔하는 것을 권장한다.

 

본래 어노테이션은 상속이 안되지만, 스프링은 지원하는 것처럼 사용 가능하게 만들어졌다.@Component 를 상속받는 어노테이션은 아래와 같다.

@Controlller : 스프링 MVC 컨트롤러에서 사용

@Service : 스프링 비즈니스 로직에서 사용

@Repository : 스프링 데이터 접근 계층에서 사용

@Configuration : 스프링 설정 정보에서 사용

 

 

필터를 통한 컴포넌트 스캔 제외와 추가

 필터를 통해 컴포넌트 스캔에 추가시키거나 제외시키는 것이 가능하다.

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

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

 

 필터의 옵션은 아래와 같다.

 

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

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

3. ASPECTJ: AspectJ 패턴 사용

4. REGEX: 정규 표현식

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

 

1번의 경우, 직접 어노테이션을 작성해서 제외시킬수도 있다. 코드로 살펴보자.

 

# 포함시키기 위한 어노테이션

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyIncludeFilter {
}

# 제외시키기 위한 어노테이션

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyExcludeFilter {
}

# 컴포넌트 스캔 설정 추가

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

 

위와 같은 설정을 사용하면 특정 어노테이션이 붙은 클래스를 제외, 추가할수 있다.

클래스 타입으로도 이런 설정이 가능하다.

@ComponentScan(
excludeFilters = { @Filter(type = FilterType.ASSIGNABLE_TYPE, classes = Alphabet.class)
 }
)
public class AutoAppConfig{
    ....
}

 

빈 중복 등록

같은 이름의 빈을 중복으로 등록하려고 할 시, 충돌이 발생한다.

과거 버전의 스프링은 수동 빈 vs 자동 빈의 경우, 수동으로 등록한 빈이 자동 등록 빈을 덮어씌우는 방식으로 작동했으나, 최신 버전으로 오면서 무조건 충돌이 일어나서 개발자가 손쉽게 확인할수 있도록 업데이트 되었다.