[Spring Basic] 스프링과 IoC
AppConfig를 도입해 제어의 흐름을 가져왔다.
각각의 서비스들은 필요한 인터페이스를 호출하지만, 어떤 구현 객체들이 호출되는지는 모른다.
프레임워크와 라이브러리도 제어의 대상에 따라 구분할 수 있다.
프레임워크가 내가 작성한 코드를 제어하고 대신 실행하면 프레임워크라고 부르고,
반대로 내가 작성한 코드가 직접 제어의 흐름을 담당한다면 라이브러리라고 부른다.
프레임워크의 대표적인 예시로는 JUnit테스트가 있다.
정적인 클래스 의존 관계와 실행 시점에 결정되는 동적인 객체 의존 관계로 나눌 수 있다.
정적인 클래스 의존관계는 클래스에서 import하는 요소들만 보고 의존관계를 판단하는걸 의미한다.
OrderServiceImpl은 MemberRepository와 DiscountPolicy에 의존함을 알 수 있지만, 실제로 어떤 객체가 주입될지는 알 수 없다.
동적인 객체 의존 관계는 애플리케이션이 실행되는 시점에 생성되는 객체 인스턴스의 참조가 연결되는 관계이다.
AppConfig를 통해 실행할 때 마다 의존관계를 새롭게 설정할 수 있다는건데.. 정적인 의존관계를 조작하지 않고도 객체 인스턴스를 쉽게 변경할 수 있다는 의미이다.
지금은 AppConfig를 사용해 IoC와 DI를 구현했는데, 이렇게 IoC와 DI를 구현해주는 요소를 IoC컨테이너, DI컨테이너라고 부른다. (스프링도 컨테이너이다.)
AppConfig를 스프링의 도움을 받아 다시 작성해보자.
package hello.core;
import hello.core.discount.DiscountPolicy;
import hello.core.discount.FixDiscountPolicy;
import hello.core.discount.RateDiscountPolicy;
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 MemoryMemberRepository memberRepository() {
return new MemoryMemberRepository();
}
@Bean
public OrderService orderService(){
return new OrderServiceImpl(new MemoryMemberRepository(), new FixDiscountPolicy());
}
@Bean
public DiscountPolicy discountPolicy(){
// return new FixDiscountPolicy();
return new RateDiscountPolicy();
}
}
@Configuation을 통해 애플리케이션의 설정 정보를 담당한다고 스프링에게 알려주자.
각 메서드에 @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();
// MemberService memberService = new MemberServiceImpl();
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
MemberService memberService = applicationContext.getBean("memberService", MemberService.class);
Member member = new Member((long)1, "memberA", Grade.VIP);
memberService.join(member);
Member findMember = memberService.findMember((long) 1);
}
}
스프링 컨테이너에서 요소를 꺼내 올 때는 ApplicationContext를 사용한다.
AnnotationConfigApplicationContext의 인자로 설정 파일을 넣어 등록한 빈들을 사용한다.
스프링 컨테이너는 @Configuation이 붙은 요소를 설정 정보로 사용하고, @Bean으로 등록된 메서드들을 모두 호출해 반환되는 객체들을 스프링 컨테이너에 등록해준다.
즉, applicationContext는 스프링 컨테이너이고 컨테이너에 등록된 객체들은 스프링 빈이라고 한다.
이제는 스프링 컨테이너를 통해 요소들을 꺼낼 수 있다.
getBean의 첫 번째 인자로는 스프링 빈으로 등록된 메서드의 이름을, 두 번째 인자로 반환타입을 넣는다. (이름은 변경 가능)
첫 번째 인자를 생략하고 타입만 지정할 경우 해당하는 타입이 하나만 있을 경우 정상적으로 동작하지만, 두 개 이상의 타입이 있을 경우 예외가 발생한다. (같은 타입이 둘 이상인 경우 인자를 생략하지 말자.)
ApplicationContext는 스프링 컨테이너이면서 인터페이스이다.
ApplicationContext의 구현체 중 하나가 AnnotationConfigApplicationContext가 된다.
AppConfig.class를 인자로 주면 빈으로 등록된 요소들을 싹다 호출시키며 컨테이너에 저장한다.
이 때 빈 이름은 특별히 설정하지 않으면 메서드의 이름으로 사용하는데, 이름이 같은 경우 오류가 발생하니 주의하자.
이후 설정관계를 참고해 의존관계를 주입해준다.
즉, 먼저 스프링 빈을 생성한 후 의존관계를 주입한다.
단순하게 자바 코드로 스프링 빈을 등록하면 두 가지 작업을 한 번에 처리하게 되는데, 이 부분은 뒤에서 자세히 알아보자.
스프링 빈을 조회할 때 상속 관계가 있으면, 부모 타입으로 조회 시 그 밑의 자식들도 모두 조회된다.
때문에, 자바 객체의 최고 조상인 Object타입으로 조회 시 모든 스프링 빈을 조회한다.
부모 타입으로 빈을 조회할 때는 빈 이름을 함께 지정해주거나, 특정 하위 타입으로 바로 지정해주도록 하자.
ApplicationContext 인터페이스의 상위 인터페이스로 BeanFactory 인터페이스가 있다.
BeanFactory은 스프링 컨테이너의 최상위 인터페이스로, 스프링 빈을 관리하고 조회하는 getBean 등의 메서드를 제공한다.
ApplicationContext는 BeanFactory의 기능을 모두 상속받아 제공한다.
BeanFactory 외에도 ApplicationContext는 여러 가지 인터페이스들을 상속받는다.
MessageSource : 국제화 기능을 제공한다. 한국에서 들어오면 한국어로, 영어권에서 들어오면 영어로..
EnvironmentCapable : 로컬, 개발, 운영 등 여러 환경변수들을 처리한다.
ApplicationEventPublisher : 이벤트를 발행하고 구독하는 모델을 지원한다.
ResourceLoader : 외부에서 파일을 읽어올 때 리소스를 편리하게 조회한다.
일반적으로 애플리케이션을 만들 때 필요한 공통 기능을 인터페이스로 묶고, ApplicationContext가 해당 인터페이스를 상속받아 부가 기능을 제공한다.
그래서, BeanFactory와 ApplicationContext를 스프링 컨테이너라고 부르지만, BeanFactory를 직접 사용하는 경우는 거의 없다.
앞에서도 간단히 언급했지만, ApplicationContext를 구현하는 객체에도 여러가지가 있다.
자바 코드 기반(Annotaion)은 현재 가장 많이 사용하는 방법이고, 지금까지 이 방법을 사용해 개발해왔다.
XML설정은 스프링 부트를 사용하게 되며 거의 사용하지 않지만, 많은 레거시 프로젝트들이 XML로 되어 있는 경우가 있고, 컴파일 없이 빈 설정 정보를 변경할 수 있는 등 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 = "hello.core.member.MemberServiceImpl">
<constructor-arg name = "memberRepository" ref="memberRepository"/>
</bean>
<bean id ="memberRepository" class ="hello.core.member.MemoryMemberRepository"/>
<bean id="orderService" class="hello.core.order.OrderServiceImpl">
<constructor-arg name = "memberRepository" ref = "memberRepository"/>
<constructor-arg name = "discountPolicy" ref = "discountPolicy"/>
</bean>
<bean id="discountPolicy" class="hello.core.discount.RateDiscountPolicy"/>
</beans>
위와 같이 appConfig.xml파일을 만들어 설정해 줄 수 있다.
빈을 사용할 때와 동일하게 1:1로 매핑해주면 된다... 정도만 알고 넘어가자.
자바 코드와 xml외에도 새로 파일을 정의해서 설정해 줄 수도 있다.
역할과 구현을 개념적으로 분리해 다양한 설정 형식을 지원할 수 있다.
xml코드를 읽어서 BeanDefinition을 만들고.. 자바 코드를 읽어서 BeanDefinition을 만들고..
@Bean, <bean> 하나당 설정 메타정보인 BeanDefinition이 생성되고, 스프링 컨테이너는 이 메타정보를 기반으로 스프링 빈을 생성한다.
'Spring > Spring' 카테고리의 다른 글
[Spring Basic] 의존관계 주입 (0) | 2022.08.08 |
---|---|
[Spring Basic] Singleton (0) | 2022.08.07 |
[Spring Basic] JPA와 AOP (0) | 2022.07.29 |
[Spring Basic] 동기와 비동기 (0) | 2022.07.15 |
[Spring Basic] MyBatis (0) | 2022.07.12 |
댓글
이 글 공유하기
다른 글
-
[Spring Basic] 의존관계 주입
[Spring Basic] 의존관계 주입
2022.08.08 -
[Spring Basic] Singleton
[Spring Basic] Singleton
2022.08.07 -
[Spring Basic] JPA와 AOP
[Spring Basic] JPA와 AOP
2022.07.29 -
[Spring Basic] 동기와 비동기
[Spring Basic] 동기와 비동기
2022.07.15