- BeanFactory 와  ApplicationContext 를 스프링 컨테이너라고 한다. 

- BeanFactory 기능을 상속받은 ApplicationContext ( ApplicationContext 는 BeanFactory의 부가기능을 더한 것)

그 밑에는 AnnotationConfig ApplicationContext 구현클래스

 

BeanFactory 
  • 스프링 컨테이너의 최상위 인터페이스
  • 스프링 빈을 관리하고 조회하는 역할을 담당한다
  • getBean() 을 제공
  • 지금까지 우리가 사용했던 대부분의 기능이  BeanFactory가 제공하는 기능이다. 
  • 사실 직접 BeanFactory 를 사용할 일은 거의 없다. 부가기능을 포함 된 ApplicationContext 을 사용한다.

 

ApplicationContext 
  • BeanFactory 기능을 모두 상속받아서 제공
  • BeanFactory 와 ApplicationContext의 차이는?
    • 애플리케이션을 개발할때는 빈은 관리하고 조회하는 기능( BeanFactory )은 물론이고 수많은 부가기능은 ApplicationContext 가 갖고있다.

 

ApplicationContext 부가기능

 

1. 메세지 소스를 활용한 국제화 기능

 : 예를들어 한국에는 한국어로, 영어권으로 가면 영어로 출력

 

2. 환경변수

  : 로컬, 개발, 운영 등을 구분해서 처리

 

3. 애플리케이션 이벤트

  : 이벤트 발행하고 구독하는 모델을 편리하게 지원

 

2. 편리한 리소스 조회

  : 파일, 클래스패스 , 외부 등에서 리소스르 편리하게 조회

 

 

 

 


 

 

스프링은 다양한 설정 형식

 

 

 

 

스프링은 다양한 설정 형식 지원을 한다 > 자바코드, XML , Groovy 등등

 

 

 

어노테이션 기반 자바 코드 설정 사용

 

AnnotationConfigApplicationContext 클래스를 사용하여 자바 코드로 된 설정 정보를 넘긴다. 

new AnnotationConfigApplicationContext(AppConfig.class);

 

 

 

 

XML 설정 사용

 

최근에는 스프링 부트를 많이 사용하면서 XML 기반 설정은 잘 사용하지 않는다.

장점으로는 컴파일 없이 빈 설정 정보를 변경할 수 있다

 

 

 

1. 테스트용 스프링 컨테이너 생성하기

[XmlAppContext 클래스]

public class XmlAppContext {

	@Test
	void xmlAppContext() {
		ApplicationContext ac = new GenericXmlApplicationContext("appConfig.xml");
		MemberService memberService = ac.getBean("memberService", MemberService.class);
		Assertions.assertThat(memberService).isInstanceOf(MemberService.class);
	}
}

 

GenericXmlApplicationContext 만 바뀌었을뿐 나머지는 ApplicationContext  코드와 같다

 

 

 

2. xml 생성하기

 

resource 폴더안에  appConfig.xml 생성하기

 

 

 

[ appConfig.xml ]

appConfig.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"></constructor-arg>
	</bean>
	
	<!--id="memberRepository"가 위의 refer="memberRepository"로 넘어가며 구현객체는 hello.core.member.MemoryMemberRepository-->
	<bean id="memberRepository" class="hello.core.member.MemoryMemberRepository"/>
	
	<bean id="orderService" class="hello.core.order.OrderServiceImpl">
		<constructor-arg name="memberRepository" ref="memberRepository"></constructor-arg>
		<constructor-arg name="discountPolicy" ref="discountPolicy"></constructor-arg>
	</bean>
	
	<bean id="discountPolicy" class="hello.core.discount.RateDiscountPolicy"></bean>

</beans>

 

 

 

아래는 원래 자바코드로 설정한 [ AppConfig.java ] 

@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(memberRepository(), discountPolicy()); //discountPolicy()로 대체해주기
	}
	
	@Bean
	public DiscountPolicy discountPolicy() {
//		return new FixDiscountPolicy();  // 1만원이든 2만원이든 천원 할인적용
		return new RateDiscountPolicy(); // 1만원 천원할인, 2만원이면 2천원할인
	}
	
}

 

 

 

 

AppConfig.java 자바코드와 appConfig.xml 기반의 스프링 설정 정보를 비교해보자

 

[AppConfig.java] 

@Bean
public MemberService memberService() {
    return new MemberServiceImpl(memberRepository());
}

 

 [appConfig.xml]

<bean id="memberService" class="hello.core.member.MemberServiceImpl">
    <constructor-arg name="memberRepository" ref="memberRepository"></constructor-arg>
</bean>
  • <bean id="memberService" class="hello.core.member.MemberServiceImpl">
    • 빈 이름 memberService , 구현 클래스 MemberServiceImpl (패키지 이름까지 모두 적기)
  • <constructor-arg name="memberRepository" ref="memberRepository"></constructor-arg>
    • 생성자 이름 memberRepository , 참조(ref) 를 넣어줘야하는데 없으니 만들자 

 

 

[AppConfig.java] 

@Bean
public MemoryMemberRepository memberRepository() {
    return new MemoryMemberRepository();
}

 

 [appConfig.xml]

<bean id="memberRepository" class="hello.core.member.MemoryMemberRepository"/>

 

 

 

 

[테스트 결과] 빈으로 등록된 메서드 이름이 나옴 

 

 

 

 

 

 

 

스프링은 이렇게 java와 xml 처럼 다양한 설정 형식을 지원한다 

어떻게 가능한가? 

  • BeanDefinition 이라는 추상화가 있다
    • XML 또는 자바코드를 읽어서 BeanDefinition 을 만든다 
    • 스프링 컨테이너는 BeanDefinition 만 알면  XML 인지 자바코드인지 몰라도 된다 
    • 역할과 구현을 나눈 것 처럼!
  • BeanDefinition 을 비 설정 메타 정보라고 하는데 
    • @Bean, <bean> 을 쓰면 각 하나씩 메타 정보가 생성된다.
  • 스프링 컨테이너는 메타정보를 기반으로 스프링 빈을 생성한다. 

 

 

 

 

반응형
LIST

 

 

 

 

부모 타입으로 조회하면 자식 타입까지 함께 조회된다. 

 

 

 

모든 자바 객체의 최고부모인 'Object 타입' 으로 조회하면 모든 스프링 빈을 조회한다. 

클래스에서 눈에 안보이지만 사실 Extends Object를 하고있다.

public class A extends Obect {
}

 

 

 

 

자식 2개인 부모 DiscountPolicy를 조회해보기
public class ApplicationContextExtendsFindTest {

	AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(TestConfig.class);
	
	@Test
	@DisplayName("부모 타입으로 조회시, 자식이 둘 이상이면, 중복 오류가 발생한다.")
	void findBeanByParentTypeDuplicate() {
		DiscountPolicy bean = ac.getBean(DiscountPolicy.class); 
	}
	
	@Configuration
	static class TestConfig {
		
		@Bean
		public DiscountPolicy rateDiscountPolicy() {
			return new RateDiscountPolicy();
		}
		
		@Bean
		public DiscountPolicy fixDiscountPolicy() {
			return new FixDiscountPolicy();
		}
	}
}

 

▶ 부모인 DiscountPolicy 는 자식 RateDiscountPolicy, FixDiscountPolicy 둘 다 조회가 되어 중복오류가 난다. 

 

 

NoUniqueBeanDefinitionException

 

 

 

 

NoUniqueBeanDefinitionException 예외처리 하기 >  assertThrows 

@Test
	@DisplayName("부모 타입으로 조회시, 자식이 둘 이상이면, 중복 오류가 발생한다.")
	void findBeanByParentTypeDuplicate() {
		assertThrows(NoUniqueBeanDefinitionException.class, 
				() -> ac.getBean(DiscountPolicy.class));
	}

 

 

 

빈 이름을 지정해주기
public class ApplicationContextExtendsFindTest {

	AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(TestConfig.class);
	
	@Test
	@DisplayName("부모 타입으로 조회시, 자식이 둘 이상이면, 빈 이름을 지정하면 된다.")
	void findBeanByParentTypeBeanName() {
		DiscountPolicy rateDiscountPolicy = ac.getBean("rateDiscountPolicy" ,DiscountPolicy.class);
		Assertions.assertThat(rateDiscountPolicy).isInstanceOf(RateDiscountPolicy.class); 
	}
	
	@Configuration
	static class TestConfig {
		
		@Bean
		public DiscountPolicy rateDiscountPolicy() {  // 이걸로 지정
			return new RateDiscountPolicy();
		}
		
		@Bean
		public DiscountPolicy fixDiscountPolicy() {
			return new FixDiscountPolicy();
		}
	}
}

 

@Test
@DisplayName("부모 타입으로 조회시, 자식이 둘 이상이면, 빈 이름을 지정하면 된다.")
void findBeanByParentTypeBeanName() {
         DiscountPolicy rateDiscountPolicy = ac.getBean("rateDiscountPolicy" ,DiscountPolicy.class);

 

 

 

특정 하위 타입으로 조회해보기

 

@Test
@DisplayName("특정 하위 타입으로 조히")
void findBeanBySubType() {
    RateDiscountPolicy bean = ac.getBean(RateDiscountPolicy.class);
    Assertions.assertThat(bean).isInstanceOf(RateDiscountPolicy.class); 
}

 

RateDiscountPolicy 하나밖에 없으니까 에러 안난다.

 

 

 

 


 

여기서 궁금증!

 

아래처럼 반환값을 구현체로 해도 되지않을까? 

@Configuration
static class TestConfig {

    //@Bean
    //public DiscountPolicy rateDiscountPolicy() {  
    //    return new RateDiscountPolicy();
    //}
    
    //@Bean
    //public DiscountPolicy fixDiscountPolicy() {
    //   return new FixDiscountPolicy();
    //}
    
    // 반환값을 구현체로?
    @Bean
    public RateDiscountPolicy rateDiscountPolicy() { 
        return new RateDiscountPolicy();
    }

    @Bean
    public FixDiscountPolicy fixDiscountPolicy() {
        return new FixDiscountPolicy();
    }
}

 

▶ 구체적인걸로 해도되지만! 역할과 구현을 나눈 것 처럼 인터페이스(역할)로 해두는게 다형성에도 좋고 가독성에도 좋다!

 

 

반응형
LIST

 

 

 스프링 빈 조회
// 빈 조회
public class ApplicationContextBasicFindTest {

	AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
	
	@Test
	@DisplayName("빈 이름으로 조회")
	void findBeanByName() {
		MemberService memberService = ac.getBean("memberService", MemberService.class);
		System.out.println("memberService = " + memberService);
		System.out.println("memberService.getClass() = " + memberService.getClass());
		
	}
	
}

 

 

 

 

이름 제외하고 타입만 조회할때
@Test
	@DisplayName("이름 없이 타입으로 조회")
	void findBeanByType() {
		MemberService memberService = ac.getBean(MemberService.class); //타입만 매개변수에
		Assertions.assertThat(memberService).isInstanceOf(MemberServiceImpl.class); //isInstanceOf 어떤 객체 인스턴스야?
		
	}

 

 

 

만약 동일한 타입이 둘 이상일때 NoUniqueBeanDefinitionException 중복 오류가 난다. 

public class SameBeanConfig {

	//타입 중복
	@Bean
	public MemberService memberService1() {
		return new MemberServiceImpl(memberRepository());
	}
	
	@Bean
	public MemberService memberService2() {
		return new MemberServiceImpl(memberRepository());
	}
}

 

이럴땐 빈 이름을 같이 적어주기! 

MemberService memberService = ac.getBean("memberService2", MemberService.class);

 

 

 

 

구체 타입으로 조회
@Test
@DisplayName("구체 타입으로 조회")
void findBeanByName2() {
    MemberServiceImpl memberService = ac.getBean("memberService", MemberServiceImpl.class);
    Assertions.assertThat(memberService).isInstanceOf(MemberServiceImpl.class); //isInstanceOf 어떤 객체 인스턴스야?

}

 

 

 MemberServiceImpl memberService = ac.getBean("memberService", MemberServiceImpl.class);

인터페이스명이 아니라 구현한 클래스명으로 한다

 

public class AppConfig {

	@Bean
	public MemberService memberService() {
		return new MemberServiceImpl(memberRepository()); // 구현체
	}
    ...
}

 

하지만 구현에 의존한 거라 유연성이 떨어져서 좋지 않음 (되도록이면 역할에 의존하도록 해야 좋은 코드!)

 

 

 

 

 

예외 테스트 해보기

없는 빈 이름으로 조회시 예외가 터져야하는 테스트
@Test
@DisplayName("빈 이름으로 조회 X")
void findBeanByNameX() {
    
    MemberService XXX= ac.getBean("XXX", MemberService.class); //없는 빈 이름
    
    // assertThrows(무조건 예외가 터져야한다, 람다식~)
    org.junit.jupiter.api.Assertions.assertThrows(NoSuchBeanDefinitionException.class,
            () -> ac.getBean("XXX", MemberService.class));
}

 

NoSuchBeanDefinitionException : 빈이름이 찾을 수 없다는 에러

 

* Assertions는 junit으로 쓴다. 

 

반응형
LIST

 

기존에는 개발자가 직접 자바코드로 모든 것을 했다면

이제는 스프링 컨테이너에 객체를 스프링 빈으로 등록하고 스프링 컨테이너에서 스프링 빈을 찾아서 사용하도록 변경되었다 

 

 

먼저 스프링 설정정보를 설정해보자

 

1.  AppConfig에 @Configuration 어노테이션 붙이기

  • @Configuration 이 붙은 AppConfig 클래스 내의 모든 메서드들을 스캔하여 반환하는 객체를 빈으로 등록시키고 스프링 컨테이너에 객체를 집어넣어 관리한다.

 

2. 각 메서드에 @Bean 어노테이션 붙이기

  • @Bean 이 붙은 메서드명을 스프링 빈으로 등록하고 스프링 빈의 이름으로 사용한다.
  • 빈 이름을 변경할 수도 있다.
    • @Bean(name="memberService2") 
@Configuration
public class AppConfig {

	@Bean
	public MemberService memberService() {
		return new MemberServiceImpl(memberRepository());
	}
    ... 
}

 

 

 

이렇게 @Bean 이 붙은 메서드가 스프링 컨테이너에 빈 이름으로 저장이 됨 

 

※ 주의

빈 이름은 항상 다른 이름을 부여해야한다.

같은 이름을 부여하면 다른 빈이 무시되거나 기존 빈을 덮어버리는 오류가 발생한다.

 

 

?궁금증? @Configuration 어노테이션을 사용하면 객체를 빈으로 등록되니 @Bean 안 써도 되지않을까? 

@Bean 어노테이션이 붙지 않아도 메서드가 빈으로 등록이 되긴하지만 명시적으로 빈을 등록하면 코드의 가독성과 명확성을 높일 수 있으니 쓰는걸 권장한다고 한다. 

 

 

정리해서 @Configuration @Bean 두 어노테이션을 붙이면 스프링 컨테이너에 의해 관리되고, 필요에 따라 인스턴스화되고 주입된다.

 

 

 

스프링 컨테이너 생성하기 

 

 

1. 스프링 컨테이너 생성
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);

2. applicationContext.getBean(이름,반환타입)
MemberService memberService = applicationContext.getBean("memberService", MemberService.class);

 

  • ApplicationContext 는 스프링 컨테이너 
    • 모든 객체들을 관리해주는 곳
  • ApplicationContext 는 인터페이스
    • 그 인터페이스를 구현한 것 중 하나가 AnnotationConfigApplicationContext 이다.



기존에는 개발자가 필요한 객체를 AppConfig 를 사용해 직접 생성하고 조회하고 DI를 했지만 이제부턴 스프링 컨테이너를 통해서 사용한다

  • ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
    • 스프링 컨테이너를 생성할 때는 구성 정보를 지정해줘야한다 
    • 파라미터에 AppConfig 을 넣으면 AppConfig 의 @Bean 이 붙은 설정정보들을 스프링 컨테이너에 넣어서 관리해준다.

 

 

스프링 컨테이너를 통해 필요한 스프링 빈(객체)를 찾을 수 있다.

  • MemberService memberService = applicationContext.getBean("memberService", MemberService.class);
    • getBean("찾을 객체 메서드 이름",  반환타입)
    • AppConfig에서 @Bean이 붙여진 memberService 이름을 찾는다.

 

 

 

결과

public class MemberApp {

	public static void main(String[] args) {
		ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
		MemberService memberService = applicationContext.getBean("memberService", MemberService.class); // 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("findMember = " + findMember.getName());
	}

}
@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(memberRepository(), discountPolicy()); //discountPolicy()로 대체해주기
	}
	
	@Bean
	public DiscountPolicy discountPolicy() {
//		return new FixDiscountPolicy();  // 1만원이든 2만원이든 천원 할인적용
		return new RateDiscountPolicy(); // 1만원 천원할인, 2만원이면 2천원할인
	}
}

* key(빈이름) : memberService / value(빈객체) : MemberServiceImpl

 

 

@Bean 붙은 메서드들이 빈으로 정의 된걸 볼 수 있다. 

 

 

 

 

스프링 컨테이너 생성 과정

 

1. 스프링 컨테이너 생성

 

 

 

 

2. 스프링 빈 등록

 

 

 

 

3. 스프링 의존 관계 설정

 

  • 동적인 의존관계 설정을 스프링이 해준다.
  • 스프링 컨테이너는 설정 정보를 참고하여 의존관계를 주입(DI)한다. 
    • '설정 정보를 참고한다'는 말은 "MemberService 에서 memberRepository를 의존할거야"  이런걸 보고 DI 하는 것단순히 자바 코드를 호출하는것이 아님.

 

 

 

스프링은 빈을 생성하고 의존관계를 주입하는 단계가 나누어져있다 

하지만 자바코드로 스프링 빈을 등록하면 생성자를 호출하면서 의존관계 주입도 한번에 처리된다. 

이부분은 의존관계 자동주입을 공부해보기! 

 

 

 

 

이제 스프링빈이 잘 등록됐는지 테스트해보자

 

빈 출력

 

  • getBeanDefinitionNames() : 등록된 모든 빈의 이름을 문자열 배열로 반환하는 메서드
public class ApplicationContextInfoTest {

	AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
	
	@Test
	@DisplayName("모든 빈 출력하기")
	void findAllBean() {
		String[] beanDefinitionNames = ac.getBeanDefinitionNames(); // bin에 등록된 모든 이름 출력
		for (String beanDefinitionName : beanDefinitionNames) { //리스트 for문으로 보게
			Object bean = ac.getBean(beanDefinitionName); //타입을 지정하지 않았기에 Object로!
			System.out.println("name = " + beanDefinitionName + "Object = " + bean);
		}
	}
}

 

 

 

빨간 네모박스는 스프링이 내부적으로 스프링을 자체 확장하기 위한 기반 빈들이고

초록색 네모박스가 내가 직접 등록한 빈들이다.

 

 

 

빨간 네모박스 빼고 내가 직접 등록한 빈들만 보고싶다면? 

public class ApplicationContextInfoTest {

	AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
	
	@Test
	@DisplayName("모든 빈 출력하기")
	void findAllBean() {
		String[] beanDefinitionNames = ac.getBeanDefinitionNames(); // bin 정리된 이름 등록
		for (String beanDefinitionName : beanDefinitionNames) { //리스트 for문으로 보게
			Object bean = ac.getBean(beanDefinitionName); //타입을 지정하지 않았기에 Object로!
			System.out.println("name = " + beanDefinitionName + "Object = " + bean);
		}
	}
	
	//스프링 내부빈 말고 내가 등록한 빈들 출력하기
	@Test
	@DisplayName("애플리케이션 빈 출력하기")
	void findApplicationBean() {
		String[] beanDefinitionNames = ac.getBeanDefinitionNames(); 
		for (String beanDefinitionName : beanDefinitionNames) { 
			//getBeanDefinition은 빈에 대한 각 정보들을 꺼낼 수 있음
			BeanDefinition beanDefinition = ac.getBeanDefinition(beanDefinitionName); 
			
			//BeanDefinition.ROLE_INFRASTRUCTURE : 스프링 내부 빈
			//BeanDefinition.ROLE_APPLICATION : 직접 등록한 빈
			if(beanDefinition.getRole() == BeanDefinition.ROLE_APPLICATION) { 
				Object bean = ac.getBean(beanDefinitionName); //타입을 지정하지 않았기에 Object로!
				System.out.println("name = " + beanDefinitionName + "Object = " + bean);
			}
		}
	}
}

 

 

 

빨간 네모박스 제외한 내가 등록한 빈만 출력한 것을 볼 수 있다. 

 

 

 

애플리케이션 빈 출력할때 getRole() 로 구분할 수 있다. 

  • BeanDefinition.ROLE_APPLICATION 
    • 내가 애플리케이션을 개발하기 위해 등록한 빈들
  • BeanDefinition .ROLE_INFRASTRUCTURE
    • 스프링이 내부에서 사용하는 빈들

 


 

여기서 든 궁금증! 


AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);

MemberService memberService = ac.getBean("memberService", MemberService.class);

 

1. getBean() 메서드 파라미터에 왜 구현 클래스를 넣어야하지?

빈 이름은 고유한데 굳이 구현 클래스를 적어야하나?

만약 빈 이름이 여러개일 경우 구현클래스를 적지않으면 일일히 찾아야하니 적어야할텐데..

▶ 아래처럼 getBean() 에서 반환타입을 적지않고 이름만 명시할 경우 Object 로 반환된다 

Object memberService = ac.getBean("memberService");

가독성과 안정성 측면에서 안좋다고 함 그러므로 가능한 한 반환받을 객체의 타입을 명시하는게 좋아서 반환타입을 적는게 좋다! 

 

 

2. 그리고 구현 클래스니까 getBean("memberService", MemberServiceImpl.class) 를 써도 되지않나? 

MemberService 인터페이스인데 왜 .interface 라고 적지않는거지?

▶ 부모 인터페이스 또는 구현체를 적어도 상관은 없지만 다형성을 위해 부모 인터페이스를 넣는게 더 좋다고 함

▶ 자바 언어는 빌드가 되면 인터페이스가 .class 가 된다 따라서 구분하지 않는다고 함

 

 

 

 

 

반응형
LIST

 

제어의 역전이란

프레임워크가 내 코드를 대신 호출해준다. 말그대로 제어권이 뒤바껴서 '제어의 역전'이라고 한다.

 

옛날에는 클라이언트의 구현객체가 스스로 필요한 서버 구현객체를 생성하고 연결하고 실행했다.

(구현객체가 제어 흐름을 스스로 다 조종)

지금은 AppConfig 가 등장한 이후 프로그램의 제어 흐름은 이제 AppConfig 가 가져간다.

 

그러므로 주문 서비스(역할) OrderServiceImpl 은 필요한 인터페이스를 실행하지만 어떤 구현객체들이 실행하는지 모른다 (제어권을 AppConfig 이 가져가서)

 

이렇듯 프로그램의 제어 흐름을 직접 제어하는것이 아닌 외부에서 관리하는것을 제어의 역전이라고 한다.

 

 

 

 

프레임 워크 VS 라이브러리

 

  • 프레임워크
    • '내가 작성한 코드' 를 제어하고 대신해서 실행하면 그것은 프레임워크 (= Junit)
    • ex) 개발자는 로직만 개발함, 이것을 실행하고 제어하는건 junit 과 테스트 프레임이 가져가서 대신 실행해준다. @beforeEach 를 먼저 실행하고 @Test를 실행하는 라이프 사이클안에서 프레임워크가 적절한 타이밍에 나의 코드를 호출하는 것
  • 라이브러리
    • 프레임워크와 반면에 '내가 작성한 코드' 가 직접제어의 흐름을 담당하면 라이브러리
    • ex) 자바객체를 xml, json으로 바꿀 때 내가 라이브러리를 불러서 직접 호출하듯이

 

 

 

 

  • 의존관계 주입

: 정적인 클래스 의존관계와 실행 시점에 결정되는 동적인 객체(인스턴스) 의존관계 둘을 분리해서 생각해야한다. 

의존관계 주입을 사용하면 정적인 클래스의 의존관계를 변경하지 않고 (=코드를 전혀 건들지 않고) 동적인 객체 인스턴스 의존관계를 쉽게 변경할 수 있다. 

 

OrderServiceImpl 은 DiscountPolicy 인터페이스만 의존한다 실제 어떤 구현객체(FixDiscountPolicy, RateDiscountPolicy)가 사용될지는 모름

 

 

  • IoC , DI 컨테이너

AppConfig 처럼 객체를 생성하고 관리하면서 의존관계를 연결해주는 것을 IoC, DI 컨테이너라고 한다.

의존관계 주입에 초점을 맞추어 최근에는 주로 'DI 컨테이너' 라고 한다

또는 어샘블러, 오브젝트 팩토링 등으로 불리기도 한다 

 

 

 

 

 

 

 

반응형
LIST

 

먼저

자바 11, 이클립스 설치는 미리 해놓고 스프링으로 프로젝트 생성하기

 

https://start.spring.io/

스프링 부트 스타터 사이트에서 스프링 프로젝트 생성하기! 

 

 

 

프로젝트 설정

  • 스프링 Gradle-groovy 선택
  • 스프링 버전 2.x 로 선택
    • 3.x 는 자바 17버전
    • 2.x 는 자바 11버전으로 해야 오류가 안난다
    • (SNAPSHOT)등의 수식어가 붙지 않은걸로 선택해야함!
  • groupId: hello
  • Artifact : core ( 프로젝트 빌드 명 )
  • Dependencies : 선택 x
    • 의존관계를 아무것도 선택하지 않으면 스프링 코어쪽 라이브러만 간단하게 구성해줌

 

 

이클립스 실행하고 생성한 프로젝트 가져오기

 

만약 아래처럼 이클립스 프로젝트 가져올때 오류가 난다면? 

 

File -> import를 통해서 Existing Gradle Project를 선택하여 프로젝트 가져오면 된다. 

 

 

 

build.gradle 에 가면 프로젝트 설정한거 볼 수 있음

 

 

 

반응형
LIST

+ Recent posts