본문 바로가기
카테고리 없음

Spring IoC/DI 컨테이너와 xml 설정 및 Java Config 이용한 설정

by am6:00 2019. 8. 5.

컨테이너 (Container)

컨테이너는 인스턴스의 생명주기를 관리하며, 생성된 인스턴스에게 추가적인 기능을 제공합니다. 컨테이너는 보통 인스턴스의 생명주기를 관리하며, 생성된 인스턴스들에게 추가적인 기능을 제공하는 것을 말합니다. 예를 들어, Servlet을 실행해주는 WAS는 Servlet 컨테이너를 가지고 있다고 말합니다. WAS는 웹 브라우저로부터 서블릿 URL에 해당하는 요청을 받으면, 서블릿을 메모리에 올린 후 실행합니다. 개발자가 서블릿 클래스를 작성했지만, 실제로 메모리에 올리고 실행하는 것은 WAS가 가지고 있는 Servlet 컨테이너입니다. Servlet컨테이너는 동일한 서블릿에 해당하는 요청을 받으면, 또 메모리에 올리지 않고 기존에 메모리에 올라간 서블릿을 실행하여 그 결과를 웹 브라우저에게 전달합니다.

 

IoC(Inversion of Control)

컨테이너가 코드 대신 오브젝트의 제어권을 갖고 있어 IoC(제어의 역전)이라 합니다.

예를 들어, 서블릿 클래스는 개발자가 만들지만, 그 서블릿의 메소드를 알맞게 호출하는 것은 WAS입니다.

이렇게 개발자가 만든 어떤 클래스나 메소드를 다른 프로그램이 대신 실행해주는 것을 제어의 역전이라고 합니다.

 

DI(Dependency Injection)

DI는 의존성 주입이란 뜻을 가지고 있으며, 클래스 사이의 의존 관계를 빈(Bean) 설정 정보를 바탕으로 컨테이너가 자동으로 연결해주는 것을 말합니다.

 

DI가 적용안된 예

class 엔진 {
		...
}

class 자동차 {
     엔진 v5 = new 엔진();
}

개발자가 코드로 객체 생성하는 경우 DI 가 적용되지 않았다고 봅니다.

자동차 인스턴스 --- [ 인스턴스 생성 후 사용 ]---> 엔진 인스턴스

 

DI가 적용된 예

@Component
class 엔진 {

}

@Component
class 자동차 {
     @Autowired
     엔진 v5;
}

개발자가 직접 객체 생성하는 코드를 작성하는 것이 아닌, IoC컨테이너가 엔진 type의 객체를 생성해두면, 자동차 class에서는 이미 생성된 엔진 type의 v5 객체를 @Autowired 어노테이션을 선언후 사용만 하면 됩니다.

DI 적용 과정 (이미지 출처 - https://www.edwith.org/boostcourse-web/lecture/20656/)

Spring에서 제공하는 IoC/DI 컨테이너

  • BeanFactory : IoC/DI에 대한 기본 기능을 가지고 있습니다.
  • ApplicationContext : BeanFactory의 모든 기능을 포함하며, 일반적으로 BeanFactory보다 추천됩니다. 트랜잭션처리, AOP등에 대한 처리를 할 수 있습니다. BeanPostProcessor, BeanFactoryPostProcessor등을 자동으로 등록하고, 국제화 처리, 어플리케이션 이벤트 등을 처리할 수 습니다.
  • BeanPostProcessor : 컨테이너의 기본로직을 오버라이딩하여 인스턴스화 와 의존성 처리 로직 등을 개발자가 원하는 대로 구현 할 수 있도록 합니다.
  • BeanFactoryPostProcessor : 설정된 메타 데이터를 커스터마이징 할 수 있습니다.

Bean 이란?

일반적인 자바 클래스를 bean이라 합니다. 이러한 빈에는 특징이 3가지가 있습니다. 1. 기본 생성자 코드 2. 필드 모두 private 하게 선언 3. getter, setter 메서드 가지고 있기(객체지향 프로그래밍에서는 객체의 속성에 직접접근하는것은 바람직하지 않다고 보기 때문에 getter, setter, 메서드로 접근해야한다.) 왜 이런 규칙을 가져야 할까요? 생성을 사용자가 직접하는 것이 아닌 누군가가 대신(Spring의 bean 팩토리가해줌) 생성해주니 반드시 규칙이 필요합니다. 또한, 스프링이 아무리 자동으로 해준다 해도 어떤 클래스를 기준으로 Bean을 생성해줘 라는 정보를 알려줘야 합니다. xml 파일이나 java 형식의 파일에 이러한 정보들을 모아놓는것입니다. 


xml 형식의 스프링 설정파일

Engine.java

package kr.or.connect.diexam01;

public class Engine {
	public Engine() {
		System.out.println("Engine 생성자");
	}
	
	public void exec() {
		System.out.println("엔진이 동작합니다.");
	}
}

Car.java

package kr.or.connect.diexam01;

public class Car {
	Engine v8;
	
	public Car() {
		System.out.println("Car 생성자");
	}
	
	public void setEngine(Engine e) {
		this.v8 = e;
	}
	
	public void run() {
		System.out.println("엔진을 이용하여 달립니다.");
		v8.exec();
	}
}

ApplicationContext.xml - xml 설정파일

<bean id="e" class="kr.or.connect.diexam01.Engine"></bean>
<bean id="car" class="kr.or.connect.diexam01.Car">
	<property name="engine" ref="e"></property>
</bean>

이러한 xml 설정은 다음의 과정을 자동으로 처리해 줍니다.

Engine e = new Engine();
Car c = new Car();
c.setEngine( e );

Java 형식의 스프링 설정파일

java Config와 어노테이션을 이용해 스프링에서 사용하는 빈을 정의하고 DI 하는 방법에 대해 알아보겠습니다.

 

Java config 설정을 위한 어노테이션

@Configuration : 스프링 설정 클래스라는 의미를 가지는 어노테이션

@Bean : bean을 정의하는 어노테이션

@ComponentScan : @Controller, @Service, @Repository, @Component 어노테이션이 붙은 클래스를 찾아 컨테이너에 등록

@Component : 컴포넌트 스캔의 대생이 되는 어노테이션 중 하나로써 주로 유틸, 기타 지원 클래스에 붙이는 어노테이션

@Autowired : 주입 대상이 되는 bean을 컨테이너에 찾아 주입하는 어노테이션

 


Java config 파일로 빈 등록하는 방법 1

ApplicationConfig.java

package kr.or.connect.diexam01;
import org.springframework.context.annotation.*;

@Configuration
public class ApplicationConfig {
	@Bean
	public Car car(Engine e) {
		Car c = new Car();
		c.setEngine(e);
		return c;
	}
	
	@Bean
	public Engine engine() {
		return new Engine();
	}
}

JavaConfig로 설정할때 클래스 위에는 @Configuration 이 붙어있어야합니다. ApplicationContext중에서 AnnotationConfigApplicationContext는 JavaConfig클래스를 읽어들여 IoC와 DI를 적용하게 됩니다.

이때 설정파일 중에 @Bean이 붙어 있는 메소드들을 AnnotationConfigApplicationContext는 자동으로 실행하여 그 결과로 리턴하는 객체들을 기본적으로 싱글턴으로 관리를 하게 됩니다.


Java config 파일로 빈 등록하는 방법 2

package kr.or.connect.diexam01;
import org.springframework.context.annotation.*;

@Configuration
@ComponentScan("kr.or.connect.diexam01")
public class ApplicationConfig2 {
}

@Configuration 아래에 @ComponentScan 어노테이션 추가하여  @Controller, @Service, @Repository, @Component 어노테이션이 붙어 있는 클래스를 찾아 메모리에 전부 올려줍니다.


Java config 파일로 빈 등록하는 방법 3

기존의 Car클래스와 Engine클래스 위에 @Component를 붙여 빈등록

Engine.java

package kr.or.connect.diexam01;

import org.springframework.stereotype.Component;

@Component
public class Engine {
	public Engine() {
		System.out.println("Engine 생성자");
	}
	
	public void exec() {
		System.out.println("엔진이 동작합니다.");
	}
}

Car.java

package kr.or.connect.diexam01;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class Car {
	@Autowired
	private Engine v8;
	
	public Car() {
		System.out.println("Car 생성자");
	}
	
	public void run() {
		System.out.println("엔진을 이용하여 달립니다.");
		v8.exec();
	}
}

ApplicationContextExam.java

package kr.or.connect.diexam01;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class ApplicationContextExam04 {

	public static void main(String[] args) {
		ApplicationContext ac = new AnnotationConfigApplicationContext(ApplicationConfig2.class);
		   
		Car car = ac.getBean(Car.class);
		car.run();
		
	}
}

@Controller, @Service, @Repository, @Component 어노테이션이 붙어 있는 객체들은 ComponentScan을 이용해서 읽어들여 메모리에 올리고 DI를 주입하도록 하고, 이러한 어노테이션이 붙어 있지 않은 객체는 @Bean어노테이션을 이용하여 직접 생성해주는 방식으로 클래스들을 관리하면 편리합니다.

 


다루는 빈(Bean)이 많아질수록 xml로 설정하는 것보다 @ComponentScan, @Component, @Autowired를 이용하는 것(Java Config 파일 설정) 중 어떤 것이 유지보수에 좋습니다. xml 설정은 거의 안쓰고 어노테이션을 이용해 자바파일에 한다.