스프링으로 전환
AppConfig 스프링 기반으로 변경
package hello.core;
import hello.core.discount.DiscountPolicy;
import hello.core.discount.FixDiscountPolicy;
import hello.core.member.MemberRepository;
import hello.core.member.MemberService;
import hello.core.member.MemberServiceImpl;
import hello.core.member.MemoryMemberRepository;
import hello.core.order.OrderService;
import hello.core.order.OrderServiceImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
//의존관계 주입!
@Configuration // 설정을 구성한다고 알려줌
public class AppConfig {
@Bean//스프링 빈으로 등록
public MemberService memberService(){
return new MemberServiceImpl(memberRepository());
}
@Bean
public MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
@Bean
//주문 서비스는 저장소와 할인정책이 필요하다.
public OrderService orderService(){
return new OrderServiceImpl(memberRepository(), discountPolicy());
}
@Bean
public DiscountPolicy discountPolicy() {
return new FixDiscountPolicy();
}
}
MemberApp에 스프링 컨테이너(객체 생성, 관리, 의존관계 주입) 적용
package hello.core;
import hello.core.AppConfig;
import hello.core.member.Grade;
import hello.core.member.Member;
import hello.core.member.MemberService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
//테스트해보는 용도
public class MemberApp {
public static void main(String[] args) {
//AppConfig를 통하여 주입
// AppConfig appConfig = new AppConfig();
// MemberService memberService = appConfig.memberService();//주입!
//멤버서비스 구현체는 생성자로 적절한 구현체들을 받게된다.
//즉, 멤버 서비스 구현체는 어떤 구현체를 가져올지 고민안해도 됨!
//밑에선 memberServiceImpl를 가져오고 MemberServiceImpl 내부적으로 어떤걸 쓸지 골라야됬음!
//MemberService memberService = new MemberServiceImpl();
/**
* 스프링 컨테이너 적용
*/
//ApplicationContext를 스프링 컨테이너라 보면된다. 스프링은 모든것이 이 A.C로 부터 시작된다.
//AppConfig로부터 구성 정보를 가져온다.
// => 스프링이 빈들을 설정해서 스프링 컨테이너에 객체를 생성해서 갖고 있는다.
//어노테이션 기반으로 config를 하므로 AnnotationConfigApplicationContext 사용
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
//getBean(메서드명, 타입)으로 가져온다.
//MemberServiceImpl.class와 같이 구체 타입을 지정해도 되나, 구현에 의존하게 되는 꼴이 되버린다.
MemberService memberService = applicationContext.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("findMember = " + findMember.getName());
}
}
OrderApp에도 스프링 컨테이너를 적용시킨다.
package hello.core;
import hello.core.AppConfig;
import hello.core.member.Grade;
import hello.core.member.Member;
import hello.core.member.MemberService;
import hello.core.order.Order;
import hello.core.order.OrderService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
//주문 하기
public class OrderApp {
public static void main(String[] args) {
// AppConfig appConfig = new AppConfig();
// OrderService orderService = appConfig.orderService(); //주입!!\
// MemberService memberService = appConfig.memberService();
//스프링 컨테이너 생성
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
//빈 가져옴
MemberService memberService = applicationContext.getBean("memberService", MemberService.class);
OrderService orderService = applicationContext.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);
}
}
실행 후 로그를 보면
17:06:34.789 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'memberService'
17:06:34.808 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'memberRepository'
17:06:34.810 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'orderService'
17:06:34.811 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'discountPolicy'
"Creating shared instance of singleton bean" 스프링에 등록된 것을 알 수 있다.
스프링에 등록시 (key(메서드명), value(타입))으로 저장이된다.
이걸 정리하면
- ApplicationContext = 스프링 컨테이너 (Context = 문맥)
- 기존엔 개발자가 AppConfig를 사용해서 직접 객체 생성하고 DI
- @Configuration 어노테이션이 붙은 AppConfig를 설정정보로 사용한다.
- @Bean 이 붙은 메서드를 모두 호출하고 반환된 객체를 스프링 컨테이너에 등록한다. (등록된 객체 = 스프링 빈)
- 관례로 @Bean이 붙은 메서드 명을 스프링 빈 이름으로 사용한다. (@Bean (name = "AA")와 같이 변경도 가능한데 왠만하면 관례따르는게 보기 편하고 관리 편하다.)
- 기존엔 직접 필요한 객체를 AppConfig를 이용해서 조회했다.
- => 스프링 컨테이너를 통해서 applicaionContext.getBean()메서드를 사용해서 스프링빈, 즉 객체를 찾게되었따.
즉, 직접 자바코드로 모든 것을 하다가
- 스프링 컨테이너에 객체를 스프링 빈으로 등록
- 스프링 컨테이너에서 스프링 빈을 찾아서 사용
과 같이 바뀌게 되었다.
이렇게 바꾸었을 때 장점은 무엇일까?..... => 뒤에서
스프링 컨테이너와 스프링 빈
스프링 컨테이너 생성과정을 다시 보면
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
ApplicationContext 이 스프링 컨테이너이고, 인터페이스이다.
따라서 다형성이 적용된다.
"new AnnotationConfigApplicationContext(AppConfig.class);"클래스는 ApplicationContext 인터페이스의 구현체이다.
스프링 컨테이너는 어노테이션 기반의 자바 설정 클래스로 만들수도 있고, XML을 기반으로 만들 수도 있다.
정리하면
스프링 컨테이너 생성
스프링 컨테이너 생성(AppConfig.class) - 스프링 컨테이너 생성시 구성정보 지정해주어야함.
스프링 빈 등록
파라미터로 넘어온 설정정보(AppConfig.class)를 사용해서 @Bean들을 다 등록함.
빈 이름(Key) : 빈 객체(value) 로 저장된다. (빈이름은 메서드명이 사용된다.)
ex) memberService : MemberServiceImpl@0x1
스프링 빈 의존관계 설정 - 준비
빈들이 스프링 컨테이너에 등록이 되었지만 서로 연결되진 않았다. = 의존관계 주입이 안되었음
스프링빈 의존관계 설정 - 완료
각 객체들이 생성되면서 의존관계가 주입되어진다.
동적 의존 관계 연결시켜줌.
(설정 정보를 참고해서 의존관계가 주입되어짐.)
** 스프링은 빈을 생성하고, 의존관계를 주입하는 단계가 나누어져있다.
위와같이 자바코드로 스프링 빈 등록시, 생성자를 호출하면서 의존관계 주입도 한번에 처리된다.
@Bean//스프링 빈으로 등록
public MemberService memberService(){
return new MemberServiceImpl(memberRepository());
} // -> 호출되면서 memberservice와 리포지토리가 자동으로 연결되게 된다
@Bean
public MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
@Bean
//주문 서비스는 저장소와 할인정책이 필요하다.
public OrderService orderService(){
return new OrderServiceImpl(memberRepository(), discountPolicy());
} // 호출되면서 리포지토리, 할인정책이 불려오면서 같이 연결되게 된다.
@Bean
public DiscountPolicy discountPolicy() {
return new FixDiscountPolicy();
}
이제 컨테이너에 잘 등록되었는지 확인 해보자.
'공부 > Spring_2-스프링 핵심 원리 이해1' 카테고리의 다른 글
스프링 핵심 원리 이해1_6 - 싱글톤 컨테이너 (0) | 2022.04.07 |
---|---|
스프링 핵심원리 이해1_5 - 다양한 설정 형식 지원(자바코드, xml) (0) | 2022.04.07 |
스프링 핵심원리 이해1_4 - 스프링컨테이너 빈 조회 (0) | 2022.04.07 |
스프링 핵심원리 이해1_2 - 새로운 할인정책 개발, AppConfig (0) | 2022.04.07 |
스프링 핵심 원리 이해1_1 - 회원, 주문, 할인정책 (0) | 2022.04.07 |