스프링 컨테이너 생성
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
스프링 컨테이너 생성 과정
키는 빈의 이름이 되고, 값은 빈의 객체가 됨
@Bean이 붙은걸 모두 호출하여 메서드 이름은 빈 이름으로, 리턴 값은 빈 객체로 등록
빈 이름은 보통 메서드 이름을 사용하지만 @Bean(name="memberService2") 이런 형식으로 직접 부여할 수도 있다.
빈의 이름은 항상 다른 이름을 부여해야 한다.
위처럼 객체를 생성한 다음에는 여기에 의존관계를 넣어줘야 한다.
동적인 객체 인스턴스 의존관계를 스프링이 다 연결을 해줌
설정 정보를 참조한다는 의미는 코드에서 설정한 파라미터 값들을 본다고 생각하면 될 듯
스프링은 빈을 생성하고, 의존관계를 주입하는 단계가 나누어져 있다. 그런데 이렇게 자바 코드로 스프링 빈을 등록하면 생성자를 호출하면서 의존관계 주입도 한번에 처리된다.
여기서 3, 4로 나눈 이유는 이해를 돕기 위해 개념적으로 나누어 설명한 것 뿐
컨테이너에 등록된 모든 빈 조회
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);
//key //value
System.out.println("name = " + beanDefinitionName + "object = " + bean );
}
}
@Test
@DisplayName("애플리케이션 빈 출력하기")
void findApplicaitonBean(){
String[] beanDefinitionNames = ac.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
//getBeanDefinition : 빈 하나하나에 대한 정보
BeanDefinition beanDefinition = ac.getBeanDefinition(beanDefinitionName);
// ROLE_APPLICATION : 일반적으로 사용자가 정의한 빈
// ROLE_INFRASTRUCTURE : 스프링이 내부에서 사용하는 빈
if (beanDefinition.getRole() == BeanDefinition.ROLE_APPLICATION){
Object bean = ac.getBean(beanDefinitionName);
//key //value
System.out.println("name = " + beanDefinitionName + "object = " + bean );
}
}
}
}
모든 빈을 출력하면
이렇게 내가 등록한 빈 외에도 스프링 내부의 자체적인 빈도 모두 출력된다. 하지만 내가 등록한 애플리케이션 빈만 출력하기 위해서 위와 같이 로직을 짜고 실행하면
이렇게 내가 등록한 빈만 출력된다.
스프링 빈 조회 - 기본
보통 위와 같이 모두 출력하는 경우는 거의 없기 때문에 여기서 부터 써먹을 듯?
public class ApplicationContextBascicFindTest {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
@Test
@DisplayName("빈 이름으로 조회")
void findBeandByName(){
MemberService memberService = ac.getBean("memberService", MemberService.class);
//memberService가 MemberServiceImpl의 인스턴스면 성공
Assertions.assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
}
@Test
@DisplayName("이름 없이 타입으로만 조회")
void findBeandByType(){
//빈의 이름을 빼고 타입으로만 조회하도록 하였음
MemberService memberService = ac.getBean(MemberService.class);
Assertions.assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
}
@Test
@DisplayName("구체 타입으로 조회")//이렇게 인터페이스가 아니라 구현체로 조회하는 것은 좋진않음(유연성 떨어짐)
void findBeandByName2(){
MemberService memberService = ac.getBean("memberService", MemberServiceImpl.class);
//memberService가 MemberServiceImpl의 인스턴스면 성공
Assertions.assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
}
@Test
@DisplayName("빈 이름으로 조회x")
void findByNameX(){
//ac.getBean("XXXX", MemberService.class);
//오른쪽의 로직을 실행했을 때, 왼쪽에 있는 예외가 터져야 성공이라는 로직
assertThrows(NoSuchBeanDefinitionException.class,
()-> ac.getBean("XXXX", MemberService.class));
}
}
스프링 빈 조회 - 동일한 타입이 둘 이상
public class ApplicationContextSameBeanFindTest {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(SameBeanConfig.class);
@Test
@DisplayName("타입으로 조회시 같은 타입이 둘 이상 있으면, 중복 오류가 발생한다.")
void findBeanByTypeDuplicate(){
//빈을 불러올 때 타입만 지정했기 때문에 아래에 있는 동일한 타입의 2개의 빈을 불러올 때 예외가 발생
Assertions.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));
}
System.out.println("beansOfType = " + beansOfType);
assertThat(beansOfType.size()).isEqualTo(2);
}
//static을 사용해서 이 테스트에서만 사용할 config를 만든 것
@Configuration
static class SameBeanConfig{
@Bean
public MemoryMemberRepository memberRepository1(){
return new MemoryMemberRepository();
}
@Bean
public MemoryMemberRepository memberRepository2(){
return new MemoryMemberRepository();
}
}
}
스프링 빈 조회 - 상속관계
여기가 좀 중요함
public class ApplicationContextExtendsFindTest {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(TestConfig.class);
@Test
@DisplayName("부모 타입으로 조회시, 자식이 둘 이상 있으면, 중복 오류가 발생한다")
void findByBeanParentTypeDuplicate(){
assertThrows(NoUniqueBeanDefinitionException.class,
() -> ac.getBean(DiscountPolicy.class));
}
@Test
@DisplayName("부모 타입으로 조회시, 자식이 둘 이상 있으면, 빈 이름을 지정하면 된다")
void findByBeanParentTypeBeanName(){
DiscountPolicy rateDiscountPolicy = ac.getBean("rateDiscountPolicy", DiscountPolicy.class);
assertThat(rateDiscountPolicy).isInstanceOf(RateDiscountPolicy.class);
}
@Test
@DisplayName("특정 하위 타입으로 조회 - 좋은 방법은 아님")
void findBeanBySubType(){
//RateDiscountPolicy라는 구체적인 타입으로 지정
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{
//DiscountPolicy 타입으로 조회하면 자식인 (정률, 정액) 2개의 빈이 출력될 것
@Bean
public DiscountPolicy rateDiscountPolicy(){
return new RateDiscountPolicy();
}
@Bean
public DiscountPolicy fixDiscountPolicy(){
return new FixDiscountPolicy();
}
}
}
여기까지가 빈을 조회하는 모든 방법들
사실 실무에서 크게 쓸 일은 없지만 자동 의존관계 주입에서 문제 없이 사용할 수 있기 때문에 필요
BeanFactory와 ApplicationContext
- 위의 기능들이 애플리케이션에 필요한 공통 기능들이며, BeanFacotory의 기능에 이를 더함
다양한 설정 형식 지원 - 자바 코드, XML
xml 기반의 스프링 빈 설정 정보
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="memberService" class="hello.core.member.MemberServiceImpl">
<constructor-arg name="memberRepository" ref="memberRepository"/>
</bean>
<bean id="memberRepository" class="hello.core.member.MemoryMemberRepository"/>
<bean id="orderService" class="hello.core.order.OrderServiceImpl">
<constructor-arg name="memberRepository" ref="memberRepository"/>
<constructor-arg name="discountPolicy" ref="discountPolicy"/>
</bean>
<bean id="discountPolicy" class="hello.core.discount.RateDiscountPolicy"/>
</beans>
XmlAppConfig 사용 자바 코드
public class XmlAppContext {
@Test
void xmlAppContext(){
ApplicationContext ac = new GenericXmlApplicationContext("appConfig.xml");
MemberService memberService = ac.getBean("memberService", MemberService.class);
assertThat(memberService).isInstanceOf(MemberService.class);
}
}
테스트 파일을 이렇게 만들고 실행하면 어노테이션 사용한거랑 똑같은 결과가 나오게 됨
스프링 빈 설정 메타 정보 - BeanDefinition
먼저 기억하고 넘어갈 것은 스프링이 다양한 형태의 설정 정보를 BeanDefinition으로 추상화해서 사용하는 것 정도만 이해하면 된다는 것이다.
BeanDefinition 정보
public class BeanDefinitionTest {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
@Test
@DisplayName("빈 설정 메타정보 확인")
void finApplicationBean(){
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);
}
}
}
}
위와 같이 테스트 파일을 만들어서 실행해보면
이런식으로 확인이 가능하다
'스프링 > 기본편' 카테고리의 다른 글
컴포넌트 스캔 (0) | 2022.05.22 |
---|---|
싱글톤 컨테이너 (0) | 2022.05.22 |
스프링 핵심 원리 이해2 - 객체 지향 원리 적용 (0) | 2022.05.21 |
스프링 핵심 원리 이해1 - 예제 만들기 (0) | 2022.05.21 |
객체 지향 설계와 스프링 (0) | 2022.05.21 |