일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 명령어
- Linux
- 고잉버스
- 리눅스
- java 백준 1차원 배열
- Kotlin
- 리눅스마스터1급
- 연습문제
- 프로그래머스
- Java
- 카카오
- 코딩테스트
- 자바스크립트 코딩의 기술
- 문자열
- 리눅스마스터 1급 정리
- 반복문
- 스프링 빈
- toCharArray
- 자바
- JavaScript
- 백준 java
- 코테
- GoingBus
- 리눅스마스터 3과목
- map
- 스프링 컨테이너
- 개발자 회고록
- 월간코드챌린지
- 백준 javascript
- Memoir
- Today
- Total
hoon's bLog
Spring gradle project 스프링 핵심 원리 기본편 | Spring Container Bean 스프링 컨테이너와 스프링 빈 조회 본문
Spring gradle project 스프링 핵심 원리 기본편 | Spring Container Bean 스프링 컨테이너와 스프링 빈 조회
개발한기발자 2024. 4. 5. 09:11
본 포스팅은 인프런에 있는 인터넷 강좌인,
김영한 강사님의 스프링 핵심 원리 기본편을 공부하며,
개인적으로 공부하고, 정리하는 용도로 포스팅을 해보겠다.
[이전 포스팅 목록]
Spring gradle project 환경설정 및 회원 가입 서비스 예제 만들기
Spring gradle project 주문/할인 도메인 설계
Spring gradle project 객체 지향 원리 적용
Spring gradle project AppConfig 리팩토링 OCP 위반 해결 및 중복 제거
Spring gradle project 좋은 객체 지향 설계 5가지 원칙 적용 및 스프링 전환
4. 스프링 컨테이너와 스프링 빈
저번 포스팅에서 스프링 컨테이너를 알아보았는데, 이번엔 생성되는 과정을 알아보자.
ApplicationContext applicationContext =
new AnnotationConfigApplicationContext(AppConfig.class);
- ApplicationContext를 스프링 컨테이너라 한다.
- ApplicationContext는 인터페이스이다.
- 스프링 컨테이너는 XML을 기반으로 만들 수 있고, 어노테이션 기반의 자바 설정 클래스로 만들 수 있다.
- 직전에 AppConfig를 사용했던 방식이 어노테이션 기반의 Java 설정 클래스로, 스프링 컨테이너를 만든 것이다.
- 이 클래스는 ApplicationContext 인터페이스의 구현체이다.
더 정확히는 스프링 컨테이너를 부를 때, BeanFactory, ApplicationContext로 구분해서 이야기한다.
BeanFactory를 직접 사용하는 경우는 거의 없으므로, 일반적으로 ApplicationContext를 스프링 컨테이너라 한다.
스프링 컨테이너의 생성 과정
- 스프링 컨테이너를 생성할 때는 구성 정보를 지정해주어야 한다.(여기서는 AppConfig.class를 구성 정보로 지정)
스프링 빈 등록
- 스프링 컨테이너는 파라미터로 넘어온 설정 클래스 정보를 사용해서 스프링 빈을 등록한다.
- 빈 이름은 메서드 이름을 사용하고, 직접 부여할 수 도 있다.(주로 camelCase로!!)
ex). @Bean(name="memberService2") - 빈 이름은 항상 다른 이름을 부여해야 한다. 같은 이름을 부여 시, 다른 빈이 제외되거나, 기존 빈을 덮어 버리거나 설정에 따라 오류가 발생한다.
스프링 빈 의존관계 설정
- 스프링 컨테이너는 설정 정보를 참고해서 의존관계를 주입(DI)한다.
- 단순히 자바 코드를 호출하는 것 같지만, 차이가 있다. 이는 뒤에 싱글톤 컨테이너에서 설명한다.
- 스프링은 빈을 생성하고, 의존관계를 주입하는 단계가 나누어져 있다.
- 그런데 이렇게 자바 코드로 스프링 빈을 등록하면 생성자를 호출하면서 의존관계 주입도 한 번에 처리된다.
컨테이너에 등록된 모든 빈 조회 테스트 코드
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(){ //Junit 5부터는 public 선언 안해도 됨!
String[] beanDefinitionNames = ac.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
Object bean = ac.getBean(beanDefinitionName);
System.out.println("name = " + beanDefinitionName + "object = " + bean);
}
}
@Test
@DisplayName("애플리케이션 빈 출력하기")
void findApplicationBean(){
String[] beanDefinitionNames = ac.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
BeanDefinition beanDefinition = ac.getBeanDefinition(beanDefinitionName);
// ROLE_APPLICATION: 직접 등록한 애플리케이션 빈
// ROLE_INFRASTRUCTURE: 스프링이 내부에서 사용하는 빈
if(beanDefinition.getRole() == BeanDefinition.ROLE_APPLICATION){
Object bean = ac.getBean(beanDefinitionName);
System.out.println("name = " + beanDefinitionName + " / object = " + bean);
}
}
}
}
- AppConfig : 스프링 설정 클래스로, 스프링 빈에 대한 정보를 포함하고 있음
- AnnotationConfigApplicationContext를 사용하여 AppConfig 클래스를 기반으로 한 Spring Application Context를 생성한다.
- findAllBean() : getBeanDefinitionNames() 메서드를 사용하여 컨테이너에 등록된 모든 빈의 이름을 가져오고, 각 이름에 대해 getBean() 메소드를 호출하여 빈 객체를 검색한 다음 객체를 출력한다
- findApplicationBean(): 동일하게 getBeanDefinitionNames()로 모든 빈 이름을 가져오고, 각 이름에 대해 getBeanDefinition()으로 빈 정의를 검색한다.
getRole() 메서드로 빈의 역할을 확인하고, BeanDefinition.ROLE_APPLICATION에 해당하는 빈만 출력! - 이렇게 하면 애플리케이션의 주요 로직을 구성하는 Bean만 필터링할 수 있다.
주의할 점
로그가 출력되지 않는 이슈 발생!!!!
Springboot 버전 3.1 이상인 경우 아래와 같이 출력될 수 있다.
new member = memberA
find Member = memberA
- Spring Container와 Spring Bean에 대한 정보 없이 그냥 Bean이름과 객체만 나오게 된다.
- 때문에 아래 경로에 logback.xml 파일을 추가해 준다!
src/main/resources/logback.xml
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>
%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} -%kvp%msg%n
</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="STDOUT" />
</root>
</configuration>
- logback.xml 파일을 추가하고 다시 빌드하여 실행하면 그림과 같이 로그가 정상적으로 출력될 것이다!
스프링 빈 조회 - 기본
- 스프링 컨테이너에서 스프링 빈을 찾는 가장 기본적인 조회 방법은
ac.getBean(빈이름, 타입)
또는ac.getBean(타입)
과 같이 getBean 메서드를 사용하는 것이다! - 조회 대상 스프링 빈이 없으면 예외 발생
package hello.core.beanfind;
import hello.core.AppConfig;
import hello.core.member.MemberService;
import hello.core.member.MemberServiceImpl;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
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(){
// ac.getBean("xxxxx", MemberService.class);
assertThrows(NoSuchBeanDefinitionException.class, () -> ac.getBean("xxxxx", MemberService.class));
}
}
- findBeanByName() : memberService라는 이름으로 등록된 빈을 찾아, 해당 빈이 MemberServiceImpl의 인스턴스인지 검증
- findBeanByType() : 빈의 이름을 지정하지 않고, MemberService 타입으로 빈을 조회하는 방법을 테스트한다.
그리고 조회된 빈이 MemberServiceImpl 클래스의 인스턴스인지 확인!! - findBeanByName2(): memberService라는 이름과 구체적인 클래스 타입(MemberServiceImpl.class)을 사용하여 빈을 조회하는 방법으로, 특정 구현 클래스로 직접 조회하는 것이며, 조회된 객체가 MemberServiceImpl 클래스의 인스턴스인지 확인한다.
- findBeanByNameX(): 존재하지 않는 빈 이름으로 조회를 시도했을 때 예외가 발생하는지 검증한다.
(assertThrows 메서드를 사용하여 NoSuchBeanDefinitionException 예외가 발생하는 것을 확인한다.)
이렇게 기초적인 Spring Bean 조회를 알아봤는데,
다음 포스팅에서는 이제 이 Bean 조회를 통해 중복 및 상속 관계에 대해 알아보겠다!
진짜 포스팅하면서 느끼지만,
뭔가 책으로만, 인강으로만 들었을 때 잘 이해되지 않았던 부분이,
다이어그램과 실제 코딩 결과를 통해 되새기니,
훨씬 더 개념이 잘 그려진다.
아직 Spring의 개념이 완벽히 잡힐 때까지 시간이 좀 걸리겠지만,
계속해서 꾸준하게 학습하고, 코딩해서 내 것으로 만들어야겠다.
끝.