이 글은 김영한 님의 스프링 핵심 원리 - 기본편(https://www.inflearn.com/스프링-핵심-원리-기본편/dashboard)을 수강하며 학습한 내용을 정리한 글입니다. 모든 출처는 해당 강의에 있습니다.
좋은 객체 지향 설계의 5가지 원칙인 SOLID 중 SRP, DIP, OCP 적용
한 클래스는 하나의 책임만 가져야 한다.
AppConfig
프로그래머는 "추상화에 의존해야지, 구체화에 의존하면 안된다." 의존성 주입은 이 원칙을 따르는 방법 중 하나다.
추상화 인터페이스(DiscountPolicy
) 뿐만 아니라 구체화 구현 클래스(FixDiscountPolicy
)에도 함께 의존
→ 새로운 할인 정책 개발 후 적용시 클라이언트 코드(OrderServiceImpl
)도 변경 필요
소프트웨어 요소는 확장에는 열려있으나 변경에는 닫혀 있어야 한다.
FixDiscountPolicy
에서 RateDiscountPolicy
로 변경하여 클라이언트 코드에 주입프로그램의 제어 흐름을 직접 제어하는 것이 아니라 외부에서 관리하는 것
AppConfig
등장 이후JUnit
: 작성한 테스트 코드를 직접 실행해 줌Jackson
: JSON을 오브젝트로 변환하는 기능 제공애플리케이션 실행 시점(런타임)에 외부에서 실제 구현 객체를 생성 후 클라이언트에 전달하여 클라이언트와 서버의 실제 의존관계가 연결 되는 것
구현 객체는 인터페이스에 의존하며, 실제 사용될 구현 객체는 알지 못함
import
코드만 보고 의존관계 쉽게 판단 가능OrderServiceImpl
이 MemberRepository
와 DiscountPolicy
에 의존한다는 것을 알 수 있음OrderServiceImpl
에 주입 될지 알 수 없음애플리케이션 실행 시점에 실제 생성된 객체 인스턴스의 참조가 연결된 의존 관계
객체를 생성하고 관리하면서 의존관계를 연결해 주는 것
[src/main/java/hello/core/AppConfig.java]
package hello.core;
import hello.core.discount.DiscountPolicy;
import hello.core.discount.RateDiscountPolicy;
import hello.core.member.MemberService;
import hello.core.member.MemberServiceImpl;
import hello.core.member.MemberRepository;
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 OrderService orderService(){
return new OrderServiceImpl(
memberRepository(),
discountPolicy());
}
@Bean
public MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
@Bean
public DiscountPolicy discountPolicy() {
return new RateDiscountPolicy();
}
}
@Configuration
@Bean
스프링 컨테이너에 스프링 빈으로 등록
객체 조회시 메소드 명과 return 값을 이용
📖 예시)
key (=메소드명) | value (=return 값) |
---|---|
memberService() | new MemberServiceImpl() |
orderService() | new new OrderServiceImpl() |
memberRepository | new MemoryMemberRepository() |
DiscountPolicy() | new RateDiscountPolicy() |
이름 따로 지정 가능 ex)@Bean(name="mmm")
[src/main/java/hello/core/MemberApp.java]
package hello.core;
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 = new AppConfig();
//MemberService memberService = appConfig.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 findMember = memberService.findMember(1L);
System.out.println("new member = " + member.getName());
System.out.println("find Member = " + findMember.getName());
}
}
AnnotationConfigApplicationContext()
@Bean
어노테이션으로 등록한 빈(객체)을 알아서 관리getBean()
[src/main/java/hello/core/OrderApp.java]
package hello.core;
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();
//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", 10000);
System.out.println("order = " + order);
}
}
ApplicationContext
AppConfig
를 사용하여 직접 객체를 관리@Configuration
AppConfig
를 설정(구성) 정보로 사용@Bean
memberService
, orderService
application.getBean()
메서드를 통해 조회 가능스프링 컨테이너의 장점 = 편리함