본 게시글은 김영한님의 스프링 핵심 원리 기본편을 정리한 글입니다.
이전 시간 여러 개념에 대해서 다시 정립하여보고 복습하는 시간을 약간 가졌었는데 실제로는 이때까지 우리가 작성한 코드들은 스프링을 사용하였다기보다는 사용하기 위하여서 개념들을 알아볼 수 있도록 순수 자바 코드로 작성을 한 시간들이었다. 이제부터는 스프링을 진짜 사용해보면서 어떠한 점들이 좋아질 수 있는지 알아보자.
public class AppConfig{
public MemberService memberService(){
return new MemberServiceImpl(memberRepository());
}
public OrderService orderService(){
return new OrderServiceImpl(memberRepository(), discountPolicy());
}
public MemberRepository memberRepository(){
return new MemoryMemberRepository();
}
public DiscountPolicy discountPolicy(){
return new FixDiscountPolicy();
}
}
@Configuration
public class AppConfig{
@Bean
public MemberService memberService(){
return new MemberServiceImpl(memberRepository());
}
@Bean
public OrderService orderService(){
return new OrderServiceImpl(memberRepository(), discountPolicy());
}
@Bean
public MemberRepository memberRepository(){
return new MemoryMemberRepository();
}
@Bean
public DiscountPolicy discountPolicy(){
// return new FixDiscountPolicy();
return new RateDiscountPolicy();
}
}
위에 어노테이션들이 추가된 걸 확인할 수 있다. @Configuration,
@Bean
이라는 것인데
@Configuration: 기획자의 의미인 설정을 구성한다는 뜻을 부여한다는 의미이다.
@Bean: 해당 어노테이션들이 붙어있는 것들을 스프링 컨테이너에 스프링 빈으로 등록을 하겠다는 의미이다.
public class OrderApp {
public static void main(String[] args) {
AppConfig appConfig = new AppConfig();
OrderService orderService = appConfig.orderService();
MemberService memberService = appConfig.memberService();
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);
}
}
public class OrderApp {
public static void main(String[] args) {
//AppConfig appConfig = new AppConfig();
//OrderService orderService1 = appConfig.orderService();
//MemberService memberService1 = appConfig.memberService();
ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
OrderService orderService = ac.getBean("orderService", OrderService.class);
MemberService memberService = ac.getBean("memberService", MemberService.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);
}
}
이전에는 AppConfig를 활용하여 객체를 직접 생성하여서 DI도 해주고 하는 작업들을 했었지만 이제는 보게 되면 ApplicationContext라는 것을 활용하여서 막 이것저것 작업을 해주고 있다. 이제는 즉 스프링 컨테이너를 사용을 할텐데 이전에 @Configuration을 붙였던 것을 기억을 하나? AppConfig에 적었었는데 그때 설정을 구성한다는 의미를 추가한다고 했었다. 그리고 @Bean을 통해서 빈을 등록한다고 하였고, 그러면 이제 정리를 해보면 이렇다.
이제부터는 스프링 컨테이너를 사용을 하는데
ApplicationContext: 스프링 컨테이너
ac.getBean()을 통하여서 이제 @Bean으로 등록되어있던 bean들을 찾아올 수 있게 된다. 이 안에 들어가는 항목은 다음과 같이 ac.getBean("빈으로 등록된 메소드 이름", 반환 타입)을 사용하여 불러온다.
이렇게 하면 장점이 뭐가 있을까? 코드상으로만 보면 길어진것만 같지 딱히 다를게 없는거 같다.
그럼 이것의 장점을 알기 위해서는 각 생성과정을 보면 알 수 있는데 하나씩 알아보자.
ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
위에 말했던 대로 ApplicationContext는 스프링 컨테이너이고 그리고 인터페이스다. 그러면? 옆에 있는 AnnotationConfigApplicationContext는 위 인터페이스의 구현체이다.
스프링 컨테이너를 부를 때 BeanFactory, ApplicationContext로 구분해서 이야기를 하는데 BeanFactory를 직접 사용하는 경우가 거의 없어 ApplicationContext를 스프링 컨테이너라고 한다.
그림으로 살펴보면 다음과 같다.
빈 이름을 따로 등록을 또 할 수가 있는데
@Bean(name="이름")
을 작성하면 나중에 getBean으로 호출할때 본인이 설정한 이름으로 부를 수 있다.
이러한 과정들이 있었는데 그림들을 보면 되게 많은 작업들이 자동적으로 수행하면서 스프링이 관리해주고 있다는 것을 알게 되었다. 물론 지금은 이렇게 그냥 몇 개의 빈 가지고 관리를 하여준다고 하면 쉽게 와닿지 않을 수는 있지만 이게 수백개 이상이 되어버린다면 관리하는 입장에서는 직접 관리하면 상당히 부담스럽지 않을 수가 없을 거 같다는 생각이 많이 들게되는 거 같다. 이후에는 이제 이 스프링 컨테이너를 사용하여 어떤것들을 할 수 있나 계속해서 알아보자.