1. 스프링 빈

스프링 빈(Spring Bean)이란?

Spring에서는 new를 사용해서 객체를 생성하는 것이 아니라, Spring Container에 의해 관리되는 자바 객체를 사용한다. 이렇게 Spring Container에 의해 생성되고 관리되는 자바 객체를 빈(Bean)이라고 한다.

스프링 빈을 등록하는 2가지 방법

• 컴포넌트 스캔과 자동 의존관계 설정
• 자바 코드로 직접 스프링 빈 등록하기


2. 컴포넌트 스캔과 자동 의존관계 설정

회원 컨트롤러에 의존관계 추가

package hello.hellospring.controller;

import hello.hellospring.service.MemberService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigurationPackage;
import org.springframework.stereotype.Controller;

@Controller // 컨트롤러 애너테이션이 있으면, 스프링 컨테이너에 스프링빈으로MemberController가 등록된다.
public class MemberController {
    // private final MemberService memberService = new MemberService();
    // new로 객체를 생성할 경우, 스프링 컨테이너에 있는 다른 컨트롤러들에서 MemberService를 다 가져다 쓸 수 있게 된다.
    // MemberService는 그닥 여러 기능도 없기 때문에 차라리 스프링 컨테이너에 등록을 하고 쓰는게 낫다

    private final MemberService memberService;

    @Autowired // 스프링컨테이너에서 멤버서비스를 가져오는 애너테이션이다.
    // 이 애너테이션으로 멤버컨트롤러가 생성이될 때, 스프링 빈에 등록된 멤버서비스 객체를 멤버컨트롤러에 넣어준다.
    // 이것이 DI이다.
    public MemberController(MemberService memberService) {
        this.memberService = memberService;
    }
}

• 생성자에 @Autowired 애너테이션이 있으면 스프링이 연관되어 있는 객체를 스프링 컨테이너에서 찾아서 넣어준다. 이렇게 객체 의존관계를 외부에서 넣어주는 것을 DI (Dependency Injection), 의존성 주입이라고 한다.
• 이전 회원 서비스 테스트에서는 개발자가 직접 주입한 경우라면, 여기서는 @Autowired를 통해 스프링이 주입해주는 것이다.


오류발생
위 상태에서 서비스 애너테이션없이 그냥 메인 메서드를 실행해보면 다음과 같이 MemberService를 찾을 수 없다고 오류가 뜬다.

이는 MemberService 클래스는 단순히 자바 클래스이고, 스프링 컨테이너에 스프링 빈으로 등록되어 있지 않기 때문이다. 그러므로 MemberService클래스 위에 @Service 애너테이션을 넣어서 스프링 컨테이너에 스프링 빈으로 등록해야 한다.
마찬가지로 리포지토리에서도 @Repository 애너테이션을 넣어줘야 한다.

컴포넌트 스캔의 원리

  • @Component 애너테이션이 있으면 스프링 빈으로 자동 등록된다.
  • @Controller 멤버 컨트롤러가 스프링 빈으로 자동 등록된 것도 컴포넌트 스캔 때문이다.
  • @Component 를 포함하는 다음 애너테이션도 스프링 빈으로 자동 등록된다.
    • @Controller
    • @Service
    • @Repository

회원 서비스 스프링 빈 등록

@Service
public class MemberService {
 private final MemberRepository memberRepository;
 @Autowired 
 // 스프링이 멤버서비스를 생성할 때, 생성자를 하고, 
 // 이 때 Autowired가 있으면 메모리멤버리포지토리를 서비스에 객체로 넣어준다.
 public MemberService(MemberRepository memberRepository) {
 this.memberRepository = memberRepository;
 }
}

참고: 생성자에 @Autowired 를 사용하면, 객체 생성 시점에 스프링 컨테이너에서 해당 스프링 빈을 찾아서 주입한다. 생성자가 1개만 있을 경우에는 @Autowired를 생략할 수 있다.

회원 리포지토리 스프링 빈 등록

@Repository
public class MemoryMemberRepository implements MemberRepository {}

스프링 빈 등록 이미지

참고: 스프링은 스프링 컨테이너에 스프링 빈을 등록할 때, 기본적으로 싱글톤으로 등록된다(유일하게 하나만 등록해서 공유). 그러므로 같은 스프링 빈이면 모두 같은 인스턴스다. 싱글톤이 아니게 설정할 수는 있으나, 특별한 경우를 제외하고는 대부분 싱글톤을 사용한다.


3. 자바 코드로 직접 스프링 빈 등록하기

  • 회원 서비스와 회원 리포지토리의 @Service, @Repository, @Autowired 애너테이션을 제거하고 진행한다. 다만, 회원 컨트롤러에 있는 애너테이션은 그대로 유지한다.

Config파일 생성

src/main/java/hello.hellospirng/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 // 이렇게 하면 스프링이 뜰 때, Configuration을 읽고 스프링 빈에 등록하라고 인식한다
public class SpringConfig {

    @Bean // 스프링 빈에 등록할거야~
    public MemberService memberService(){
        return new MemberService(memberRepository());
        // 멤버서비스 생성자에서 넣어주는 값 그대로 넣기
        // 바로 아래 스프링 빈에 등록된 memberRepository() 메소드를 넣어주는 것이다.
    }

    @Bean
    public MemberRepository memberRepository(){ // MemnerRepository는 인터페이스, MemoryMemberRepository()는 구현체
        return new MemoryMemberRepository();
    }
}

컴포넌트 스캔을 사용하다가 구현체를 변경한다거나 하는 변동 사항이 생기면, 여러 코드를 수정해야 한다.
그러나 @Configuration을 사용할 경우, 다른 것을 수정할 필요없이 이 파일만 수정하면 된다는 것이 장점이다.

참고: XML로 설정하는 방식도 있지만 최근에는 거의 사용하지 않으므로 생략한다.

참고: DI에는 필드 주입, setter 주입, 생성자 주입 이렇게 3가지 방법이 있다. 의존관계가 실행중에 동적으로 변하는 경우는 거의 없으므로 생성자 주입을 권장한다. 동적으로 변하는 경우란, 서버 가동 중에 변경사항을 수정하는 것을 말한다.

// 필드 주입
    @Autowired private MemberService memberService;
// 이 방식은 중간에 수정할 수가 없기 때문에 권장되지 않는 방법이다.
// setter 주입
    @Autowired
    public void setMemberService(MemberService memberService){
        this.memberService = memberService;
    }
// setter의 단점은, MemberController가 호출 될 경우에 
// public으로 열려있어야 가능하기 때문에 public으로 다 노출된다는 것이다.
// 생성자 주입
	@Autowired
	public MemberController(MemberService memberService) {
    this.memberService = memberService;
    }
// MemberController(MemberService memberService) 생성자를 통해서  
// memberService가 위의 MemberController클래스에 주입이 된 경우,
// 생성하는 시점에 한번 설정해놓고 이후 변경을 못하도록 할 수 있다.

참고: 실무에서는 주로 정형화된 컨트롤러, 서비스, 리포지토리 같은 코드는 컴포넌트 스캔을 사용한다.
정형화 되지 않거나, 상황에 따라 구현 클래스를 변경해야 하면 설정을 통해 스프링 빈으로
등록한다.

주의: @Autowired 를 통한 DI(Dependency Injection)는 helloController , memberService 등과 같이 스프링이 관리하는 객체에서만 동작한다. 스프링 빈으로 등록하지 않고 내가 직접 생성한 객체에서는 동작하지 않는다.

후속 강의에서 스프링 컨테이너와 의존성 주입에 관련해 더 Deep한 내용을 다룰 예정이다.

profile
주니어개발자 시켜줘요

0개의 댓글