..

[Spring Basic] 4. Spring Bean and Dependency Injection

Table of contents

  • Spring Bean and Dependency Injection
    • Annotation
      • @Controller
      • @Autowired
    • How to register Spring Bean
      • Component Scan and Auto Dependency Injection
      • Component Scan의 대상(Range)
  • Spring Bean Registration with Java code
    • 의존성 주입 (Dependency Injection, DI)
      • 생성자 주입 (Factory Pattern)
      • 필드 주입
      • Setter 주입
    • 상황에 따라 구현 클래스를 변경해야 할 경우
      • 직접 스프링 빈을 등록할 때의 장점
        • 주의 사항

Spring Bean and Dependency Injection

  • 지금까지 MemberRepository, MemberService, Member객체를 만들고 Service를 통해서 member를 가입 시키면 repository에 저장이 된다
  • 화면이 이 기능들을 붙일려면 Controller와 view template이 필요하다
  • 그럴려면 controller가 memberService를 통해서 회원가입 하고, 데이터를 조회 할 수 있어야 한다.
  • 이를 의존관계가 있다고 표현한다. (MemberController가 MemberService를 의존한다)
  • 이 작업을 스프링스럽게 해보자

Annotation

@Controller

  • Spring이 Spring Container에 memberController객체를 생성해서 관리를 한다. (이걸 spring container에서 spring bean이 관리 된다고 표현한다)

@Autowired

  • 연관관계를 맺어준다
  • 객체 생성 시점에 스프링 컨테이너에서 해당 스프린 빈을 찾아 주입한다 (DI)
  • 생성자가 1개만 있으면 @Autowired는 생략 가능하다

How to register Spring Bean

1. Component Scan and Auto Dependency Injection

  • @Controller, @Service, @Repository, @Autowired - 전부 @Component라는 annotation을 포함하고 있다
  • 스프링은 스프링 컨테이너에 스프링 빈을 등록할때 Singleton으로 등록한다 (유일하게 하나만 등록 및 공유). 따라서 같은 스프링 빈이면 모두 같은 인스턴스다

Component Scan의 대상(Range)

  • main method가 있는 동일 package (e.g., ericbyeric.firstspringdemo) 또는 하위패키지를 spring이 찾아서 등록한다.
  • @ComponentScan이라는 annotation을 사용하면 다른 경로의 package에서도 등록은 가능 but 잘 하지는 않는다..

2. Java code

정형화된 패턴

  • Controller - 외부 요청 받기
  • Service - 비지니스 로직 만들기
  • Repository - 데이터를 저장

Spring Bean Registration with Java Code

  • 컴포넌트 스캔은 자동으로 스프링 빈을 등록하고 의존 관계가 설정이 된다. 이번엔 하나하나 직접 스프링에 등록하는걸 해보자

ericbyeric.springbasic package에 SpringConfig파일 생성 ericbyeric/springbasic/SpringConfig.java

@Configuration  
public class SpringConfig {  
  
	@Bean  
	public MemberService memberService(){  
		return new MemberService(memberRepository());  
	}  
	  
	@Bean  
	public MemberRepository memberRepository(){  
		return new MemoryMemberRepository();  
	}  
}

  • @Configutation, @Bean
  • memberService와 memberRepository를 Spring에 등록하고 등록된 memberRepositorymemberService에 넣어준다.
  • @Controller는 어쩔수 없이 두고 컴포넌트 스캔으로 올리간 controller가 @autowired로 의존성이 연결된다(Spring bean으로 등록된 memberService가 DI된다)

과거에는 XML로 설정 했으나 실무에서는 잘 사용하지 않는다

의존성 주입 (Dependency Injection, DI)

총 3가지의 의존성 주입 방법이 있다

  • 생성자 주입
  • 필드 주입
  • setter 주입

생성자 주입 (Factory Pattern

@Controller  
public class MemberController {  
	  
	private final MemberService memberService;  
	  
	@Autowired  
	public MemberController(MemberService memberService) {  
		this.memberService = memberService;  
	}  
}
  • 가장 선호하는 방식!
  • 조립 시점에서 생성자가 한번만 세팅하고 변경 못하도록 막아버릴 수 있다 (Factory Pattern)

필드 주입

@Controller
public class MemberController {
	@Autowired private MemberService memberService;
	...
}
  • 단점 : 중간에 변경할 수 없다

Setter 주입

@Controller
public class MemberController {
	private MemberService memberService;

	@Autowired
	public void setMemberService(MemberService memberService){
		this.memberService = memberService;
	}
}
  • 단점: 누군가가 MemberController를 호출했을때 이게 열려있어야 한다. 현재 public으로 열려있으니 중간데 변경이 될 수도 있다. 호출되면 안되는 메소드들은 호출되면 안된다. 잘못 바꾸면 문제가 생길수도 있다. 굳이 한번 세팅하면 변경할 필요가 없는 부분인데..

상황에 따라 구현 클래스를 변경해야 할 경우

  • 우리가 회원 관리 예제 설계시 MemberRepository를 만들 때 아직 데이터 저장소가 선정되지 않았다고 했다. 그래서 Memory로 만들고 나중에 교체하자고 했었다. 그래서 interface를 설계를 하고 구현체로 MemoryMemberRepository를 쓰는 그림이 됐다.

  • 기존에 운영중인 코드(controller나 memberService들)를 하나도 손대지 않고 나중에 MemoryMemberRepository를 교체 할 수 있는 방법이 있다.
  • 그래서 이런 경우에 상황에 따라 구현 클래스를 변경해야 하면 설정을 통해 스프링 빈으로 등록한다.

직접 스프링 빈을 등록할 때의 장점

  • Repository를 교체하기 쉽다!
@Configuration  
public class SpringConfig {  
  
	@Bean  
	public MemberService memberService(){  
		return new MemberService(memberRepository());  
	}  
	  
	@Bean  
	public MemberRepository memberRepository(){  
		return new DbmoryMemberRepository();  
	}  
}

  • DbMemberRepository로 교체만 해주면 된다!
  • 컴포넌트 스캔으로 조립했을 경우는 여러 코드를 바꾸어 주어야 한다
주의 사항
  • @Autowired 를 통한 DI는 helloController , memberService 등과 같이 스프링이 관리하는 객체에서만 동작한다. 스프링 빈으로 등록하지 않고 내가 직접 생성한 객체에서는 동작하지 않는다.
  • @Bean이 설정 안된 객체 안에서 @Autowired를 사용해도 적용이 안된다!
  • 스프링 컨테이너에 올라 간 것들에게만 @Autowired가 동작한다!