LostCatBox

SpringCore CH04

Word count: 1.1kReading time: 6 min
2022/12/24 Share

스프링 핵심 원리 (기본편) CH04

Created Time: June 15, 2022 12:04 PM
Last Edited Time: June 23, 2022 3:17 PM

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

스크린샷 2022-06-15 오후 12.19.18.png

  • ApplicationContext가 스프링 컨테이너다!!

과정

  1. 스프링 컨테이너 생성

스크린샷 2022-06-15 오후 12.31.05.png

  1. 스프링 빈 등록(@Bean붙은것들 호출)(반드시 빈이름 무조건다르게하라)
    key: 메서드이름 , value:반환된객체 가되어 스프링 빈 저장소에 등록됨

    스크린샷 2022-06-15 오후 12.31.23.png

  2. 스프링 빈 의존관계 설정 준비
    (객체 생성함)

    스크린샷 2022-06-15 오후 12.31.33.png

  3. 스프링 빈 의존관계 설정 완료
    (의존관계 주입, 연결됨)

    스크린샷 2022-06-15 오후 12.31.45.png

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

  • 스프링 컨테이너에 실제 스프링 빈이 잘들어가있는지확인
  • 테스트코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package hello.core.beanfind;

import hello.core.AppConfig;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class ApplicationContextInfoTest {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);

@Test
@DisplayName("모든 빈 출력하기")
void findAllBean() {
String[] beanDefinitionNames = ac.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
Object bean = ac.getBean(beanDefinitionName); //getBean에서 타입지정없으므로 타입모름
System.out.println("beanDefinitionName = " + beanDefinitionName+"object"+bean);
}
}

@Test
@DisplayName("애플리케이션 빈 출력하기")
void findApplicationBean() {
String[] beanDefinitionNames = ac.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
BeanDefinition beanDefinition = ac.getBeanDefinition(beanDefinitionName);

//Role ROLE_APPLICATION: 직접 등록한 애플리케이션 빈
//Role ROLE_INFRASTRUCTURE: 스프링이 내부에서 사용하는 빈
if (beanDefinition.getRole() == BeanDefinition.ROLE_APPLICATION) { //내가 빈에 직접 등록한것들만
Object bean = ac.getBean(beanDefinitionName); //getBean에서 타입지정없으므로 타입모름
System.out.println("beanDefinitionName = " + beanDefinitionName + "object" + bean);
}
}

}
}

스프링 빈 조회 - 기본

  • 스프링 컨테이너에서 스프링 빈을 찾는 가장 기본적인 조회 방법

  • ac.getBean(빈이름, 타입)

  • ac.getBean(타입)

  • 조회 대상 스프링 빈이 없으면 예외 발생(NoSuch)

  • 테스트 코드로 보기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class ApplicationContextBasicFindTest {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);

@Test
@DisplayName("빈 이름으로조회")
void findBeanByName(){
MemberService memberService = ac.getBean("memberService", MemberService.class);
assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
}
@Test
@DisplayName("이름없이 타입으로만 조회")
void findBeanByType(){
MemberService memberService = ac.getBean(MemberService.class);
assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
}
@Test
@DisplayName("구체 타입으로 조회")
void findBeanByName2(){
MemberService memberService = ac.getBean("memberService", MemberServiceImpl.class);
assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
}

@Test
@DisplayName("실패 테스트 빈 이름으로 조회안됨")
void findBeanByNameX(){
assertThrows(NoSuchBeanDefinitionException.class,
()->ac.getBean("xxx", MemberService.class)); //오류남, 오류도 검증해야함)

}

}

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

  • 타입으로 조회시 같은 타입의 스프링 빈이 둘 이상이면 오류발생>> 빈 이름 지정하자
  • ac.getBeanOfType()을 사용하면 해당 타입의 모든 빈을 조회할수있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
package hello.core.beanfind;

import hello.core.AppConfig;
import hello.core.discount.DiscountPolicy;
import hello.core.member.MemberRepository;
import hello.core.member.MemberService;
import hello.core.member.MemoryMemberRepository;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.Map;

import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;

public class ApplicationContextSameFindTest {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(SameBeanConfig.class); //기존 AppConfig파일 변경하지않고 진행하기위해

@Test
@DisplayName("타입으로 조회시 같은 타입이 둘 이상있으면, 중복 오류 발생한다.")
void findBeanByTypeDuplicate(){

assertThrows(NoUniqueBeanDefinitionException.class,
()-> ac.getBean(MemberRepository.class)); // 아ㅐㄹ ㅋ컨
}

@Test
@DisplayName("타입으로 조회시 같은 타입이 둘 이상있으면, 이름을 지정해야한다")
void findBeanByName(){
MemberRepository memberRepository = ac.getBean("memberRepository1", MemberRepository.class);
assertThat(memberRepository).isInstanceOf(MemberRepository.class);
}

@Test
@DisplayName("특정 타입을 모두 조회하기")
void findAllBeanByType(){
Map<String, MemberRepository> beansOfType = ac.getBeansOfType(MemberRepository.class);
for (String key : beansOfType.keySet()) {
System.out.println("key = " + key + "value=" + beansOfType.get(key));
assertThat(beansOfType.size()).isEqualTo(2);

}
}

@Configuration
static class SameBeanConfig {
@Bean
public MemberRepository memberRepository1() {
return new MemoryMemberRepository();
}

@Bean
public MemberRepository memberRepository2() {
return new MemoryMemberRepository();
}
}
}

스프링 빈 조회 - 상속관계(!!!)

  • 부모 타입으로 조회하면, 자식 타입도 함께 조회한다.

  • 그래서 모든 자바 객체의 최고 부모인 Object 타입으로 조회하면, 모든 스프링 빈을 조회한다.

  • 실제 개발할때는 애플리케이션 스프링 컨테이너 직접들어가서 인잭션까지하지는 않는다. 하지만 지식적인요소

    스크린샷 2022-06-15 오후 5.05.02.png

  • 테스트 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
public class ApplicationContextExtendsFindTest {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(TestConfig.class);

@Test
@DisplayName("부모타입으로 조회시, 자식이 둘이상있으면, 중복 오류가 발생")
void findBeanByParentTypeDuplicate(){
assertThrows(NoUniqueBeanDefinitionException.class,
()->ac.getBean(DiscountPolicy.class));
}
@Test
@DisplayName("부모타입으로 조회시, 자식이 둘이상있으면, 빈 이름을 지정하면 된다.")
void findBeanByParentByName(){
DiscountPolicy rateDiscountPolicy = ac.getBean("rateDiscountPolicy", DiscountPolicy.class);
assertThat(rateDiscountPolicy).isInstanceOf(RateDiscountPolicy.class);
}

@Test
@DisplayName("특정 하위 타입으로 조회")
void findBeanBySubType (){
RateDiscountPolicy bean = ac.getBean(RateDiscountPolicy.class);
assertThat(bean).isInstanceOf(RateDiscountPolicy.class);
}

@Test
@DisplayName("부모 타입으로 모두 조회하기")
void findAllBeanByParentType(){
Map<String, DiscountPolicy> beansOfType = ac.getBeansOfType(DiscountPolicy.class);
assertThat(beansOfType.size()).isEqualTo(2);
for (String key : beansOfType.keySet()) {
System.out.println("key = " + key +"value="+beansOfType.get(key));
}
}

@Test
@DisplayName("부모 타입으로 모두 조회하기 - Object")
void findAllBeanByObjectType(){
Map<String, Object> beansOfType = ac.getBeansOfType(Object.class);
for (String key : beansOfType.keySet()) {
System.out.println("key = " + key+"value+"+beansOfType.get(key));
}
}

@Configuration
static class TestConfig{
@Bean
public DiscountPolicy rateDiscountPolicy(){
return new RateDiscountPolicy();
}
@Bean
public DiscountPolicy fixDiscountPolicy(){
return new FixDiscountPolicy();
}
}

}

BeanFactory와 ApplicationContext

스크린샷 2022-06-15 오후 5.59.24.png

BeanFactory

  • 스프링 컨테이너의 최상위 인터페이스
  • 스프링 빈을 관리하고 조회하는 역할(getBean()등등제공)

ApplicationContext

  • BeanFactory 모두 상속받아서 제공

  • 빈 관리 기능 + 많은 부가기능필요한것들 모두 제공

  • ApplicationContext만 사용한다고 생각하자

    스크린샷 2022-06-15 오후 6.00.29.png

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

  • 스프링 컨테이너는 다양한 형식의 설정 정보를 받아드릴수있게 유연하게 설정되어있다.

    스크린샷 2022-06-15 오후 6.11.43.png

애노테이션 기반 자바 코드 설정 사용

지금까지 했던 것이다.
new AnnotationConfigApplicationContext(AppConfig.class)
AnnotationConfigApplicationContext 클래스를 사용하면서 자바 코드로된 설정 정보를 넘기면 된다.

XML 설정 사용

최근에는 스프링 부트를 많이 사용하면서 XML기반의 설정은 잘 사용하지 않는다. 아직 많은 레거시프로젝트 들이 XML로 되어 있고, 또 XML을 사용하면 컴파일 없이 빈 설정 정보를 변경할 수 있는 장점도
있으므로 한번쯤 배워두는 것도 괜찮다.
GenericXmlApplicationContext 를 사용하면서 xml 설정 파일을 넘기면 된다.

파일생략

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

  • 스프링은 어떻게 이런 다양한 설정 형식을 지원하는 것일까? 그 중심에는 BeanDefinition 이라는 추상화가 있다.

  • 쉽게 이야기해서 역할과 구현을 개념적으로 나눈 것이다! XML을 읽어서 BeanDefinition을 만들면 된다. 자바 코드를 읽어서 BeanDefinition을 만들면 된다. 스프링 컨테이너는 자바 코드인지, XML인지 몰라도 된다. 오직 BeanDefinition만 알면 된다.

  • BeanDefinition 을 빈 설정 메타정보라 한다.
    @Bean , 당 각각 하나씩 메타 정보가 생성된다.

  • 스프링 컨테이너는 이 메타정보를 기반으로 스프링 빈을 생성한다.

    스크린샷 2022-06-15 오후 6.44.20.png

  • ApplicationContext생성과정
    (각Reader가 Config파일을 읽어서 각각의 BeanDefinition을 만듬)

스크린샷 2022-06-15 오후 6.45.53.png

BeanDefinition정보

  • 이를 기반으로 스프링 컨테이너에 등록됨.

스크린샷 2022-06-15 오후 6.56.06.png

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package hello.core.beandefinition;

import hello.core.AppConfig;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class BeanDefinitionTest {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);

@Test
@DisplayName("빈 설정 메타정보 확인")
void findApplicationBean(){
String[] beanDefinitionNames = ac.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
BeanDefinition beanDefinition = ac.getBeanDefinition(beanDefinitionName);

if (beanDefinition.getRole()== BeanDefinition.ROLE_APPLICATION){
System.out.println("beanDefinitionName = " + beanDefinitionName
+"beanDefinition="+beanDefinition);
}
}
}
}
CATALOG
  1. 1. 스프링 핵심 원리 (기본편) CH04
  2. 2. 스프링 컨테이너와 스프링 빈
    1. 2.1. 과정
    2. 2.2. 컨테이너에 등록된 모든 빈 조회
    3. 2.3. 스프링 빈 조회 - 기본
    4. 2.4. 스프링빈 조회 - 동일한 타입이 둘 이상
    5. 2.5. 스프링 빈 조회 - 상속관계(!!!)
  3. 3. BeanFactory와 ApplicationContext
    1. 3.1. BeanFactory
    2. 3.2. ApplicationContext
    3. 3.3. 다양한 설정 형식 지원 - 자바 코드, XML
      1. 3.3.1. 애노테이션 기반 자바 코드 설정 사용
      2. 3.3.2. XML 설정 사용
  4. 4. 스프링 빈 설정 메타 정보 - BeanDefinition
    1. 4.1. BeanDefinition정보