목차

  1. Dependency Injection의 기본 개념을 이해
  2. 스프링 컨테이너와 ApplicationContext를 이해
  3. XML, Annotation, Java를 이용한 DI 설정 방법 이해
  4. 빈 객체의 스코프와 라이프사이클을 이해




DI(Dependency Injection)

  • 엔터프라이즈 애플리케이션 개발할 때는 하나의 기능을 처리를 수행하기 위해 여러 개의 콤포넌트를 조합 해서 구현하는 경우가 일반적이다
    • 데이터베이스 컴포넌트
    • GUI 관련 컴포넌트
    • 외부 접속 컴포넌트
    • 다양한 오픈소스 라이브러리
  • 여러 개의 컴포넌트를 통합할 때 의존 관계 주입(Dependency Injection) 디자인 패턴 이 매우 효과적이다

DI(Dependency Injection) 란?

  • 의존 관계 주입을 의미
  • 오브젝트 간의 의존 관계를 만드는 것
  • 스프링 프레임워크는 런타임시 사용할 객체들의 의존 관계를 부여한다
  • 객체 간의 결합도를 낮춘다

IoC(Inversion of Control) 란?

  • 역전 제어, 즉 인스턴스를 제어하는 주도권이 역전 된다는 의미
  • 컴포넌트를 구성하는 인스턴스 생성과 의존 관계 연결을 개발자의 소스 코드가 아닌 DI 컨테이너가 대신해 주기 때문에 제어가 역전되었다고 정의함

IoC 컨테이너

  • 스프링 빈의 생성, 관계, 조립, 생명주기를 관리하는 스프링 프레임워크의 핵심
  • 의존관계주입(Dependency Injection) 을 이용하여 어플리케이션을 구성하는 컴포넌트들을 관리한다.
  • 인터페이스와 외부 메타 정보를 결합해 의존성을 해결하는 기술
  • 스프링 프레임워크가 제공하는 IoC 컨테이너를 통해 인스턴스의 생명주기 관리 및 의존 관계 주입을 처리한다

주도권 역전이 왜 필요한가??(Update 2020.04.17)

  • 생성자 호출 뿐만 아니라, 생성된 객체의 생명주기 관리까지 모든 객체에 대한 제어권을 프레임워크(컨테이너)에게 주면서 개발자는 비즈니스 로직에만 신경 쓸 수 있도록 해주기 때문이다.

의존관리 방법

  • new 연산자를 사용
    • MemberSampleMain이 MemberService를 new로 생성하고 MemebrService 가 MemberDao를 new로 생성한 다음 각각의 인스턴스를 이용하는 형태
      Class MemberService {
      MemberDAO memberDAO = new MemberDAO();
      }
      
  • DI 컨테이너 활용(new 연산자 제거)
    • MemberSampleMain이 이용하는 MemberService 인스턴스, 그리고 MemberService가 이용하는 MemberDao 인스턴스는 DI 컨테이너가 생성.
    • MemberDao 인스턴스를 MemberService에 인젝션(의존 관계 주입)
  • DI 컨테이너 활용(인터페이스 이용)
    • 인터페이스 기반의 컴포넌트화를 실현하려면 ProductService와 ProductDao (라는 이름)를 인터페이스로 하고, 그 구현 클래스는 인터페이스 이름에 Impl을 덧붙임

스프링 컨테이너 & ApplicationContext

스프링 빈(Spring Bean) 이란?

  • 스프링 컨테이너가 관리하는 객체

스프링 컨테이너 종류

  • BeanFactory
    (org.springframework.beans.factory.BeanFactory)
    • 빈의 생성, 빈의 의존관계 관리등의 DI의 기본 기능을 제공
    • 빈이 많지 않고 경량 컨테이너로 작업할 때 활용
    • XML 파일로부터 설정 정보를 활용하는 가장 많이 사용되는 클래스
  • ApplicationContext
    (org.springframework.context.ApplicationContext)
    • 일반적인 스프링 컨테이너를 의미
    • BeanFactory 인터페이스를 상속받은 하위 인터페이스로 확장된 기능 제공
      • Internationalization, AOP, Transaction Management
    • XML 파일로부터 설정 정보를 활용하는 가장 많이 사용되는 클래스

WebApplicationContext

(org.springframework.web.context.support.XmlWebApplicationContext)

  • 웹 애플리케이션을 위한 ApplicationContext
  • XML 파일로부터 설정 정보를 활용하는 가장 많이 사용되는 클래스
  • WebApplicationContext(WAC) 종류
    • ContextLoaderListener
      • Persistence(DAO), Service 관련 스프링 빈들을 등록
      • 웹 어플리케이션 전체에서 사용할 WAC 객체 생성
      • root-context.xml 파일에 설정
    • DispatcherServlet
      • 컨트롤러와 같은 서블릿 관련 빈 등록
      • 해당 서블릿 마다 사용할 WAC 객체 생성
      • servlet-context.xml 파일에 설정
  • web.xml에서 ConetxtLoadListener, DispatcherServlet 를 사용하여 ApplicationContext 생성

DI(Dependency Injection) 설정 방법

  • XML, Annotation, JAVA 기반 설정을 통해서 객체간의 의존 관계 를 설정한다

XML을 이용한 DI 설정


DI-XML의 전체 소스코드는 이곳 Github를 참고해 주세요


  • XML을 이용한 Denpendency Injection에는 생성자 방식, 설정자 방식 이 있다.
  • 생성자 기반 의존성 주입(Constructor based dependency Injection)
    • 생성자의 인수를 사용해 의존성을 주입
    • 설정파일 XML에 constructor-arg 태그를 사용하여 주입할 컴포넌트를 설정
  • 설정자 기반 의존성 주입(Setter based dependency Injection)
    • 메서드의 인수를 통해 의존성을 주입
    • 설정파일 XML에 property 요소의 name 속성 에 주입할 컴포넌트의 이름을 설정

XML을 이용한 DI 예시

  • ApplicationContext의 내용

  • Test & 결과

  • Bean 태그 속성


Annotation을 이용한 DI 설정


DI-Annotation의 전체 소스코드는 이곳 Github를 참고해 주세요


  • JDK5 버전부터 추가
  • 메타데이터를 XML등의 문서에 설정하는 것이 아니라 소스 코드에 “@애노테이션” 의 형태로 표현.
  • 클래스, 메소드, 필드의 선언부에 표현하여 특정 기능이 적용되었음을 알려줌
  • 애플리케이션 규모가 커질수록 XML 설정이 복잡하여 애노테이션 적용으로 개선

  • 빈 정의 파일 주요 스키마

Annotation을 이용한 DI 예시

  • 스프링 주요 태그
    • 태그
      • @Autowired, @Resource, @Required를 이용할 때 선언
      • XML 파일에 이미 등록된 빈들의 애노테이션 기능을 적용하기 위해 선언
      • 빈을 등록하기 위한 검색 기능은 없다.
    • <context:component-scan base-package=“패키지명”/> 태그
      • @Component, @Service, @Repository, @Controller등의 컴포넌트 이용할 때 선언한다
      • 특정 패키지 안에 클래스를 검색해서 빈을 자동으로 찾아서 DI 컨테이너에 등록
      • 이 설정을 사용할 경우에는 를 선언할 필요가 없다
  • @Component 확장 애너테이션

  • 만약 다음 태그가 선언되었을 경우는 생략 가능
    • mvc:annotation-driven 태그
    • context:component-scan 태그

1. 설정 방법

  1. ApplicationContext 에 빈 객체 등록한다(DAO, Service)
  2. ApplicationContext 에 태그를 입력한다
  3. Service 클래스에 @Autowired 애너테이션을 사용하여 Dao와의 의존관계를 주입해준다
  • ApplicationContext
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:c="http://www.springframework.org/schema/c"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
	
	<!-- Root Context: defines shared resources visible to all other web components -->
		
	
	<!-- Annotation boardDAO DI by doorisopen -->
	<bean id="boardDAO" class="org.doorisopen.myspring.Board.Persistence.BoardDAOImpl">
	</bean>
	<bean id="boardService" class="org.doorisopen.myspring.Board.Service.BoardServiceImpl">
	</bean>
	<context:annotation-config></context:annotation-config>
	
</beans>
  • BoardService.class
public class BoardServiceImpl implements BoardService{

	@Autowired
	private BoardDAO dao;
	
	@Override
	public void BoardWrite(BoardVO vo) throws Exception {
		// TODO Auto-generated method stub
		dao.boardWrite(vo);
	}
	@Override
	public BoardVO BoardDetail(String boardTitle) throws Exception {
		// TODO Auto-generated method stub
		return dao.boardDetail(boardTitle);
	}
}
  • Test Case
public class BoardTest {
	
	private static ApplicationContext ac = null;
	public static void main(String[] args) throws Exception {
		System.out.println("Hello DI - XML");
		
		ac = new GenericXmlApplicationContext("Testcontext.xml");
		BoardService service = (BoardService)ac.getBean("boardService");
		
		BoardVO vo = new BoardVO();
		vo.setBoardTitle("두 번째 게시글 테스트");
		
		service.BoardWrite(vo);
		
		vo = service.BoardDetail("두 번째 게시글 테스트");
		System.out.println(vo);
	}
}

2. 설정 방법

  1. ApplicationContext 에 을 등록한다.
  2. Service, DAO 클래스에 @Component 애너테이션 등록을 해준다.
  3. Service 클래스에 @Autowired 애너테이션을 사용하여 Dao와의 의존관계를 주입해준다
  • Test Clas 변경 사항(Method 1 -> Method 2)
public class BoardTest {
	
	private static ApplicationContext ac = null;
	public static void main(String[] args) throws Exception {
		System.out.println("Hello DI - XML");
		
		ac = new GenericXmlApplicationContext("Testcontext.xml");
		/* Method 1. annotation-config
		BoardService service = (BoardService)ac.getBean("boardService"); // by Component name
		*/
		// Method 2. context:component-scan
		BoardService service = (BoardService)ac.getBean(BoardService.class); // by Class name
		
		BoardVO vo = new BoardVO();
		vo.setBoardTitle("두 번째 게시글 테스트");
		
		service.BoardWrite(vo);
		
		vo = service.BoardDetail("두 번째 게시글 테스트");
		System.out.println(vo);
	}
}

JAVA를 이용한 DI 설정


JAVA를 이용한 DI 설정은 실습은 생략


  • XML 문법 대신 자바 코드로 빈을 설정한다
  • 개발 환경에 따라서 설정 방식을 선택한다
    • 타사의 외부 라이브러리를 사용하여 DI하고자 할 경우에는 소스를 가지고 있지 않고 있기 때문에 애노테이션을 이용하는 방법은 불가능 -> XML 로 설정
    • XML 기반 + 애노테이션 기반, 자바 기반 + 애노테이션 기반
  • Java로 DI 설정 코드의 장점은 TypeSafe하고 Refactoring에 매우 적합하다
    • 프로퍼티 명 또는 클래스 명이 틀렸을 경우 컴파일 에러를 낸다
  • 컨테이너 생성 클래스
    • AnnotationConfigApplicationContext
  • JAVA 설정 시 주요 Annotation
    • XML 설정 없이 자바 코드를 이용해서 빈 객체 생성과 빈 객체간의 의존 관계 설정
    • @Configuration
      • 빈 설정 메타 정보를 담고 있는 클래스를 선언 해당 클래스가 스프링 설정으로 사용된다
    • @Bean
      • 클래스 내의 매서드를 정의하여 새로운 빈 객체를 정의할 때 사용 name 속성을 사용하여 새로운 빈 이름 적용 가능

자바 설정과 XML 관계

  • @Bean 애노테이션과 매서드 이름을 이용해서 컨테이너가 사용할 빈 객체를 생성
  • 자바 설정에서는 빈 객체를 직접 생성한다.
  • @Bean 매서드를 불러들여서 객체를 취득
    • 매서드 이름인 memberDAO를 빈의 식별자로 사용한다
  • XML 에서는 property 태그constructor-arg 태그를 이용해서 설정하였으나 자바 설정에서는 직접 의존 객체를 주입해야 한다

빈 객체의 스코프(Scope)와 라이프사이클(Life Cycle)

빈 객체 스코프(Scope)

  • 빈이 생성될 수 있는 범위를 설정하기 위해서는 scope 속성을 사용

  • applicationContext

<bean id="boardDAO" class="org.doorisopen.myspring.Board.Persistence.BoardDAOImpl
scope="singleton">
</bean>
<bean id="memberService" class="org.doorisopen.myspring.Board.Service.BoardServiceImpl">
    <property name = "boardDAO" ref="boardDAO" />
</bean>

빈 객체의 라이프 사이클(LifeCycle)

  • 빈 객체의 라이프사이클은 초기화->이용->종료 3단계로 진행된다.
  • 빈 생성 후 초기화 작업과 빈 종료 전 전처리 과정을 수행할 수 있는 방법을 제공

  • 다음은 빈의 전 처리와 후 처리가 InitializingBean, DisposableBean 인터페이스의 메서드 를 통해 실행되는 형태다.
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
public class MyBeanImpl implements LifeBean, InitializingBean, DisposableBean{
    // InitializingBean 인터페이스 메소드
    @Override
    public void afterPropertiesSet() throws Exception {
        .................................
    }
    // DisposableBean 인터페이스 메소드
    @Override
    public void destroy() throws Exception {
        ...................................
    }
} 
  • 빈 종료 전의 전 처리 작업은 prototype 스코프의 빈에서는 동작하지 않는다

관련된 Post


References

  • 대학교 웹 프레임워크 수업 정리한 내용 입니다.