반응형
스프링으로 전환
@Configuration
public class AppConfig {
@Bean
public MemberService memberService() {
return new MemberServiceImpl(getMemberRepository());
}
@Bean
public MemberRepository getMemberRepository() {
return new MemoryMemberRepository();
}
@Bean
public OrderService orderService() {
return new OrderServiceImpl(getMemberRepository(), discountPolicy());
}
@Bean
public DiscountPolicy discountPolicy() {
//return new FixDiscountPolicy();
return new RateDiscountPolicy();
}
}
- 이전에는 개발자가 필요한 객체를 AppConfig 를 사용해서 직접 조회, 스프링은 컨테이너를 통해서 필요한 스프링 빈(객체)을 조회
- 빈으로 등록 : @Configuration + @Bean
- @Bean 이 붙은 메서드의 명을 스프링 빈의 이름으로 사용
public class MemberApp {
public static void main(String[] args) {
AnnotationConfigApplicationContext configApplicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
// 스프링 컨텍스트에서 빈 꺼내 쓰기
MemberService memberService = configApplicationContext.getBean("memberService", MemberService.class);
Member member = new Member(1L, "memberA", Grade.VIP);
memberService.join(member);
Member findMember = memberService.findMember(1L);
System.out.println("new member = " + member.getName());
System.out.println("find Member = " + findMember.getName());
}
}
public class OrderApp {
public static void main(String[] args) {
AnnotationConfigApplicationContext configApplicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
// 스프링 컨텍스트에서 빈 꺼내 쓰기
MemberService memberService = configApplicationContext.getBean("memberService", MemberService.class);
OrderService orderService = configApplicationContext.getBean("orderService", OrderService.class);
long memberId = 1L;
Member member = new Member(memberId, "memberA", Grade.VIP);
memberService.join(member);
Order order = orderService.createOrder(memberId, "itemA", 10000);
System.out.println("order = " + order);
}
}
스프링 컨테이너 & 빈
- ApplicationContext 를 스프링 컨테이너라 한다.
- ApplicationContext 는 인터페이스이다.
- Xml / annotation 으로 컨테이너를 만들 수 있다.
- BeanFactory 를 직접 사용하는 경우는 거의 없으므로 일반적으로 ApplicationContext 를 스프링 컨테이너라 한다.
생성과정
- 스프링 컨테이너 생성
- new AnnotationConfigApplicationContext(AppConfig.class)
- 스프링 빈 등록
- 빈 이름은 메서드 이름을 사용한다.
- 빈 이름을 직접 부여할 수 도 있다. @Bean(name="memberService2")
- 빈 의존관계 설정
- 싱글톤 컨테이너로 의존관계를 주입
자바 코드로 스프링 빈을 등록하면 생성자를 호출하면서 의존관계 주입도 한번에 처리된다.
빈 출력 테스트
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); // 빈 이름으로 조회
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 ROLE_APPLICATION: 직접 등록한 애플리케이션 빈
//Role ROLE_INFRASTRUCTURE: 스프링이 내부에서 사용하는 빈
if (beanDefinition.getRole() == BeanDefinition.ROLE_APPLICATION) {
Object bean = ac.getBean(beanDefinitionName);
System.out.println("name=" + beanDefinitionName + " object=" +
bean);
}
}
}
}
빈 조회
직접 빈을 조회하거나 가져올 일은 대부분 일어나지 않지만,
기본적인 기능과 동작API종류는 알고 넘어가자
이름 & 타입 조회
public class ApplicationContextBasicFindTest {
AnnotationConfigApplicationContext ac = new
AnnotationConfigApplicationContext(AppConfig.class);
@Test
@DisplayName("빈 이름으로 조회")
public void findBeanByName() throws Exception {
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() {
MemberServiceImpl memberService = ac.getBean("memberService",
MemberServiceImpl.class);
assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
}
@Test
@DisplayName("빈 이름으로 조회X")
void findBeanByNameX() {
assertThrows(NoSuchBeanDefinitionException.class, () ->
ac.getBean("xxxxx", MemberService.class));
}
}
동일한 타입
특정 빈만 가져오려면 빈이름까지 지정해줘야 한다.
public class ApplicationContextSameBeanFindTest {
AnnotationConfigApplicationContext ac = new
AnnotationConfigApplicationContext(SameBeanConfig.class);
@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));
}
System.out.println("beansOfType = " + beansOfType);
assertThat(beansOfType.size()).isEqualTo(2);
}
@Configuration
static class SameBeanConfig {
@Bean
public MemberRepository memberRepository1() {
return new MemoryMemberRepository();
}
@Bean
public MemberRepository memberRepository2() {
return new MemoryMemberRepository();
}
}
}
상속 관계 조회
부모 타입으로 조회하면, 자식 타입도 함께 조회한다.
public class ApplicationContextExtendsFindTest {
AnnotationConfigApplicationContext ac = new
AnnotationConfigApplicationContext(TestConfig.class);
@Test
@DisplayName("부모 타입으로 조회시, 자식이 둘 이상 있으면, 중복 오류가 발생한다")
void findBeanByParentTypeDuplicate() {
assertThrows(NoUniqueBeanDefinitionException.class, () ->
ac.getBean(DiscountPolicy.class));
}
@Test
@DisplayName("부모 타입으로 조회시, 자식이 둘 이상 있으면, 빈 이름을 지정하면 된다")
void findBeanByParentTypeBeanName() {
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
- BeanFactoy --구현--> ApplicationContext --구현--> AnnotationConfigApplicationContext
- BeanFactoy
- 최상위 인터페이스
- 빈을 관리/조회 역할
- getBean() 제공
- ApplicationContext (실제 사용되는 클래스)
- BeanFactoy 상속
- 애플리케이션을 개발에 필요한 빈은 관리하고 조회하는 기능
- 수 많은 부가기능이 포함
- 메시지 소스를 활용한 국제화 (MessageSource)
- 환경변수 (EnvironmentCapable)
- profile
- 애플리케이션 이벤트 (ApplicationEventPublisher)
- 이벤트 발행/구독 모델 지원
- 편리한 리소스 조회 (ResourceLoader)
- 파일, 클래스패스, 외부 리소스 편리하게 조회
GenericXmlApplicationContext
- 자바코드가 아닌 .xml으로 빈을 설정할 수 있다.
- 컴파일 없이 빈 설정 정보를 변경할 수 있다.
- SpringBoot를 이용하면 xml사용은 잘 사용하지 않는다.
resources/appConfig.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>
테스트 코드
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)
- 자바코드/xml/custom 빈 설정(Config)들은 모두 BeanDefinition(메타정보)를 만든다. (interface)
- 스프링 컨테이너는 이러한 BeanDefinition을 기반으로 빈을 생성한다.
- BeanDefinition을 직접 생성해서 스프링 컨테이너에 등록할 수 도 있다. 하지만 실무에서 BeanDefinition을 직접 정의하거나 사용할 일은 거의 없다
메타 정보 확인 테스트
public class BeanDefinitionTest {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
// GenericXmlApplicationContext ac = new GenericXmlApplicationContext("appConfig.xml");
@Test
@DisplayName("빈 설정 메타정보 확인")
void findApplicationBean() {
String[] beanDefinitionNames = ac.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
BeanDefinition beanDefinition =
ac.getBeanDefinition(beanDefinitionName);
//beanDefinition의 api를 통해 빈에 대한 정보를 읽어올 수 있다
if (beanDefinition.getRole() == BeanDefinition.ROLE_APPLICATION) {
System.out.println("beanDefinitionName" + beanDefinitionName +
" beanDefinition = " + beanDefinition);
}
}
}
}
728x90
반응형
'스프링' 카테고리의 다른 글
스프링 핵심 원리 - (9) (0) | 2022.08.09 |
---|---|
스프링 핵심 원리 - (8) (0) | 2022.08.05 |
스프링 핵심 원리 - (6) (0) | 2022.08.04 |
스프링 핵심 원리 - (5) (0) | 2022.08.04 |
스프링 핵심 원리 - (4) (0) | 2022.08.03 |
댓글