본문 바로가기

springboot

[스프링 입문] 3. 스프링 빈과 의존관계

  • Component Scan과 자동 의존관계 설정
  • 직접 Spring Bean 등록

 

이번에는 구현한 서비스가 화면상에서 동작하기 위해서 Controller와 연결되도록 한다. 

Controller가 Service를 통해서 회원을 등록하고 조회할 수 있도록 하는 것이 의존관계가 있다고 한다. 

 

Component Scan과 자동 의존관계 설정

가장 먼저 Controller는 @Controller 어노테이션을 붙이고 있다면 스프링이 실행될 때 가장 먼저 스프링 컨테이너에 Controller 객체를 생성해서 넣어둔다. 

일단 MemberController를 controller 패키지 밑에 새로 만든다.

@Controller
public class MemberController {
    private final MemberService memberService = new MemberService();

}

MemberController는 MemberService를 이용해서 동작하기 때문에 MemberService를 갖고있도록 한다.

 

하지만 MemberService는 객체를 새로 생성해서 여러개 존재할 필요가 없다.

 

지금은 기능이 회원 등록, 조회밖에 없어서 Controller가 하나만 필요하지만, 나중에 기능을 추가해서 주문 등 다른 기능이 생긴다면 거기에서도 MemberService를 사용해야할 수도 있다. 

만약 그 때도 각 Controller가 그마다 다른 MemberService 객체를 들고 있어야할까? 아니다. 

MemberService 객체를 스프링 컨테이너에 등록하여 하나만 생성해서 전체 서비스에서 돌려쓸 수 있도록 해야한다. 

 

@Controller
public class MemberController {
    private final MemberService memberService;

    @Autowired
    public MemberController(MemberService memberService) {
        this.memberService = memberService;
    }
}

생성자에 @Autowired 어노테이션을 달고 MemberService를 받게한다면 스프링 컨테이너에서 MemberService를 가져다준다. 

하지만 이렇게만 작성하면 빨간줄이 뜬다. 스프링 컨테이너에 아직 MemberService를 등록하도록 하지 않았기 때문이다.

실제로 실행해보면 스프링 컨테이너에서 MemberService를 찾을 수 없다고 뜬다. 

 

이 때 MemberService 에 @Service 어노테이션을 달아준다.

스프링은 @Component 어노테이션을 달고 있는 객체에 대해서 자동으로 스프링 빈으로 등록해준다.

@Service, @Repository, @Controller@Component를 포함하기 때문에 MemberService에 @Service 어노테이션을 달아주면 스프링 빈으로 등록할 수 있다. 

 

@Service
public class MemberService {

    private final MemoryMemberRepository memberRepository;

    @Autowired
    public MemberService(MemoryMemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }
    ...
}

생성자와 클래스에 어노테이션을 새로 달면 memberRepository에 빨간줄이 뜬다. 

MemberRepository의 구현체인 MemoryMemberRepository 에도 @Repository 어노테이션을 달아주면 된다. 

 

결과적으로 현재 @Component 어노테이션을 달아 스프링 빈으로 자동 등록된 객체는 MemberController, MemberService, MemoryMemberRepository이며, 생성자의 @Autowired 어노테이션으로 연결되었다. 

 

참고로 컴포넌트 스캔은 @SpringBootApplication 어노테이션이 붙은 Main class와 같은 패키지를 가장 상위 패키지로 하여 그 하위 패키지에만 적용된다. 

 


 

직접 Spring Bean 등록

정형화된 Controller, Service, Repository는 어노테이션을 사용해 컴포넌트 스캔을 사용하지만,

정형화되지 않거나, 상황에 따라 구현 클래스를 변경해야 하면 설정을 통해 스프링 빈으로 직접 등록한다.

 

현재처럼 데이터베이스를 정하지 않았고, 메모리 저장이 아니라 데이터베이스를 이후에 추가해줘야하기 때문에 Repository 구현 클래스를 변경해야한다. 따라서 직접 스프링 빈으로 등록해줘야 한다. 

 

@Autowired를 통한 의존성 주입은 직접 생성한 객체에서는 동작하지 않는다.

 

모든 @Component, @Autowired 어노테이션을 제거하고, MemberController의 생성자에 붙은 @Autowired 어노테이션만 남긴다. 

Controller는 컴포넌트 스캔으로 컨테이너에 올라가고, service와 연결하기 위해서 남겨준다. 

 

스프링 빈으로 직접 등록하기 위해서 SpringConfig라는 설정 파일 만든다.

@Configuration
public class SpringConfig {

    @Bean
    public MemberService memberService() {
        return new MemberService(memberRepository());
    }

    @Bean
    public MemberRepository memberRepository() {
        return new MemoryMemberRepository();
    }
}

 

@Bean 어노테이션을 사용해서 만든다. 

데이터베이스의 변경이 일어나서 MemberRepository 구현체 클래스를 바꿔야한다면 return new MemoryMemberRepository(); 한 줄만 바꿔주면된다. 

 

 

참고로 의존성 주입은 필드, setter, 생성자 주입 3가지가 있다. 

 

1. 필드 주입

@Autowired private MemberService memberService;

2. setter 주입

@Autowired
public void setMemberService(MemberService memberService) {
    this.memberService = memberService;
}

사실상 서비스 중간에 바뀔 이유가 없기 때문에 setter 주입은 잘 사용하지 않는다고 한다. 

(게다가 setter는 public으로 열려있어야 하기도 하고)

3. 생성자 주입

@Autowired
public MemberController(MemberService memberService) {
    this.memberService = memberService;
}