본문 바로가기

프로그래밍/JAVA & SPRING

[박재성] Spring 기반 웹 프로그래밍 7강 - dependency injection

반응형

코드를 통한 dependency injection 설명

DI를 spring 프레임워크로 설정

main method 기반으로 spring 프레임워크 설정 테스트

junit 기반으로 spring 프레임워크 설정 테스트

 

간단한 예제의 소스코드를 통해 

dependency injection를 설정하고 

 

springframework기반으로 해서 dependency injection구조로 개발되어 있는 코드를

어떻게 설정하는지 살펴본다.

 

요구사항

- 하나의 Interface 기반하에서 "Hello World!"와 "Hi World!" 메시지를 출력해야 한다.

- 출력하는 메시지를 생성하는 부분(Provider)과 생성된 메시지를 Rendering(Renderer)하는 부분

 

이 분리되어야 한다.

 

src/test/java/

ㄴnet.slipp.di

 

DI를 적용하지 않았을 경우

클라이언트(main메소드, 사용자..)는 

MessageRenderer는 직접 객체를 생성(메시지 출력로직)해서  

화면에 표시하는 Provider객체를 N개를 각각 생성해서

처리해야 한다.

 

MessageProvider -> 구현체 HelloWorldMessageProvider

MessageProvider -> 구현체 HiWorldMessageProvider

 

MessageProvider provider = new HelloWorldMessageProvider();

MessageProvider provider = new HiWorldMessageProvider();

 

클래스끼지 강한결합 상태를 가지고 있다.

 

DI를 이용한 방법(외부에서 주입)은 여러가지가 있는데

MessageRenderer가 MessageProvider를 의존하고 있어서

멤버변수에 주입받을 객체(MessageProvider)를 변수에 선언하고

setMethod() 를 통해서 주입받는 방법이 있다.

 

그러면 MessageRenderer가 의존의존관계에 있는 클래스는 어디서 결정하냐?

MessageRenderer를 사용하고 있는 클라이언트 코드에서 결정된다.

 

MessageRenderer render = new MessageRenderer();
render.setMessageProvider(new HelloWorldMessageProvider());
render.render();

render.setMessageProvider(new HiWorldMessageProvider());
render.render();

 

 

이 방식(DI)으로 구현하면

MessageRenderer를 사용하는 클라이언트 코드쪽에서 

MessageRenderer의 내부동작을 얼마든지 변경할 수 있다.

그만큼 유연성이 증가하게 된다.

 

매번 프로그램내에서 클래스간의 의존관계를 설정하는 데에는 불편함이 있고

MessageRenderer를 사용할 때 매번 MessageProvider를 전달하는 것이

중복 코드가 발생하게 된다. 

 

 

부연하면 사용자, main메소드, 클라이언트에서 

사용할 객체(MessageProvider) 생성시에 

필요한 객체(new HelloWorldMessageProvider(), new HiWorldMessageProvider() ,,,)를 

반복해서 주입시켜줘야 하는 번거로움이 있다.

 

이런 부분을 스프링프레임워크의 설정파일을 이용해서

다음과 같이 구현할 수 있다.

 

이전에는 자바코드로 클래스간의 의존관계를 설정한 것을

스프링 설정파일을 이용해서 매핑(?)을 해보기로 한다.

 

src/test/resources에 di.xml을 생성한다.

xml파일은 spring bean configuration file로 하고 

namespace는 'p'를 선택한다.

 

클래스간의 의존관계 주입 설정파일은 아래와 같다.

 

<bean id="messageProvider" class="net.slipp.springdi.HelloWorldMessageProvider" />

<bean id="messageRenderer" class="net.slipp.springdi.MessageRenderer">
  <property name="messageProvider" ref="messageProvider"></property>
</bean>

 

이 의존관계를 자바 코드에서 읽어본다.(MessageRenderer 클래스의 main()에서)

프로그램을 실행(사용)하는 코드를 보면

요구사항에 따라 다른 객체를 주입하는 코드는 없다.

사용자와 가까운(?) Renderer를 객체 생성하는 코드만 있다. (Renderer는 제일 말단에 있다?)

 

요구사항에 따른 변화는 xml 설정파일에서 일어난다.

 

위와 같이 클래스간의 의존관계를 설정파일(di.xml)로 해 놓으면 

요구사항의 변동이 있을때 

프로그램 코드(자바코드)는 그대로 두고 설정파일만 바꾸면 된다.

 

 

String configLocation = "di.xml";

ApplicationContext ac = new ClassPathXmlApplicationContext(configLocation);

MessageRenderer bean = ac.getBean("messageRenderer", MessageRenderer.class);

bean.render();

위의 코드를 보면 컨테이너를 생성하고 getBean()으로 bean을 취득하고

bean을 실행시키는데 

컨테이너를 생성, bean() 취득방법을 몰라도 처리할 수 있게

스프링프레임워크에서 제공하는 JUnit라이브러리가 있다.

 

다시말해 main()메소드로 테스트 했던것을

JUnit 기반으로 테스트 할 수 있다.

 

JUnit을 클래스패스에 추가한다.

pom.xml에 dependency추가 - 빌드 - F5 

MessageRenderer의 new - Junit Test Case를 만든다.

그리고 JUnit의 runner를 스프링에서 지원하는 runner로 바꾼다.

@RunWith(SpringJUnit4ClassRunner.class)

SpringJUnit4ClassRunner.class에서 에러가 뜨는데

이는 spring-test 라이브러리(모듈)에 대한 의존관계를 설정하지 않아서이다.

<dependency>

  <groupId>org.springframework</groupId>

  <artifactId>spring-test</artifactId>

  <version>${org.springframework.version}</version>

</dependency>

 

의존객체가 많아지면 ${org.springframework.version}를 써서 버전관리를 해준다.

org.springframework.version도 아래와 같이 설정해준다.

<properties>

  <org.springframework.version>4.0.5.RELEASE</org.springframework.version>

</properties>

 

모듈추가 - 빌드 - F5

 

@RunWith(SpringJUnit4ClassRunner.class)

JUnit4에 대한 ClassRunner를 변경하였다.

 

@ContextConfiguration("di.xml")

스프링에서 지원하는 ContextConfiguration를 이용해서 

어노테이션(@)을 활용해서 스프링 설정파일을 지정할 수 있다.

 

이런 후에 테스트 하고 싶은 것. MessageRenderer를 

@Autowired로 주입받아 (@Autowired도 스프링에서 지원하는 Autowired클래스를 이용)

private MessageRenderer messageRenderer;

 

메소드에서 messageRenderer.render();를 테스트한다.

테스트하면 에러가 나는데

@ContextConfiguration("di.xml")로 하면

기본적으로 해당 폴더에 있는 xml을 찾는다.

현재는 클래스 패스(src/test/resources)에 있기 때문에

@ContextConfiguration("classpath:di.xml")로 변경한다.

 

스프링기반으로 테스트를 한다면 

main()의 자바코드를 이용하는 방법 보다도

JUnit 기반으로 테스트 할 수 있다.

 

참고) 아래의 중복코드는 이클립스 템플릿 플러그인으로 설정해보자

@RunWith(SpringJUnit4ClassRunner.class)

@ContextConfiguration("classpath:/di2.xml")



${is1:import('org.junit.runner.RunWith')}

${is2:import('org.springframework.test.context.ContextConfiguration')}

${is2:import('org.springframework.test.context.junit4.SpringJUnit4ClassRunner')}

@RunWith(SpringJUnit4ClassRunner.class)

@ContextConfiguration("classpath:/di2.xml")

아래처럼 정리해서 template으로 넣어보자.

${is1:import('org.junit.runner.RunWith')}${is2:import('org.springframework.test.context.ContextConfiguration')}${is3:import('org.springframework.test.context.junit4.SpringJUnit4ClassRunner')}@RunWith(SpringJUnit4ClassRunner.class)

@ContextConfiguration("classpath:/${cursor}")