당니의 개발자 스토리

스프링 빈 조회 - 기본 본문

스프링/스프링 핵심 원리 - 기본편

스프링 빈 조회 - 기본

clainy 2024. 1. 20. 14:23

스프링 빈 조회 - 기본

이번 시간에는 스프링 빈을 조회하는 가장 기본적인 조회 방법부터 하나씩 알아보겠습니다.

빈을 조회하는 가장 간단한 방법은 ac.getBean(빈이름, 타입) 이라는 메서드를 쓰면 되구요. 파라미터로 빈 이름과 타입을 주면 됩니다. 또는 ac.getBean(타입)처럼 빈 이름을 생략하고 타입만 줘도 됩니다.

그런데 조회하려고 하는 스프링 빈이 없을 수도 있죠. 그러면 NoSuchBeanDefinitionException 이라는 예외가 나요.


바로 코드로 직접 보겠습니다.

beanfind에다가 ApplicationContextBasicFindTest 라고 할게요. public은 지워도 됩니다.

cmd + e 해서, ApplicationContextInfoTest를 보고 스프링 컨테이너를 생성하는 코드를 복붙 해줍시다.

그리고 DisplayName을 "빈 이름으로 조회" 라고 하고, findBeanByName 라는 메서드를 만들어서, 이름으로 조회하는 것을 보여드릴게요.

그 다음에 soutv 해서

찍어보고 한번 돌려보겠습니다. 객체 자체를 호출하면 toString()이 호출된다고 말씀드렸었죠.

메서드 안에서 cmd + R 하면, 이 메서드가 돌아갑니다.

우리가 기대했던 대로 잘 나왔죠? MemberServiceImpl 나오고 클래스 타입도 MemberServiceImpl로 제대로 잘 나왔습니다.


getClass() 메서드


이제 검증을 해볼 건데, 검증은 Assertions로 하라고 했죠? sout 부분을 주석처리 하고,

우리가 꺼냈던 memberService가 isInstanceOf 해서, 어떤 객체의 인스턴스인지,

MemberServiceImpl의 인스턴스가 맞는지 검증하는 겁니다. 돌려보면,

성공했습니다. 따라서, memberServiceMemberServiceImpl의 인스턴스라는 게 검증된 겁니다.

이 Assertions는 static import 해서 지우겠습니다.

어지간한 건 cmd + option으로 해결됩니다.

이렇게 하면 완성입니다.


두 번째는 이름 없이 타입으로만 조회를 해보겠습니다.

cmd + d 로 테스트를 복사 붙여넣기 해줍니다.

이렇게 고쳐주시구요. 그 다음에 getBean에서 전달해줬던 이름을 그냥 빼는 거에요.

이렇게 하면 훨씬 편리하겠죠. 장단점이 있는데 이건 뒤에서 설명드리겠습니다. 타입으로 하면, 같은 타입들이 여러 개일 경우 좀 곤란해지거든요.

이것도 성공했습니다.


그 다음에는 뭐로 조회할 거냐면, 위에서는 MemberService라는 인터페이스로 조회했죠. 이렇게 인터페이스로 조회를 하면, 이 인터페이스의 구현체가 대상이 되거든요?

그런데 구체 타입으로 조회할 수도 있습니다.

이렇게 인터페이스 명이 아닌, 구현체 타입으로 조회해도 돼요. 스프링 컨테이너에 MemberServiceImpl 객체가 등록이 돼있으면, 그냥 조회가 돼요.

실행해보면 성공합니다. 이거는 왜 보여드렸냐면,

AppConfig에서 이 반환 타입을 꼭 전달해줘야하는 건 아니에요. 스프링 빈에 등록된 인스턴스 타입을 보고 결정하기 때문에 꼭 여기 있는 인터페이스 이름이 아니어도 됩니다.

물론 구체적으로 적는 거는 안 좋아요. 항상 역할과 구현을 구분해야 된다.

그리고 역할에 의존해야 된다(DIP). 기억나시죠?

그런데 이렇게 하면, 이거는 구현에 의존한 거죠.

그래서 역할에 의존하도록 해야 되기 때문에 이 코드는 별로 좋은 코드는 아닌데, 가끔 살다 보면은 모든 게 다 이상적으로 돌아가지 않을 때가 있죠.


만약 조회가 안되면?

항상 테스트는 실패 테스트를 함께 만들어야 됩니다.

그럼 내가 getBean 했는데, xxxxx 라는 이름의 MemberService가 없죠.

그냥 이렇게 하고 돌려보면,

에러가 터지죠. NoSuchBeanDefinitionException 이라고 해서 xxxxx 라는 이름의 이용 가능한 빈이 없다고 합니다. 즉, xxxxx라는 이름의 빈이 등록한 적이 없는 겁니다.

그럼 이렇게 예외가 터지는 것도 테스트 코드로 검증해야 되는데, 아 이게 JUnit 5 여서 좀 지저분해졌어요.

AssertionsassertThrow 라는 Java 8의 람다 기능을 쓰는데, Assertionsorg.junit.jupiter.api 겁니다.

이렇게 하고, 앞에가 너무 길기 때문에 static import로 빼겠습니다.

static import로 빼준 게 2개가 됩니다. 이렇게 해놓고,

NoSuchBeanDefinitionException를 파라미터로 적어주면, 무조건 이 예외가 터져야 된다는 거예요. 즉, 이 예외가 터져야 테스트가 성공하는 거에요. 자 그런데 어떤 로직을 실행했을 때?

그걸 오른쪽에다가 람다식을 넣어주는 거죠.

ac.getBean("xxxxx", MemberService.class); 

현재 이 로직을 실행하면, 왼쪽의 예외가 터지는 거에요.

정리하면, 오른쪽의 로직을 실행하면 왼쪽에 있는 예외가 터져야된다는 겁니다. assertThrows, 즉 예외가 던져져야 된다는 거죠. 테스트를 돌려보면,

성공했습니다.

참고로 구체 타입으로 조회하면 유연성이 떨어진다는 거 말씀드렸었고, 기본적인 조회는 간단하죠. 이걸 가지고 개발하면 되겠죠.


다음 시간에는 만약에 동일한 타입이 두 개 이상 있으면 어떻게 되는지 좀 복잡한 상황에 대해서 설명을 드리겠습니다.