[Spring] Bean 수동 등록 사용

이병수·2024년 1월 24일
0

스프링 정리

목록 보기
14/24
post-thumbnail

Bean 수동 등록이란


@Component 어노테이션을 사용하게 되면 처음 스프링 서버가 시작이 될 때 @ComponentScan 어노테이션에 의해 자동으로 스캔되어 해당 클래스를 Bean으로 등록한다.

@Component 어노테이션이 부여된 어노테이션들은 @Controller, @Service, @Repository 등이 있다.

일반적으로는 @Component 어노테이션을 사용하여 Bean을 자동으로 등록하는 것이 좋지만, 기술적인 문제나 공통적인 관심사를 처리할 때 사용하는 객체들은 수동으로 등록하는 것이 좋다.

  • 공통 로그처리와 같은 비즈니스 로직을 지원하기 위한 부가적이고 공통적인 기능들을 기술 지원Bean 이라고 부르며, 수동 등록을 한다.

  • 수동 등록된 Bean에서 문제가 발생할 경우 해당 위치를 파악하기 쉽다.



Bean 수동 등록하는 방법

@Configuration
public class TestConfig{
	@Bean
    public Test testMethod() {
    	return new Test();
    }
}
  • Bean으로 등록하고자 하는 객체를 반환하는 메서드를 선언하고, 해당 메서드 위에 @Bean 어노테이션을 설정한다.

  • Bean을 등록한 메서드가 속한 해당 클래스 위에 @Configuration 어노테이션을 설정한다.

  • 스프링 서버가 뜰 때 스프링 컨테이너 안에 이름과 같이 Bean으로 저장이 된다.

  • 'Bean' 이름 : public Test testMethod() {...} -> testMethod

이렇게 수동으로 등록된 빈을 다른 클래스 내의 @Autowired에서 DI 해주면 된다.



같은 타입의 Bean이 두개라면?

예시를 들어보겠다.

public interface Computer {
	void info();
}

Computer라는 인터페이스가 있다고 가정을 하자

하나는 이 컴퓨터 인터페이스를 구현한 최신식 컴퓨터이고, 또 하나는 이 컴퓨터 인터페이스를 구현한 조선식 컴퓨터이다.

@Component
public class NewComputer implements Computer {
	@Override
    public void info() {
    	System.out.println("겁나 빠른 최신 컴퓨터");
    }
}

@Component
public class ChosunComputer implements Computer {
	@Override
    public void info() {
    	System.out.println("겁나 느린 조선 컴퓨터");
    }
}

Computer 인터페이스를 구현한 이 두 구현체 위에 @Component 어노테이션을 부여하게 된다면 컴포넌트 스캔을 할 때, 해당 클래스를 빈으로 등록한다.

이제 테스트를 한번 해보자.

@Autowired
Computer computer; // -> 여기서부터 오류가 발생한다. 

@Test
@Display("테스트")
void test1() {

}

테스트 환경에서 오류가 발생한다.
해당 오류를 보면 이렇게 출력이 나온다.

Could not autowire. There is more than one bean of 'Computer' type.

즉, "Computer 타입의 Bean 객체가 하나 이상 있다" 이 말이다.
따라서 어떤 Bean으로 DI를 해줘야할 지 모른다는 의미이다.

이 문제를 해결하기 위해 3가지 방법이 있다.


등록된 Bean 이름 명시하기

@SpringBootTest
public class Test {
	@Autowired
    Computer newComputer;
    
    @Autowired
    Computer chosunComputer;
}
  • 직접 등록된 Bean의 이름을 명시하면 해결해 줄 수 있다.

  • 이를 통해 알 수 있는 점은

    • @Autowired는 기본적으로 Bean Type으로 DI를 지원한다는 것이다.

    • 하지만 연결이 되지 않을 경우 Bean Name으로 찾는다는 것을 확인할 수 있다.



@Primary 어노테이션 사용

위에서 인터페이스를 구현한 두 클래스 중 한 클래스 위에 @Primary 어노테이션을 붙인다.

@Component
@Primary
public class NewComputer implements Computer {
	@Override
    public void info() {
    	System.out.println("겁나 빠른 최신 컴퓨터");
    }
}
  • 즉, 이 NewComputer를 우선적으로 사용한다는 것이다.
@SpringBootTest
public class Test {
	@Autowired
    Computer computer;
}
  • @Primary 어노테이션이 추가되면 같은 타입의 Bean이 여러개 있더라도 우선적으로 @Primary가 설정된 Bean 객체를 DI 해준다.


@Qualifier 어노테이션 사용

마지막으로 @Qualifier 어노테이션을 사용하는 것이다.

지금 현재 NewComputer 클래스에 @Primary 어노테이션을 사용하니 ChosunComputer 클래스에 붙여주자.

@Component
@Qualifier("chosunComputer")
public class ChosunComputer implements Computer {
	@Override
    public void info() {
    	System.out.println("겁나 느린 조선 컴퓨터");
    }
}

그리고 나서 주입하고자 하는 필드에 @Qualifier("chosunComputer") 를 추가해주면 해당 Bean 객체가 주입이 된다.

@SpringBootTest
public class Test {
	@Autowired
    @Qualifier("chosunComputer")
    Computer computer;
}
  • 이를 통해 확인할 수 있는점이 있다.

  • 우선적으로 @Primary 보다 @Qualifier 어노테이션의 우선순위가 높다는 것을 확인할 수 있다.

  • 그리고 @Qualifier는 적용하기 위해 주입하고자 하는 곳에 해당 @Qualifier를 반드시 추가해줘야 한다는 것이다.



@Primary vs @Qualifier

같은 타입의 Bean이 여러개 있을 경우, 범용적으로 사용하는 Bean 객체는 @Primary를, 지엽적으로 사용하는 Bean 객체는 @Qualifier 를 사용하는 것이 좋다.

profile
백엔드 개발자가 되고 싶어요

0개의 댓글