Programming/Java

[Java Spring] IoC, DI (의존성 주입 방법 xml, annotation)

erinh 2022. 10. 30. 03:39
반응형

IoC(Inversion of Control), 제어의 역전

: 객체 생성을 프레임워크가 유연하게 제어함으로써 객체간 결합도를 줄여, 유연성 증가, 코드 중복 제어, 유지 보수의 편리성 증가

- 스프링이 모든 의존성 객체를 스프링이 실행될 때 다 만들어주고 필요한 곳에 주입
- Bean들은 싱글턴 패턴의 특징을 가지며, 제어의 흐름을 사용자가 컨트롤하지 않고 스프링에게 맡겨 작업 처리

[ 일반적인 객체 제어 과정 ]
1. 객체 생성
2. new 키워드를 통해 의존성 객체 생성(클래스 내부에서 생성)
3. 의존성 객체의 메소드 호출
[ 스프링의 객체 제어 과정 ]
1. 객체 생성
2. 의존성 객체 주입 (스프링이 만들어놓은 객체를 주입)
3. 의존성 객체의 메소드 호출

DI (Dependency Injection), 의존성 주입

: 외부에서 두 객체간의 관계를 결정해줌으로써, 클래스 레벨에서 의존관계가 고정되지 않고 런타임시 관계를 동적으로 주입 → 유연성 확보 및 결합도 감소

[ 의존성 주입을 하지 않았을 경우 ]

컴퓨터를 1대 판매하는 Store 클래스가 있다고 할 때, 보통의 객체 생성은 아래와 같은 방법으로 가능

// 강한 결합
// Store클래스는 Computer 클래스에 의존함

/* 문제점
* 1. 강한 결합으로 인해 Computer가 아닌 다른 상품을 판매하고 싶다면, 생성자 변경이 필요 -> 유연성 감소, 유지보수 어려움
* 2. 객체 간 관계가 아닌 클래스 간 관계가 맺어짐으로써 객체 지향 프로그래밍에서 멀어짐
*/

public class Store {

  private Computer computer; // 
  
  public Store() {
    this.computer = new Computer();
  }
}

[ 의존성 주입을 할 경우 ]

다형성의 원리에 의해, Computer 등의 상품을 하나로 묶어줄 Interface 생성하여 Store가 인터페이스를 통해 실제 객체를 주입받도록 함
- Spring에서는 애플리케이션 실행 시점에 필요한 객체(빈)을 생성하여 의존성이 있는 두 객체를 연결하기 위해 한 객체를 다른 객체로 주입시켜주는 역할을 해 줌
※ Spring IoC 컨테이너가 관리하는 자바 객체를 빈(Bean)이라고 하며, ApplicationContext에 해당 객체가 만들어져야 의존성 주입을 할 수 있음

// 상품들을 묶어줄 Product 인터페이스 생성
public interface Product {

}

// Product 인터페이스를 implement한 클래스
public class Computer implements Product {

}

public class Labtop implements Product {

}

public class Store {
  
  private Product product;
 
  // 어떤 물건을 팔지 외부에서 상품을 주입 받아야 함
  public Store(Product product) {
    this.product = product;
  }
}

Spring IoC Container

: Bean의 객체화 조립, 관리, 사용 소멸에 대한 처리 담당
- Bean Factory: 프레임워크 설정과 기본 기능을 제공하는 컨테이너, 모든 유형의 객체 관리 가능한 메커니즘 제공
- ApplicationContext: BeanFactory 하위 인터페이스로 이벤트 처리, 국제화용 메시지처리, AOP 통합 기능 제공
- WebApplicationContext: 웹 환경에서 Spring을 사용하기 위한 기능 추가. 대표적 구현 클래스로 XmlWebApplicationContext가 있음

의존성 주입 방법

1. Bean 생성 및 설정

1-1. xml 방식

: xml 파일에 모든 bean 객체를 등록하고, 의존관계를 설정해주는 방법

 

- id: 스프링 컨테이너에서 사용할 bean 고유의 식별자
- class: 객체화할 객체의 클래스 전체 경로 (정규화된 자바 클래스 이름)
- scope: 객체의 범위 (기본 singleton이며 prototype 설정시에는 객체 주입시마다 새로운 객체를 생성함)

- constructor-arg: 생성時 생성자에 전달할 인수

- property: 생성時 bean setter에 전달할 인수

// 기존 자바코드 방식의 의존성 생성 과정

// 0. Exam 객체 생성
Exam exam = new MidtermExam();

// 1. 생성자를 통해 결합
ExamConsole console = new InlineExamConsole(exam);

// 2. setter를 통해 결합
ExamConsole console = new InlineExamConsole();
console.setExam(exam);

// 스프링 프레임워크를 통한 의존성 주입 과정
// xml파일에 bean객체들 생성 후 주입.

// 0. Exam 빈 객체 생성
<bean id="exam" class="spring.di.entity.MidtermExam" />

// 1. 생성자를 통해 의존성 주입
<bean id="console" class="spring.di.ui.InlineExamConsole">
  <constructor-arg ref="exam" />
</bean>

// 2. setter를 통해 의존성 주입
<bean id="console" class="spring.di.ui.InlineExamConsole">
  // name 뒤는 setExam을 의미, ref 뒤는 의존성 주입 대상 빈 객체의 id를 의미
  <property name="exam" ref="exam" />
</bean>

1-2. Annotation 방식

: xml 파일에는 Bean등록을 위한 대상 패키지만 설정하고 객체를 생성할 대상 클래스에 어노테이션 표시를 하는 방법

 

- @Component: 스프링에서 관리되는 객체임을 표시하기 위해 사용되는 가장 기본적 annotation
※ Component의 기능을 좀 더 명확하게 하기 위해 명시적으로 아래와 같이 분리 사용함 (aspect에 연관성 부여)
- @Controller: 클라이언트로부터 요청이 왔을 때 지정된 뷰에 모델 객체를 넘겨주는 역할
                         즉, 요청에 따라 어떤 처리를 할지 결정하여 service에 넘겨주고 service가 처리한 내용을 view에 넘김

- @Service: 비즈니스 로직이나 repository layer를 호출하는 함수에 사용. 비즈니스 로직을 수행 결과를 Controller에 넘김

- @Repository: 외부 DB에 접근하는 코드(DAO)

- @Autowired: 의존성을 주입할 대상 위에 작성(xml에서는 constructor-arg, property 태그로 명시

- @Qualifier: 주입 대상이 두 개 이상이거나 빈 객체일 경우, bean을 지정하여 식별 가능하게끔 함

 

- 어노테이션 방식의 경우, 생성되는 bean 객체의 이름은 클래스 첫 글자를 소문자로 바꾼 것
  임의 지정하고 싶다면 @Component(value="bean-name")형태로 지정 가능

// xml파일에 Annotation으로 Bean을 등록할 대상 패키지 설정
<context:component-scan base-package="spring.di.*" />
// 각 클래스에 어노테이션을 붙여 Bean객체로 만듦
@Component
public class MidtermExam implements Exam {
...
}

@Service
public class InlineExamConsole implements ExamConsole {

  @Autowired // 의존성 주입할 대상에 작성
  private Exam exam;
  
  ...
}

2. 스프링 컨테이너 객체 생성

- main함수에서 스프링 컨테이너 생성 후, 빈 객체 가져온 후 사용 가능

public class Test {
  public static void main(String[] args) {
    Application context context = new GenericXmlApplicationContext("spring/di/applicationContext.xml"); // xml설정파일이 있는 주소
    ExamConsole console = context.getBean(ExamConsole.class);
    console.print();
  }
}

 

반응형