스프링 컨테이너와 스프링 빈

고동현·2024년 3월 27일
0

Spring 기본

목록 보기
4/10

스프링 컨테이너 생성

스프링 컨테이너가 생성되는 과정을 알아보자

ApplicationContext applicationContext = new 
AnnotationConfigApplicationContext(AppConfig.class)

우선 ApplicationContext는 인터페이스이다. 그렇기 때문에 구현체 new부분에는 Xml형식으로도, 애노테이션 기반의 자바 설정 클래스로도 구현이 가능하다.
우리는 AppConfig에서 애노테이션 기반으로 만들었으므로 AnnotationCOnfigApplicationContext를 사용하였다.

참고로, 스프링컨테이너를 부를때, BeanFactory와 ApplicationContext를 구분해서 말을하는데 우리는 거의 BeanFactory를 직접 사용하는 경우가 없으므로 일반적으로 ApplicationContext를 스프링 컨테이너라 ㅎ나다.

스프링 컨테이너의 생성과정

  1. 스프링 컨테이너 생성

    스프링 컨테이너에 스프링 빈저장소가 있는데 이 저장소를 채우기위해서 파라미터로 넘어온 AppConfig를 보고 채워준다.
  2. 스프링 빈 등록

    Bean이 붙은 메서드를 최초로 호출하면서, key value로 이름을 메서드명, 빈 객체를 반환값으로 넣어둔다.
    여기서 Bean name을 직접 부여할수 있다=> @Bean(name ="memberService2")
    그러나 반드시 빈 이름은 항상 다른 이름을 부여해야한다.
  3. 스프링 빈 의존관계 설정 - 준비
  4. 스프링 빈 의존관계 설정 - 완료

    스프링 컨테이너는 설정 정보를 보고 참고해서 DI를 한다.
    단순히 자바코드를 호출하는것이 아니다.
    원래는, 스프링에서는 빈을 생성하고, 의존관계를 주입하는 단계가 나뉘어 있다.
    그런데 이렇게 자바 코드로 스프링 빈을 등록하면 생성자를 호출하면서 의존관계 주입도 한번에 처리된다.
    자세한 내용은 뒤에서 설명하겠다.

컨테이너에 등록된 모든 빈 조회

스프링 컨테이너에 실제로 모든 빈들이 잘 등록되었는지 조회 해보도록 하겠다.

우선 스프링 컨테이너에 AppConfig에서 설정한 모든 빈들을 등록해준다.
그다음 Test부분으로 넘어가게 되는데
저기 DisplayName부분이 모든 애플리케이션 빈 출력이 아니고, 내가 설정한 빈 출력으로 바뀌어야한다... 죄송합니다..
그리고 for문 iter로 돌리면서 확인하는데
ac.getBeanDefinitionNames()를 통해서 모든 빈 이름을 조회해서 배열에 넣는다.
그다음에 for문을 도는데, beanDefinition.getRole을 통해서 빈의 종류에 대해서 가져올 수 있다.
ROLE_APPLICATION은 일반적으로 사용자가 정의한 빈
ROLE_INFRASTRUCTURE는 스프링 내부에서 사용하는 빈이다.
그래서 내가 정의한 빈만 조회하기 위해서 getRole()의 반환값이 ROLE_APPLICATION이 맞으면,
if문을 실행시키는데
ac.getBean을 통해 빈 이름으로 빈 객체를 조회하고, 그다음에 출력하는 과정을 실행했다.

스프링 빈 조회

빈 조회방법

  1. ac.getBean(빈이름,반환 타입)
  2. ac.getBean(반환 타입)
  3. 만약 조회 대상이 없으면 NoSuchBeanDefinitonException발생


빈 이름으로 조회는, ac.getBean으로 memberService라는 이름으로 가져왔을때, 이게 MemberServiceImpl 형식이냐를 확인했고,

이름없이 타입만으로 조회는
ac.getBean은 빈이름없이 타입으로만 조회가 가능하기 떄문에,
ac.getBean의 반환타입을 MemberService.class로 두고, 그러면 memberService가 나오는데 실제로 타입이 겹치고 이러면 안된다. 그러면 뭘 반환해야할지 모르니까.
그리고 이 memberService가 MemberServiceImple의 instance인지 확인하였다.

여기서 구체타입으로도 조회가 가능한데, 좋지 않다 왜? 역할과 구현을 나누었는데, 여기서 MemberServiceImpl.class를 가져다 쓰면 구현에 의존하므로, 역할인 MemberSerivce에 의존하는게 좋다.
이런 기능이 있다. 정도로 알아보기 위해서 사용하였다.

ac.getBean에서 반환타입을 구체클레스로 설정하였는데, 인터페이스가 아니라 구체로 하여도, 스프링컨테이너에 MemberServiceImpl이 등록되어있다면, 자동적으로 조회가 가능하다.

마지막으로 빈이름으로 조회했을때 없는것으로 확인해보는것인데
없는것으로 조회를 했을때 생기는 오류가 NoSuchBeanDefinitionException이다. 람다를 사용해서 ac.getBean을 하였을때 생기는 오류가 NoSuchBeanDefinitionException이 맞는지 확인하였다.

스프링 빈 조회 - 동일한 타입이 둘 이상

ac.getBeansofType()를 통해 인자의 해당 타입의 모든 빈을 조회할 수 있다.
우선, test class 내부에 임시 config를 만들도록 하겠다.

여기서 중요한 점은, 빈으로 등록할때, key에 해당하는 메서드 명은 다르지만, value는 동일한 MemoryMemberRepository라는 점이다.

참고로, static을 적으면, class내에서 class를 만들때, static을 쓰면 이 바깥 class의 scope내에서만 쓰겠다는 의미이다.


그리고 이 Appconfig.class가 아닌 SameBeanConfig.class를 인자로 넘겨주고,

첫번째 test는 동일한 타입으로 조회하는것이다.
MemberRepository에 해당하는 class가 현재 2개 있으므로, 뭘 가져와야할지 모르는 상태이다. 그러므로, notUniqueBeanDefinition오류가 발생한다.

두번째 Test는 MemberRepository는 맞는데 키에 해당하는 메서드명을 같이 적어주는것이다. 이러면 정상적으로 가져올 수 있다.


마지막으로 특정 타입을 모두 조회하는것이다.
getBeansofType을 통해서 해당하는 타입의 모든 빈들을 가져올 수 있다.
이때, 가져온 값들은 key,value 형식으로 Map에 저장이 된다.
올바르게 가져왔는지 iter를 통해서 확인 하였다.

스프링 빈 조회 - 상속 관계

빈을 조회할때, 부모타입으로 조회하면, 자식타입도 함께 조회한다.
여기서 궁금증이 생겨 또다른 글을 포스트 했으니 보면 좋을것 같다.
빈을 조회할때 왜 자식까지 조회를 해줄까?

우선 TestConfig.class를 만들어준다.

여기서 보면 알듯이, DiscountPolicy의 구현체로 Rate와 Fix를 구현해놨는데, 만약 Discount로 찾는다면 2개의 class가 있으니까 오류가 나올것을 예측할 수 있다. why? 그야, 둘중 뭘 가져와야할 지 모르니까.


첫번째 test는 그냥 DiscountPolicy.class type으로 빈을 조회하는것이다.
이렇게하면 Rate와 Fix 두가지의 Type이 존재하므로, NoUniqueBeanOfDefinition오류가 발생한다.

두번째 test는 동일하게 부모 Type과 key에 해당하는 메서드명을 같이 적어주는것이다.
당연히 이렇게하면 올바르게 조회가 가능하다.


마지막으로, DiscountPolicy 부모 type 으로 전부 조회하는것이다.
이렇게하면, 부모타입으로 조회시, 자식타입도 다 딸려나오므로, Map에 FixdiscountPolicy와 RatediscountPolicy둘다 확인이 가능하다.

getBeansOfType을 하면, 이전에 했던것처럼 Map형식으로 가져올 수 있다.
iter를 사용해서 확인하여 보았다.

참고로 모든 Java class 상위에는 Object가 있다.
즉 모든 클래스는 Object class의 자식 class이다.
어? 그럼 다른 class를 상속하는것을 명시하면요?
근데 그 부모class가 만약 다른 class를 상속받지 않는다면,=>명시되어있지 않는다면, Object를 상속받는다.

그래서 getBeansOfType에서 Object.class를 하면, Fix와 Rate 뿐만아니라 스프링 내부에서 사용하기위해 등록된 Bean까지 전부 튀어나온다.

BeanFactory와 ApplicationContext


BeanFactory

  • 스프링 컨테이너의 최상위 인터페이스이다.
  • 스프링 빈을 관리하고 조회한다.
  • getBean을 제공한다.

ApplicationContext

  • BeanFactory 기능을 모두 상속받아서 제공한다.

  • Bean과 관련된 기능 외에도 다른 인터페이스를 상속받아 지원한다.

  • 메세지 소스를 활용한 국제화 기능=>한국에서 접속시 한국어로, 영미권에서 들어오면 영어로 출력

  • 환경변수
    개발을 할때, 내컴퓨터에서하는 로컬 서버, 그다음에 test끼리 엮어서 검증가능한 개발 서버, 그다음에 실제 운영이 되는 운영서버가 따로 있다.
    그래서 생각해보면, test할때의 DB와 실제 User의 정보가 담겨있는 운영서버의 DB를 다르게 설정해야한다.
    이러한 환경변수를 처리해주기도 한다.

  • 애플리케이션 이벤트
    이벤트를 발행하고 구독하는 모델을 편리하게 지원

프로그래밍에서 이벤트란, 애플리케이션 내에서 발생시킬 수 있는 어떠한 사건을 의미한다. 애플리케이션에는 어떠한 이벤트를 발생시키는 주체와, 정해진 이벤트의 발생을 탐지해 동작을 처리하는 주체가 존재할 수 있다.

예를 들어, 경기를 생성했을 때, 경기 생성에 대한 알림을 같이 생성하는 경우를 생각해보자. 가장 간단하게 생각해볼 수 있는 구현 방식은 Game을 생성하는 CreateGameService에서 Game을 생성해 GameRepository에 저장하고, 이어서 Notice를 생성해 NoticeRepository에 저장하는 것이다.

  • 편리한 리소스 조회
    파일,클래스패스, 외부등에서 리소스를 편리하게 조회가 가능하다.

정리.

  1. ApplicationContext는 BeanFactory 기능을 상속받는다.
  2. ApplicationContext는 빈 관리기능 + 편리한 부가기능을 제공한다.
  3. BeanFactory를 직접 사용할 일은 거의 없다. 부가기능이 포함된 ApplicationContext를 사용한다.
  4. BeanFactory나 ApplicationContext를 스프링 컨테이너라한다.

다양한 설정 형식 지원 -자바 코드,xml


BeanFactory와 ApplicationContext가 당연히 인터페이스니까 구현체가 있어야된다.
여러가지 형식을 지원할수 있는데 에노테이션기반 context xml기반 context등 여러가지가 있다.
xml은 거의 사장된 기술이고,
혹시나 annation기반 conifg applicationcontext가 뭐지? 하고 까먹으신 분들을위해
우리는 이전에 AppConfig에서 @Configuration, @Bean으로 스프링컨테이너에 빈을 등록해두었다.
이처럼 Bean에노테이션으로 지정된 객체들을 가지고 있는것을 AnnotationConfig applicationContext이다.

스프링 빈 설정 메타 정보 - BeanDefinition

BeanDefinition을 빈 설정 메타정보라 한다.
@Bean 당 각각 하나의 메타 정보가 생성되는데 스프링 컨테이너가 이 메타정보를 기반으로 스프링 빈을 생성한다.

이처럼, AppConfig.class에서 설정정보를 보고, BeanDefinition을 생성한다.
그리고 이 Definition을 보고 스프링 컨테이너에서 빈을 생성한다.

beanDefintionNames 배열에 Bean등록 이름들이 들어간다.
그리고 이 배열에서 이름들을 이터레이터를 돌면서 하나씩 꺼낸다.
그리고 스프링이 사용하는 내부 빈이 아닌 ROLE_APPLICATION 내가 등록한 빈이면 beanDefintion을 출력하게 하였다.

결과

BeanClassName:생성할 빈의 클래스 명
factoryBeanName: 팩토리 역할의 빈을 사용한경우 이름=>appConfig
factoryMethodName: 빈을 생성할 팩토리 메서드 지정 =>memberService
Scope: 기본값 싱글톤
layInit: 스프링 컨테이너를 생성할때 빈을 생성하는게 아니라, 실제 빈을 사용할때 생성하는것
InitMethodName: 빈을 생성하고, 의존관계를 적용한 뒤에 호출되는 초기화 메서드명
DestoryMethodName: 빈의 생명주기가 끝나서 제거하기 직전에 호출되는 메서드명

정리
Spring은 BeanDefinition으로 Spring Bean의 메타정보를 추상화한다.
Spring Bean을 만들때는 두가지 방법이 있는데
하나는 직접적으로 Spring Bean을 등록하는방법=> 내가 직접 new BeanDefinition 해서 직접 등록하는 방식.=>거의 사용되지 않음

두번째는 Factory Bean을 사용하여서 등록하는 방법=>appConfig를 통해서 등록하는방법

profile
항상 Why?[왜썻는지] What?[이를 통해 무엇을 얻었는지 생각하겠습니다.]

0개의 댓글