스프링은 웹 어플리케이션 실행 시 스프링 컨테이너에 스프링 빈을 등록해서 의존 관계를 자동으로 설정해준다. 스프링을 사용하면 객체를 스프링 빈으로 등록해서 써야 얻는 이점이 많다.
스프링 Bean 등록 방법에는 2가지가 있다.
두가지 방법 모두 알아야 한다.
hello.hellospring 패키지와 하위 패키지의 @Component가 있는 클래스만 스프링 컨테이너의 스프링 Bean으로 생성
@SpringBootApplication에 들어가보면 @ComponentScan이 있다.
demo의 Demo 클래스는 hello.hellospring의 하위 패키지가 아니므로 스프링 빈으로 생성되지 않는다.
@Controller, @Service, @Repository는 내부에 @Component가 있다. 따라서 스프링 빈으로 자동 등록된다.
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;
//DI의 생성자 주입 방식.
@Autowired
public MemberController(MemberService memberService) {
this.memberService = memberService;
}
}
이대로 Controller만 추가한 후 실행시키면 아래와 같은 에러가 뜬다.
memberService가 스프링 빈으로 등록되어 있지 않기 때문이다.
@Service
public class MemberService {
private final MemberRepository memberRepository;
@Autowired
public MemberService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
@Repository
public class MemoryMemberRepository implements MemberRepository {
@Service와 @Repository로 memberService와 memberRepository가 스프링이 컨테이너에 스프링 빈으로 등록되었다.
@Autowired로 각 개체의 의존성이 주입되어 연결되었다.
참고로 스프링은 스프링 컨테이너에 스프링 빈을 등록할 때, 기본으로 싱글톤으로 등록한다. 즉, 유일하게 하나만 등록해서 공유한다. 따라서 같은 스프링 빈이면 모두 같은 인스턴스이다. 설정으로 싱글톤이 아니게 설정할 수 있지만, 특별한 경우를 제외하면 대부분 싱글톤을 사용한다.
스프링 빈에 등록하지 않고, new 로 객체를 직접 생성한 경우 @Autowired가 작동하지 않는다. @Autowired는 스프링 컨테이너에서 관리하는 스프링 빈에만 작동한다.
회원 서비스와 회원 리포지토리의 @Service, @Repository, @Autowired 애노테이션을 제거하고 진행한다.
SpringConfig 클래스 파일을 만든다.
@Configuration은 현재 클래스가 Spring의 설정 파일임을 알려주는 어노테이션이다.
@Bean: 스프링 컨테이너의 스프링 빈으로 등록
@Autowired로 스프링 컨테이너가 의존성 주입을 해주었던 MemberSerivce의 MemberRepository는 자바 소스에서 직접 넣어준다.
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() {
//@Autowired로 스프링 컨테이너가 의존성 주입을 해주는 대신, JAVA 소스로 직접 넣어준다.
return new MemberService(memberRepository());
}
@Bean
public MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
}
자바 코드로 직접 스프링 빈을 등록하는 것의 이점은 나중에 사용하는 구현체 클래스를 바꿔야 할 때, 설정 파일(Ex., SpringConfig)에서 해당 부분만 변경하면 된다는 것이다.
여기서는 향후 MemoryMemberRepository를 다른 리포지토리로 변경할 예정이므로, 나중에 아래와 같이 변경해주면 된다.
@Bean
public MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
@Bean
public MemberRepository memberRepository() {
return new DbMemberRepository();
}
DI에는 3가지 주입 방식이 있다.
3가지 방법 중 생성자 주입 방식을 제일 권장한다.
@Controller
public class MemberController {
@Autowired
private final MemberService memberService;
}
@Controller
public class MemberController {
private MemberService memberService;
@Autowired
public void setMemberService(MemberService memberService) {
this.memberService = memberService
}
@Controller
public class MemberController {
private final MemberService memberService;
@Autowired
public MemberController(MemberService memberService) {
this.memberService = memberService;
}
}
@Controller
public class MemberController {
private final MemberService memberService;
@Autowired
public MemberController(MemberService memberService) {
this.memberService = memberService;
}
}
@RequiredArgsConstructor
@Controller
public class MemberController {
private final MemberService memberService;
}