자바 코드로 직접 스프링 빈 등록하기
컨트롤러 파일을 제외한 @Service, @Repostiory, @Autowired 지우자.
@Controller
public class MemberController {
private final MemberService memberService;
@Autowired
public MemberController(MemberService memberService) {
this.memberService = memberService;
}
}
이전과 똑같은 오류가 난다.
hello.hellospring
패키지 내에 SpringConfig
자바 클래스 파일을 만든다.
@Configuration
public class SpringConfig {
@Bean
public MemberService memberService() {
return new MemberService(memberRepository());
}
@Bean
public MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
}
@Configuration
을 읽고 @Bean
을 읽고 memberService
를 스프링 빈으로 등록, 이 로직을 호출해서 스프링 빈으로 등록memeberRepository
를 멤버 서비스에 넣어준다.참고: 과거에는 자바 코드말고 XML로 설정하는 방식도 있지만 최근에는 잘 사용하지 않으므로 생략한다.
참고: DI(의존성 주입)에는 필드 주입, setter 주입, 생성자 주입 이렇게 3가지 방법이 있다. 의존관계가 실행중에 동적으로 변하는 경우는 거의 없으므로 생성자 주입을 권장한다.
1. 필드 주입
@Controller
public class MemberController {
@Autowired private MemberService memberService;
2. setter 주입
@Controller
public class MemberController {
private MemberService memberService;
@Autowired
public void setMemberService(MemberService memberService) {
this.memberService = memberService;
}
3. 생성자 주입
@Controller
public class MemberController {
private final MemberService memberService;
@Autowired
public MemberController(MemberService memberService) {
this.memberService = memberService;
}
}
참고: 실무에서는 주로 정형화된 컨트롤러, 서비스, 리포지토리 같은 코드는 컴포넌트 스캔을 사용한다. 그리고 정형화 되지 않거나, 상황에 따라 구현 클래스를 변경해야 하면 설정을 통해 스프링 빈으로 등록한다.
-> SpringConfig 파일에서@Bean public MemberRepository memberRepository() { return new DBMemberRepository(); }
로만 바꿔주면 된다.
주의:
@Autowired
를 통한 DI는 helloConroller , memberService 등과 같이 스프링이 관리하는 객체에서만 동작한다. 스프링 빈으로 등록하지 않고 내가 직접 생성한 객체에서는 동작하지 않는다.
ex) MemberService를 빈으로 등록하지도 않았는데 MemberService 클래스 안에서@Autowired
를 써도 안 먹힘.
의존성을 주입한다는 이유는 객체간의 결합도를 낮추기 위함이다.
그럼 왜 결합도를 낮추어야 하는가? 라고 질문을 해보면, 각 객체는 비즈니스 요구사항에 의해 계속 변하기 때문인데, 그럼 의존성 주입을 해야하는 객체는 대체로 변화할 여지가 있는 객체라고 말할 수 있다.
그러나 컨트롤러는 사용자의 URL을 특정 서비스에 전달하는 매퍼의 역할을 할 뿐 비즈니스 로직을 가지고 있지 않다. 그래서 비즈니스 요구사항이 변한다고 해서 컨트롤러를 수정하거나 다른것으로 교체해야 하는 경우는 없다. 이런 이유 떄문에 컨트롤러 자체를 Config로 관리하지 않는다고 생각한다.
스프링 컨테이너가 직접 의존 관계를 처리해야 한다면
@Autowired
를 명시해야 하며 이 때 두 객체(의존성을 주입받는 객체와 주입할 객체) 모두 스프링 빈이어야 합니다.
@Autowired
로 의존성을 주입하려 할 때는 둘 다 스프링 빈이어야 하나 사용자가 집적 의존관계를 주입해준다면 주입할(주입받는) 객체는 빈이 아니어도 괜찮습니다.
@Bean이 붙은 메서드에 대해서는 메서드 이름이 빈의 이름이 된다.
@Component
어노테이션이 달린 클래스를 찾아 빈으로 등록(Service, Controller, Repository 등) @Autowired
을 찾아 의존성 주입을 수행함MemberController
에 MemberService
가 @Autowired
로 되어있으므로 컨테이너에에 빈으로 올라가있는 MemberService
주입MemberService
에 Repository
부분이 @Autowired
이므로 컨테이너에 빈으로 올라간 Repository
주입 => 생성자 주입의 경우 생성 되는 과정 속에서 의존관계 주입이 발생
@Configuration
어노테이션에 @Component
가 포함되어 있음 springConfig
를 통해 memberService
, memberRepository
가 Bean으로 등록됨 memberService
에 return new MemberService(memberRepository());
로 인해 MemberService
와 MemberRepository
는 의존성이 존재하고, MemberService
에 MemberRepository
를 주입해 줌 MemberController
등록 @Autowired
를 통해 memberService
를 주입해야 함 MemberService
빈이 올라가 있으므로 그것을 주입=> MemberController
의 경우 MemberService
가 빈으로 등록되어 있어도 @Autowired
는 필요합니다. (물론, 스프링 부트의 경우 생성자 주입에 대하여 편의를 위해 @Autowired를 생략할 수 있도록 구현되어 있습니다.) 스프링이 @Autowired
를 보고 의존관계를 자동 주입(컨테이너에서 의존관계에 해당하는 빈을 찾아와서 주입)하기 때문입니다.