티스토리 뷰
Dependency Injection (의존성 주입)
"스프링 철저 입문" 책 정리
배경
의존관계를 가지는 컴포넌트들의 결합도를 낮추기 위해 나타남
- 컴포넌트 내에서 직접 의존관계를 가지는 컴포넌트에 대한 인스턴스 생성
public class UserServiceImp implements UserService{
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
public UserServiceImp(){
//구현체를 직접 생성
this.userRepsitory = new UserImplRepository();
this.passwordEncoder = new BCryptPasswordEncoder();
}
....
}
userRepository, passwordEncoder에 대한 구현체가 바뀐다면?
=> UserserviceImp 클래스를 직접 수정해야 함.
- 외부에서 UserService가 필요한 컴포넌트를 생성하고 UserService의 인스턴스 생성 시 주입
...
UserRepository userRepository = new UserImplRepository();
PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
UserService userService = new UserServiceImp(userRepositry, passwordEncoder);
...
UserService 내부 코드는 UserRepository, PasswordEncoder의 구현체가 변경되더라도 영향을 받지 않는다.
하지만, 여전히 개발자가 직접 구현체를 생성해서 주입해야 하는 번거로움이 남아있다.
DI 컨테이너는 이러한 번거로운 과정을 대신 처리한다.
- 인터페이스, 구현체, 의존관계를 정의해주면,
컴포넌트를 생성할 때, 자동으로 의존관계를 가지는 컴포넌트를 생성하여 주입한다.
* DI 컨테이너가 컴포넌트를 꺼내오는 방법
ApplicationContext context = ...; //스프링 DI 컨테이너
UserService userService = context.getBean(UserService.class);
장점
- 인스턴스의 스코프를 제어할 수 있음
( 싱글톤 또는, 프로토타입으로 컴포넌트 호출 가능 )
- 인스턴스의 생명 주기를 제어할 수 있음
- AOP 방식으로 공통 기능을 집어넣을 수 있음
- 의존하는 컴포넌트 간의 결합돌르 낮춰서 단위 테스트하기 쉽게 만듦
ApplicationContext
스프링에서의 DI Container
스프링의 DI Container에 컴포넌트 등록하는 방법
1. @Configuration + @Bean : 자바 기반 컴포넌트 등록 방식
2. XML 기반 컴포넌트 등록 방식
3. ComponentScan : 어노테이션 기반 컴포넌트 등록 방식
※ 룩업(lookup) : DI Container 에서 빈을 찾아오는 행위
※ 빈(Bean) : DI Container에 등록된 컴포넌트 (스프링 환경에서)
※ 빈 정의(Bean Definition) : 빈에 대한 설정 저보 (Configuration)
1. 자바 기반 설정 방식
: 자바 코드로 빈 설정
: 이 때 사용되는 자바 코드 -> Configuration 클래스
: 컴포넌트의 빈 설정에 필요한 모든 컴포넌트 또한 빈으로 설정해야 함 (설정해야 할게 많음...)
-> 어노테이션 설정 방식과 함께 사용하여, 많은 부분을 줄일 수 있음
@Configuration
public clas AppConfig{
@Bean
public UserRepository userRepository(){
return new UserImplRepository();
}
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
/////////////////////////
@Bean // ---- (1) 메서드 내에서 의존성 주입
public UserService userService(){
return new UserServiceImpl(userRepository(), passwordEncoder());
}
@Bean // ---- (2) 메서드의 매개변수를 통한 의존성 주입
public UserService userService(UserRepository userRepository, PasswordEncoder passwordEncoder){
return new UserServiceImpl(userRepository, passwordEncoder);
}
}
2. XML 기반 설정 방식
: <beans> 내 <bean> 태그에서 속성 id(bean 명), class(구현클래스-FQCN) 를 이용하여 bean을 등록한다.
: <constructor-arg> 태그에서 ref 속성을 이용해 의존성 주입할 빈 이름을 기재한다, value 속성을 이용하여 특정 값 또한 기재할 수 있다.
※ FQCN : Fully Qualified Class Name - 패키지 명을 포함한 클래스 명
3. 어노테이션 기반 설정 방식
: 빈 정의 어노테이션을 클래스에 부여
: ComponentScan을 통해, @Component가 붙은 클래스를 DI Containter에 자동으로 등록
: 어노테이션이 붙은 클래스에 대해서 DI Container가 자동으로 의존성 주입 수행 => 오토 와이어링(Auto Wiring)
@Component
public class UserImplRepository implements UserRepository{
...
}
@Component
public class BCryptPasswordEncoder implements PasswordEncoder{
...
}
////////////////
@Component
public class UserServiceImpl implements UserService{
@Autowired // ----------- DI Container가 자동으로 주입해줌
UserRepository userRepository;
@Autowired // ----------- DI Container가 자동으로 주입해줌
PasswordEncoder passwordEncoder;
}
* 자바 + 어노테이션 기반 설정 방식
: Configuration 클래스에 ComponentScan을 붙여서 사용
@ComponentScan("com.example.base") //---- DI Container에 자동으로 등록할 컴포넌트를 찾는 패키지 경로 설정
@Configuration
public class AppConfig{
...
}
* Bean 명
: Bean 명은 기본적으로 클래스 명의 첫 글자를 소문자로 바꾼 이름과 같다.
: 첫글자부터 대문자가 연속으로 2회이상 나오면 그대로 클래스 명 그대로 이름이 등록된다.
ex)
UserService -> userService
BCryptPasswordEncoder -> BCryptPasswordEncoder
: 이름 변경을 원할 경우, 어노테이션에 명시적으로 이름을 작성한다.
ex)
@Component("userService")
'Spring' 카테고리의 다른 글
[Spring in Action] RestTemplate, Traverson 을 이용하여 REST 서비스 사용하기 (0) | 2022.03.24 |
---|---|
[ Spring in Action ] HATEOAS 란? (0) | 2022.03.15 |
[ validation ] BindException? MethodArgumentNotValidException? (+ @RequestBody) (0) | 2022.01.04 |
[ junit5 ] 단위테스트 - controller (0) | 2021.12.30 |
[Spring Rest Docs ] Spring Rest Docs 적용하기 (2) | 2021.11.23 |