spring공부함에 있어서 내용은 인프런 김영한님 강의를 듣고 정리한 것입니다 (필기 및 공부정리용)
DI(Dependency Injection)란
객체간의 의존성을 자신이 아닌 외부의 설정을 통해서 주입하는 개념이다.
그렇다면 왜 DI가 필요할까
물론 자신이 직접 객체를 생성하고 설정을 하여도 된다. 하지만 이렇게 할 경우에는
OCP, DIP를 잘 지킨다고 할 수 없다.
public class MemberServiceImpl implements MemberService {
private static MemberRepository memberRepository = new MemoryMemberRepository();
@Override
public void join(Member member) {
memberRepository.save(member);
}
@Override
public Member findMember(Long memberId) {
return memberRepository.findById(memberId);
}
}
위 코드를 보게되면 역할을 MemberService라는 인터페이스로 구현을 MemberServiceImpl로 작성하여 잘 구현을 한 것처럼 보이지만 memberRepository를 메모리저장이 아닌 db에 저장을 하게되면 코드를 변경을 하게된다. (OCP위반)
또한 추상화뿐만 아닌 MemoryMemberRepository라는 구체화에도 의존을 하고 있다. (DIP위반)
위와 같은 것을 DI방식으로 변경을 하게 되면 아래와 같아진다.
public class MemberServiceImpl implements MemberService {
private final MemberRepository memberRepository;
public MemberServiceImpl(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
@Override
public void join(Member member) {
memberRepository.save(member);
}
@Override
public Member findMember(Long memberId) {
return memberRepository.findById(memberId);
}
}
public class AppConfig {
public MemberService memberService() {
return new MemberServiceImpl(new MemoryMemberRepository());
}
}
public class MemberApp {
public static void main(String[] args) {
//MemberService memberService = new MemberServiceImpl();
AppConfig config = new AppConfig();
MemberService memberService = config.memberService();
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());
}
}
위 코드와 같이 AppConfig라는 외부 클래스를 통해 조립을 해주면 된다.
이러면 MemberServiceImpl은 오로지 구현만 생각을 하면되고 어떤 memberRepository가 주입 되는지는 모르게 된다.
이렇게 AppConfig라는 설정 파일을 통해서 외부에서 의존성을 주입하는 것을 DI 의존성 주입이라고 한다.
의존성 주입 방식에는 2가지가 있다
- 생성자를 통한 방식 - 이 방식이 더 끌리는 것 같다.
- setter를 통한 방식
AppConfig와 같이 객체를 생성하고 관리하면서 의존관계를 연결해주는 것을 IoC컨테이너 또는 DI컨테이너라고 한다
스프링 설정은 xml파일과 annotation을 통해서 할 수 있는데 xml은 <bean>
annotation은 @Bean
을 통해서 객체(스프링 빈)를 생성하게 된다.
이러한 객체는 컨테이너에서 관리되게 된다.
요새는 annotation을 통한 방식을 사용을 한다고 하기때문에 annotation을 중점으로 공부를 할 생각이다. 하지만 xml로 설정하는 방식 또한 알고 있어야 한다고 생각하기에 두개를 비교하면서 할 예정이다.
지금 위에서 한 코드는 spring없이 오로지 java코드만을 가지고 의존성 주입하는 방법을 알아봤다.
이제 xml과 annotation을 이용한 의존성 주입 방법을 알아보겠다.
의존성 주입 방법
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="com.jhs.Member.MemberServiceImpl">
<constructor-arg name="memberRepository" ref="memberRepository"/>
</bean>
<bean id="orderService" class="com.jhs.Order.OrderServiceImpl">
<constructor-arg name="memberRepository" ref="memberRepository"/>
<constructor-arg name="discountPolicy" ref="discountPolicy"/>
</bean>
<bean id="memberRepository" class="com.jhs.Member.MemoryMemberRepository"/>
<bean id="discountPolicy" class="com.jhs.Discount.FixDiscountPolicy"/>
</beans>
xml파일을 통해서 콜렉션 설정과 properties값 설정, 네임스페이스, import와 같은 방식이 있지만 앞에서도 말했듯이 xml파일 설정은 잘 사용하지 않는다고 하기 때문에 추가적인 작성은 하지 않을 것이다. 필요할 때 따로 spring 공식 레퍼런스 문서를 확인하면 되겠다.
spring 공식 레퍼런스 문서
annotation
@Configuration //설정정보가 있는 파일이라는 것을 나타냄
public class AppConfig {
@Bean //Spring 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();
}
}
//위에 사용했던 예제에서 주문관련 서비스를 추가하였다.
xml파일은 AppConfig파일을 대신한다고 생각을 하면 된다.
AppConfig에서 의존 정보를 설정하는 것처럼 직접 bean을 만들고 주입을 하는 것이다.
여기서 bean은 특별한 것이 아니라 스프링에서 사용하는 자바객체 정도로 생각을 하면 이해하기 쉽다.
AppConfig파일을 보게되면 등록되어 있는 각 bean을 보게되면 return 값으로 계속해서 객체를 생성해서 반환되는 것을 보게 된다. 언뜻보게되면 메소드를 호출할때마다 객체를 생성하는 것처럼 보이지만 spring은 그렇지 않다.
하나의 bean을 생성해서 컨테이너에 담아두고 호출할 때마다 해당 bean을 반환해주는 형식이다.(싱글톤 패턴)
public class MemberApp {
public static void main(String[] args) {
//ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
ApplicationContext ac = new GenericXmlApplicationContext("classpath:config.xml");
MemberService memberService = ac.getBean("memberService", MemberService.class);
OrderService orderService = ac.getBean("orderService", OrderService.class);
Member member = new Member(1L, "memberA", Grade.BASIC);
memberService.join(member);
Member findMember = memberService.findMember(member.getId());
Order order = orderService.createOrder(member.getId(), "itemA", 10000);
System.out.println("new member: " + member.getName());
System.out.println("find member: " + findMember.getName());
System.out.println("discountPrice: " + order.getDiscountPrice());
}
}
bean을 호출하는 방식은 다음과 같다
'공부기록 > Spring' 카테고리의 다른 글
Spring MVC1 (0) | 2021.12.31 |
---|---|
Spring MVC 세팅 (0) | 2021.12.31 |
Spring - Bean (0) | 2021.12.24 |
Spring - DI 2 (0) | 2021.12.16 |
Spring 다시시작 (0) | 2021.12.04 |