- DI 개념
- 스프링 컨테이너의 이해와 사용
- 스프링 DI 개념을 이해하고 사례를 통해 확인합니다.
- 스프링 컨테이너의 역할과 동작방식을 이해합니다.
- DI란?
[ 각 클래스간의 의존관계를 빈 설정(Bean Definition) 정보를 바탕으로 컨테이너가 자동으로 연결해주는 것을 말함 ]
※ 개발자들은 단지 빈 설정파일에서 의존관계가 필요하다는 정보를 추가하면 됨
※ 객체 레퍼런스를 컨테이너로부터 주입 받아서, 실행 시에 동적으로 의존관계가 생성됨
※ 컨테이너가 흐름의 주체가 되어 애플리케이션 코드에 의존관계를 주입해주는 것- DI 장점
+ 코드가 단순해진다
- 컴포넌트 간의 결합도가 제거된다.
일반적으로 한 객체 다른 객체를 멤버변수로 사용하거나, 메소드 내에서 다른 객체를 생성하는 경우가 많습니다. 이렇게 객체들 간의 의존성을 알아서 관리하는 기능이 DI라고 합니다.
기존에선 new 연산자를 통해 객체를 생성했다면, DI가 자동으로 생성해서 주입시켜주겠다는 것입니다.
덕분에 소스코드가 간결해지고, 성능도 좋아집니다.
개발자는 각 클래스간의 의존관계를 설정 파일에 한 번 선언만 해주면 됩니다.
- Setter Injection - setter 메서드를 이용한 의존성 삽입
- 의존성을 입력받는 setter 메서드를 만들고 이를 통해 의존성을 주입한다.
- Constructor Injection - 생성자를 이용한 의존성 삽입
- 필요한 의존성을 포함하는 클래스의 생성자를 만들고 이를 통해 의존성을 주입한다.
- Method Injection - 일반 메서드를 이용한 의존성 삽입
- 의존성을 입력 받는 일반 메서드를 만들고 이를 통해 의존성을 주입한다.
DI의 유형은 3가지입니다. 실제로 Setter와 Constructor가 가장 많이 사용되고 그 중에서도 Setter가 제일 자주 사용됩니다.
Spring DI 컨테이너가 관리하는 객체를 빈(Bean)이라고 하고, 이 빈(Bean)들을 관리한다는 의미로, 컨테이너를 빈 팩토리(Bean Factory)라고 한다.
※ 객체의 생성과 객체 사이의 런타임(run-time) 관계를 DI 관점에서 볼 때는 컨테이너를 Bean Factory라고 한다.
※ Bean Factory에 여러 가지 컨테이너 기능을 추가하여 애플리케이션 컨텍스트(Application Context)라고 부른다.
Spring DI 컨테이너 = Spring IoC 컨테이너 = Spring 컨테이너 다 같은 말입니다.
이 Spring 컨테이너를 생성할 때는 몇 개의 클래스가 관련을 합니다.
Bean Factory 인터페이스의 자식 클래스인 Application Context 클래스가 Spring 컨테이너를 생성합니다.
- Bean Factory
- Bean을 등록, 생성, 조회, 반환 관리함
- 보통은 Bean Factory를 바로 사용하지 않고, 이를 확장한 Application Context를 사용함
- getBean() 메서드가 정의되어 있음
- Application Context
- Bean을 등록. 생성, 조회, 반환 관리하는 기능은 Bean Factory와 같음
- Spring의 각종 부가 서비스를 추가로 제공함
- Spring이 제공하는 Application Context 구현 클래스가 여러 가지 종류가 있음
Bean Factory 클래스에 실질적으로 중요한 메소드들이 정의되어 있습니다.
또한 DL를 지원하는 getBean() 메소드도 Bean Factory 클래스에 정의되어 있습니다.
Application Context는 Bean Factory 클래스를 상속하고 몇몇 메소드가 더 추가된 클래스입니다.
실질적으로는 Bean Factory 클래스에 웬만한 주요 메소드가 정의되어 있지만 우리는 직접 Bean Factory 클래스를 사용하지는 않습니다.
Bean Factory에 대한 전체적인 구조입니다.
두 개의 클래스를 만들고 한 클래스에서 다른 클래스를 참조해보는 실습을 할 겁니다.
수동으로 먼저 해보고, DI 방식으로 해보면서 비교도 해보겠습니다.
지난 포스팅 프로젝트에 이어서 Service 클래스를 하나 생성하겠습니다.
↑ 현재 패키지인 hello 패키지 안에 service라는 패키지를 새로 하나 만들기 위해 Package명을 바꿉니다.
↑ 클래스 이름 입력하시고 Finish 클릭
↑ 새로 만든 클래스에 클래스 참조 변수와 참조할 클래스의 메소드를 호출하는 메소드를 정의합니다.
↑ 클래스 import 하시는 거 잊지 마시구요! 잊어버려도 뭐 알려주긴 합니다.
↑ main()을 수정합니다.
↑ 프로그램 실행 결과입니다.
이 방법이 우리가 Spring을 몰랐을 때 사용했던 방법입니다.
이제 DI를 활용해서 클래스를 참조해보겠습니다.
어디 얼마나 간결해졌는지 보겠어
↑ 먼저 HelloService 클래스를 다시 수정했습니다.
↑ setter를 생성했는데, setter 이름 지을 때 일반적으로 'set클래스명' 이렇게 짓습니다. 파라미터로 HelloDAO형의 객체가 들어갑니다.
↑ 그리고 파라미터 객체를 private 참조 변수에 주입해줍니다.
↑ setter를 생성했으니 이를 설정 파일에 등록해줍니다.
↑ 우리는 HelloService 클래스에서 setter를 만들었으니 helloService의 bean 태그 내에 추가합니다. property 태그를 만들고 name엔 참조하는 클래스 bean id 명을 넣어줍니다.
property 태그 = "나 setter 있으니까 setter로 주입 좀 해주라"
ref에는 실제 주입시켜줄 객체를 써줍니다. 이 역시도 bean에 등록된 이름으로 사용하셔야 합니다.
이렇게 입력하시면 끝입니다. 실제 JAVA 코드엔 참조를 위한 클래스 생성(new) 코드가 없습니다. 하지만 코드는 잘 돌아갑니다. 우리가 방금 설정 파일에 선언했기 때문에 알아서 참조할 객체를 생성해주기 때문입니다.
↑ main()는 건들지 않고 그대로 실행하면 정상적으로 결과가 나옵니다.
setter를 지우고 생성자를 이용해서 객체를 주입해봅시다.
↑ HelloService의 생성자를 만듭니다.
↑ 설정 파일을 다시 들어가보면 빨간 줄이 막 떠있죠? 이건 우리가 setter를 지워서 그렇습니다. 위에서 설명했듯이 property 태그는 setter를 위한 태그인데, setter가 없어서 에러를 알려준겁니다.
↑ 생성자 주입 방식에는 constructor-arg 태그가 필요합니다.
ref값은 참조할 클래스 Bean id명을 넣어줍니다.
↑ 실행 결과입니다.
개인적으로는 생성자 주입이 더 편한 것 같습니다.
이번에는 HelloService 클래스에서 2개의 HelloDAO 객체를 참조하는 실습을 해봅니다.
먼저 HelloDAO 클래스의 경로를 바꾸겠습니다.
↑ Hello패키지 우클릭 -> new -> package 클릭
↑ Name에 ".dao" 추가 -> finish 클릭
↑ HelloDAO 클래스를 새로 만든 패키지에 드래그하여 옮깁니다.
↑ 새로 만든 패키지에 클래스를 하나 생성합니다.
↑ 제곱 메소드를 하나 생성합니다.
↑ 설정 파일에 AnotherDAO Bean을 등록합니다.
↑ HelloService 클래스를 수정합니다. 두 개의 클래스 객체를 참조할 것이므로 각각 setter를 만들어줍니다. 그리고 메소드도 수정합니다. 넘겨 받은 두 값을 더한 후, 제곱한 값을 return한다는 내용입니다.
↑ 설정파일을 수정합니다. setter 주입 방식이기 때문에 property 태그를 사용합니다.
↑ 실행결과입니다.