지금까지 순수한 자바 코드만으로 DI를 적용했다. 이제 스프링을 사용해 순수 자바코드를 스프링 기반으로 변경한다.
package hello.core;
import hello.core.discount.DiscountPolicy;
import hello.core.discount.RateDiscountPolicy;
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());
}
/* 메소드 실행 시 MemoryMemberRepository 생성자를 매개변수로 갖는
MemberServiceImpl 생성자를 MemberService타입으로 반환
인터페이스를 먼저 불러오고
*/
@Bean
public static MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
/* 메소드 실행 시 MemoryMemberRepository 생성자를 만들어 MemberRepository타입으로 반환
인터페이스의 생성자로 구현체를 리턴한다
*/
@Bean
public OrderService orderService(){
return new OrderServiceImpl(memberRepository(), discountPolicy());
} // 기존 인터페이스를 먼저 참조시킨다.
@Bean
public DiscountPolicy discountPolicy() {
return new RateDiscountPolicy();
}
/*
public MemberService memberService() {
return new MemberServiceImpl(new MemoryMemberRepository());
}
MemberService에서 바로 구현체를 연결하면, 중간 과정을 알 수 없다.
위 코드에서는 MemoryMemberRepository가 무슨 인터페이스를 구현했는지의 정보가 없다.
public OrderService orderService() {
return new OrderServiceImpl(
new MemoryMemberRepository(),
new FixDiscountPolicy());
리팩토링을 안하면 MemoryMemberRepository와 FixDiscountPolicy가 어떤 인터페이스를
구현했는지 모른다.
또한 new MemoryMemberRepository() 부분이 중복되어 구현체를 바꿔야 할 때에도 중복 작업이 예상된다.
*/
}
@Configuration을 클래스 앞에 붙여 스프링 설정을 해당 클래스에 구성한다. 각 메소드 앞에 @Bean을 붙여주면 스프링 컨테이너에 스프링 빈으로 등록된다.
package hello.core;
import hello.core.member.Grade;
import hello.core.member.Member;
import hello.core.member.MemberService;
import hello.core.member.MemberServiceImpl;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MemberApp {
public static void main(String[] args) {
// AppConfig appConfig = new AppConfig();
// MemberService memberService = appConfig.memberService();
//AppConfig에 정의된 내용 대로 생성자를 주입한다.
//여기서는 memory방식의 회원 저장소가 주입된 memberService 객체를 저장한다.
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
MemberService memberService = applicationContext.getBean("memberService", MemberService.class);
Member member = new Member(1L, "memberA", Grade.VIP);
memberService.join(member);
//Member객체-MemberService인터페이스-MemberServiceImpl구현객체의 join메소드-MemberRepository인터페이스-MemoryMemberRepository의 save메소드 순서로 넘어가며 실행한다.
Member findMember = memberService.findMember(1L);
System.out.println("new member = " + member.getName());
System.out.println("find Member = " + findMember.getName());
}
}
package hello.core;
import hello.core.member.Grade;
import hello.core.member.Member;
import hello.core.member.MemberService;
import hello.core.member.MemberServiceImpl;
import hello.core.order.Order;
import hello.core.order.OrderService;
import hello.core.order.OrderServiceImpl;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class OrderApp {
public static void main(String[] args) {
// AppConfig appConfig = new AppConfig();
// MemberService memberService = appConfig.memberService();
// OrderService orderService = appConfig.orderService();
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", 20000);
System.out.println("order = " + order);
}
}
MemberApp과 OrderApp의 메인 메소드를 실행하면 기존과 동일한 결과가 출력된다.
<aside> 💡 코드가 오히려 더 복잡해진 것 같은데, 스프링 컨테이너를 사용하면 어떤 장점이 있지? → 졸라 많다.
</aside>