회원 컨트롤러가 회원서비스와 회원 리포지토리를 사용할 수 있게 의존관계를 설정하자.
MemberController.java
package com.jiu.spring_basic.controller;
import com.jiu.spring_basic.service.MemberService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller
public class MemberController {
private final MemberService memberService;
@Autowired
public MemberController(MemberService memberService){
this.memberService = memberService;
}
}
오류 발생 - memberService가 스프링 빈으로 등록되어있지 않기 때문이다.
스프링 빈 등록 방법
- 컴포넌트 스캔과 자동 의존관계 설정
- 자바 코드로 직접 빈 등록
@Component
(@Controller
, @Service
, @Repository
)@Autowired
@Service
public class MemberService {
private final MemberRepository memberRepository;
@Autowired
public MemberService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
@Autowired
를 사용하면 객체 생성 시점에 스프링 컨테이너에서 해당 스프링 빈을 찾아서 주입한다.@Autowired
생략 가능@Repository
public class MemoryMemberRepository implements MemberRepository {}
memberService 와 memberRepository 가 스프링 컨테이너에 스프링 빈으로 등록되었다.
스프링은 스프링 컨테이너에 스프링 빈을 등록할 때, 기본으로 싱글톤으로 등록한다
SpringConfig.java
package com.jiu.spring_basic;
import com.jiu.spring_basic.repository.MemberRepository;
import com.jiu.spring_basic.repository.MemoryMemberRepository;
import com.jiu.spring_basic.service.MemberService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SpringConfig {
@Bean
public MemberService memberService() {
return new MemberService(memberRepository());
}
@Bean
public MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
}
@Bean
public MemberRepository memberRepository() {
return new DbMemberRepository();
}
이런 식으로 한줄만 변경할 수 있다. -> 스프링 빈 자바로 직접 등록했기 때문에DI는 생성자 주입, setter 주입, field 주입이 있다. 생성자 주입을 권장한다.
실무에서는 주로 정형화된 컨트롤러, 서비스, 리포지토리 같은 코드는 컴포넌트 스캔을 사용한다.
정형화 되지 않거나, 상황에 따라 구현 클래스를 변경해야 하면 설정을 통해 스프링 빈으로 등록한다. (MemoryMemberRepository
->DbMemberRepository
)
@Autowired
를 통한 DI는 스프링이 관리하는 객체(@Component
으로 등록된 객체 / @Configuration
클래스에서 @Bean
으로 등록된 객체)에서만 동작한다. 스프링 빈으로 등록하지 않고 내가 직접 생성한(자바 코드로만) 객체에서는 동작하지 않는다
인터페이스 Bean 등록
구현체는 어떻게 빈으로 등록될까?(하나의 인터페이스에 구현체가 여러개라면?)
// UserRepository.java
public interface UserRepository {
void save(User user);
User findById(Long id);
}
// InMemoryUserRepository.java
import org.springframework.stereotype.Repository;
import java.util.HashMap;
import java.util.Map;
@Repository
public class InMemoryUserRepository implements UserRepository {
private final Map<Long, User> users = new HashMap<>();
@Override
public void save(User user) {
users.put(user.getId(), user);
}
@Override
public User findById(Long id) {
return users.get(id);
}
}
// UserService.java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserService {
private final UserRepository userRepository;
@Autowired
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public void createUser(User user) {
userRepository.save(user);
}
public User getUser(Long id) {
return userRepository.findById(id);
}
}
UserRepository
인터페이스를 @Autowired
로 주입받는다.UserRepository
의 구현체로 InMemoryUserRepository
를 찾아서 주입한다.