지난 블로그에서 작성했던 예제를 가지고 스프링 빈과 의존관계에 대해서 알아보려고 한다. 컨트롤러 패키지 안에 MemberController 클래스를 생성해주었다. MVC 중에 C를 가지고 요청을 받고 어떤 동작을 해줄지를 결정하기 위해서이다.
package hello.hellospring.controller;
import hello.hellospring.service.MemberService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller
public class MemberController {
private final MemberService memberService;
@Autowired //이걸 사용하면 스프링 컨테이너에 있는 memberService와 딱 연결시켜준다! (Dependency Injection : 의존 관계 주입 임)
public MemberController(MemberService memberService) {
this.memberService = memberService;
}
// service 와 repository도 각각 @ annotation을 통해서 컨테이너에 등록해줘야 함!
}
컨트롤러를 작성할 때는 @Controller를 항상 먼저 써줬다. 이러한 표시는 스프링이 실행될 때 확인하고 이건 컨트롤러구나 알 수 있도록 해주는 역할을 한다. 요청이 들어올 때 스프링 컨테이너에서 컨트롤러를 들고 있는데 이를 스프링 컨테이너에서 스프링 빈이 관리된다고 표현한다.
그리고 나선 컨트롤러 안에서 사용될 MemberService를 호출해주려고 한다. 이 때 memberService = new MemberService(); 해도 되지만, 우리가 호출하려는 서비스는 전체 프로젝트에서 한 개만 있으면 된다. 즉, memberController에서 호출한 서비스를 주문컨트롤러에서 사용해도 된다. 따라서 동일한 객체를 하나만 생성해주려고 한다. 이를 위해서 스프링 컨테이너 안에서 memberService 하나를 생성하고 사용되는 모든 memberService 에 컨테이너의 그것을 호출해주면 편하다. 이 때 생성자를 이용해서 생성해주면서 @Autowired annotation을 추가해주면 스프링에서 알아서 컨테이너 내의 memberService를 연결해준다. 즉, 이 annotation을 사용하면 DI(Dependency Injection : 의존성 주입)가 된다는 것이다.
@Autowired 를 추가해주고 나면, 빨간 줄이 그어지면서 제대로 실행이 안 된다. 의존관계의 다른 컴포넌트(여기서는 MemberService)도 컨테이너에 등록되어야 컨테이너 내에서 찾아서 연결을 해주는데, 그 과정이 안 되어있기 때문이다. controller을 이용해서 외부 요청을 받고 service에서 비즈니스 로직을 만들고 repository에서 데이터를 저장하는 게 정형화된 패턴이다. 따라서 각 컴포넌트가 무엇인지 스프링에게 알려줘야 할 필요가 있다.
MemoryMemberRepository 클래스 위에 @Repository를 추가해준다.
@Repository
public class MemoryMemberRepository implements MemberRepository{
...
}
MemberService 클래스 위에도 @Service를 추가해주고, 서비스의 생성자 함수 위에 @Autowired를 이용해준다. 생성자에 @Autowired 를 사용하면 객체 생성 시점에 스프링 컨테이너에서 해당 스프링 빈을 찾아서 주입한다. 생성자가 1개만 있으면 @Autowired 는 생략할 수 있다.
@Service
public class MemberService {
private final MemberRepository memberRepository;
@Autowired
public MemberService(MemberRepository memberRepository){
this.memberRepository = memberRepository;
}
...
}
이렇게 서비스에도 Autowired를 이용해줘야 스프링에서 인식한 repository를 컨테이너에서 생성한 후에 service에 연결해준다.
이렇게 스프링 빈을 등록하는 것에는 두 가지 방법이 있다.
첫 번째가 방금 해본 컴포넌트 스캔과 자동 의존 관계 설정 방법이다. 이 외에도 자바 코드로 직접 스프링 빈 등록하는 방법이 있다.
컴포넌트 스캔 방식이 바로 애노테이션을 이용하는 것이다. @Service가 구성된 방식을 자세히 살펴보면 다음과 같다.
Service는 Component임을 확인할 수 있다.
이제 우리의 스프링 컨테이너는 다음과 같은 스프링 빈을 가지고 있다.
참고로 스프링 컨테이너에 스프링 빈이 등록될 때 기본적으로 싱글톤으로 등록된다. 즉, 전체 프로젝트 내에서 해당 객체는 하나의 인스턴스만을 가진다는 뜻이다. 따라서 같은 스프링 빈이면 모두 같은 인스턴스이다. 설정을 통해서 싱글톤이 아니도록 할 수 있지만, 보통은 그냥 싱글톤으로 등록해서 사용한다.
추가적으로 컴포넌트는 hello.hellospring 패키지 하위의 클래스에서만 스캔 가능하다. 이건 HelloSpringApplication에서 package 설정된 디렉토리와 동일하고, 이것보다 상위 디렉토리에서 컴포넌트 선언한 건 읽지 못한다!
자바 코드로 직접 스프링 빈을 등록하는 방법
다음은 자바 코드로 직접 스프링 빈을 등록하는 방법이다.
앞선 코드에서 작성한 annotation ( @Service, @Repository, @Autowired )를 먼저 삭제해 준다. Controller와 컨트롤러 내부의 @Autowired는 삭제하지 않는다. 그리고 hello.hellospring 디렉토리 안에 SpringConfig라는 클래스를 하나 생성해준다.
이 SpringConfig 클래스 내부에서 직접 스프링빈을 등록해주면 된다.
package hello.hellospring;
import hello.hellospring.repository.MemberRepository;
import hello.hellospring.repository.MemoryMemberRepository;
import hello.hellospring.service.MemberService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SpringConfig {
@Bean
public MemberService memberService(){
return new MemberService(memberRepository());
}
@Bean
public MemberRepository memberRepository(){
return new MemoryMemberRepository();
}
}
컴포넌트 스캔 방식과 직접 등록 방식
실무에서는 정형화된 컨트롤러, 서비스, 리포지토리 같은 코드는 컴포넌트 스캔을 사용한다. 그리고 정형화 되지 않거나 상황에 따라 구현 클래스를 변경해야 한다면 설정을 통해 스프링 빈으로 등록한다. 우리 예제 같은 경우는 DB가 설정되지 않아서 우선 메모리에 구현체로 등록해놓고 추후에 수정하기로 했다. 따라서 @Bean을 이용해서 등록하고 나중에 return new MemoryMemberRepository() 만 new DBRepository() 로 수정해주면 코드 한 줄만으로 쉽게 설정 변경이 가능하다.
DI에는 필드 주입, setter 주입, 생성자 주입 이렇게 세 가지 방식이 있다고 한다. 생성자 주입이 우리가 사용한 방식인데, 필드 주입이나 setter 주입은 의존관계가 실행 중에 변할 수 있다. 하지만 실행 중(스프링이 run하고 있는 중)에 변해야 하는 경우는 거의 존재하지 않기 때문에 대부분 생성자 주입 방식으로 처리한다.
※ @Autowired 를 통한 DI는 helloConroller , memberService 등과 같이 스프링이 관리하는 객체에서만 동작한다. 스프링 빈으로 등록하지 않고 내가 직접 생성한 객체에서는 동작하지 않는다.
'TIL > Java | Spring Boot' 카테고리의 다른 글
[Spring Boot] 스프링 DB 접근 기술 (0) | 2022.01.14 |
---|---|
[Spring Boot] 회원 관리 예제 - 웹 MVC 개발 (0) | 2022.01.11 |
[Spring Boot] 간단한 예제 - 회원 관리 예제 2 (0) | 2022.01.07 |
[Spring Boot] 간단한 예제 - 회원 관리 예제 1 (0) | 2022.01.07 |
[Spring Boot] 서버에서 스프링 파일 실행시키기 (0) | 2022.01.03 |
댓글