- 시작하게 된 계기 및 다짐 😮
이번 코드스테이츠의 백엔드 엔지니어링 개발자 부트캠프
에 참여하게 되면서 현직개발자 분들의 빠른 성장을 위한 조언 중 자신만의 블로그를 이용하여 배운 것 들을 정리하는게 많은 도움이 된다 하여 시작하게 되었다.
- 학습 목표 😮
목표 | 결과 |
---|---|
Spring DI(Dependency Injection)의 의미를 이해 | O |
생성자 주입 방법(Constructor-based DI)권장 이유 이해 | O |
Component 스캔에 대해 이해 | O |
- 정리
1. Spring DI(Dependency Injection)
1). 생성자 주입
2). 수정자 주입(setter 주입)
3). 필드 주입
4). 일반 메서드 주입
1). 생성자 주입
- @Autowired를 하면 스프링 컨테이너에 @Component로 등록된 빈에서 생성자에 필요한 빈들을 주입한다.
- 특징
(1) 생성자 호출 시점에 딱 1번만 호출되는 것이 보장
(2) 불변과 필수 의존 관계에 사용
(3) 생성자가 1개만 존개하는 경우 @Autowired생략 가능
(4) NullPointerException을 방지
(5) 주입받을 필드를 final로 선언 가능
[예제 코드]
@Component
public class OrderServiceImpl implements OrderService {
private final UserRepository userRepository;
private final DiscountInfo discountInfo;
@Autowired
public [OrderServiceImpl](UserRepository userRepository, DiscountInfo discountInfo) {
this.userRepository = userRepository;
this.discountInfo = discountInfo;
}
}
2). 수정자 주입(setter 주입)
- setter라 불리는 필드의 값을 변경하는 수정자 메서드를 통한 의존관계 주입
- 특징
(1) 선택과 변경 가능성이 있는 의존 관계에 사용
(2) 자바빈 프로퍼티 규약의 수정자 메서드 방식을 사용하는 방법
- 생성자 주입과 차이점은 생성자 대신 set필드명 메서드를 생성하여 의존 관계를 주입
- 수정자의 경우 @Autowired를 입력하지 않으면 실행되지 않음
(1) @Component가 실행하는 클래스를 스프링 빈으로 등록합니다.
(2) 스프링 빈으로 등록한 다음 의존 관계를 주입하게 되는데 @Autowired 있는 것들을 자동으로 주입하게 됩니다.
[예제 Code]
@Component
public class OrderServiceImpl implements OrderService {
private UserRepository userRepository;
private DiscountInfo discountInfo;
@Autowired
public [void setUserRepository](UserRepository userRepository) {
this.userRepository = userRepository;
}
@Autowired
public [void setDiscountInfo](DiscountInfo discountInfo) {
this.discountInfo = discountInfo;
}
}
3). 필드 주입
- 필드에 @Autowired를 붙여서 주입
- 특징
(1) 코드가 간결하지만, 외부에서 변경이 불가능하여 테스트가 어렵다
(2) DI 프레임워크가 없으면 아무것도 할 수없음
(3) 실제 코드와 상관없는 테스트에 사용됨
(4) 정상적으로 작동하려면 setter 가 필요하게 되서 수정자 주입을 사용하는게 편함
[예제 Code]
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private UserRepository userRepository;
@Autowired
private DiscountInfo discountInfo;
}
4). 일반 메서드 주입
- 한번에 여러 필드를 주입받을 수 있지만, 잘 사용하지 않음
5). 옵션처리
- 주입할 스프링 빈이 없을 경우 동작이 필요할때
@Autowired(required=false);
[ex] public void setNoBean1(User noBean1);
- 일반적으로 @Autowired만 사용하는 경우 requried 옵션 기본값인 true가 적용되어 자동 주입대상이 없으면 오류발생
- 옵션 처리 방법
(1) @Autowired(required=false) : 자동 주입할 대상이 없으면 수정자 메서드 자체가 호출되지 않음
(2) @Nullable : 자동 주입할 대상이 없으면 null이 입력
(3) Optional<user> : 자동 주입할 대상이 없으면 Optional.empty가 입력됨
6). 생성자 주입을 사용해야 하는 이유
(1) 불변
- 의존 관계 주입은 실행시 정해지고나서 변경되지 않고 변경되서는 안되기 때문
- 수정자 주입의 경우, 메서드를 public으로 열어두어 변경이 가능하므로 안됨
- 객체 생성시 최초1 번만 호출되고 이후 호출되지 않게
(2) 누락
- 호출시 NPE(Null Point Exception)이 발생하는데, 의존 관계 주입이 누락되었기 떄문
- 생성자 주입을 사용하면 주입 데이터 누락시 컴파일 오류가 발생
(3) final 키워드 사용 가능
- 생성자 주입을 사용하면 필드에 final 키워드 사용가능
- 나머지 방식은 생성자 이후에 호출되므로 final 키워드를 사용할 수 없음
(4) 순환참조
[순환참조] : 메서드를 호출할 때 서로가 의존클래스 이기에 A->B가 필요하고 B->A가 필요하여 무한 루프에 빠지게 되는 상황
- 순환 참조를 방지
- 개발시 여러 컴포넌트 간에 의존성이 생기는데, 필드 주입과 수정자 주입은 빈이 생성된 후 참조하기에 오류와 경고 없이 구동되므로 실제 코드가 호출되기 전까지 문제를 알 수 없음 -> 순환 참조가 발생해도 실행됨
- 생성자를 통해 주입하면 BeanCurrentlyInCreationException이 발생
[@Lazy 어노테이션을 통해서 임의로 해결도 가능]
(5) 수정자 주입이 필요시, 필요에 의해서만 사용하믄됨
1. Component Scan
- 설정 정보 없이 자동으로 스프링 빈을 등록하는 컴포넌트 스캔 기능
- @ComponentScan을 통해 @Component가 붙은 모든 클래스를 스프링 빈으로 등록해준다.
- 의존관계도 자동으로 주입하는 @Autowired 기능도 함께 제공
- @Bean으로 등록한 클래스를 볼 수 없음
- @Configuration이 붙은 설정 정보도 자동으로 등록되고, 그외(@Target,Rententation,documented )
- 기존에 작성한 AppConfig가 있다면 정상적으로 작동하지 못하는데, 이 경우
[@ComponentScan(excludeFilters = @Filter(type = FilterType.ANNOTATION, classes = Configuration.class))]
코드를 작성해 해결 가능
[예제 Code]
@Configuration
@ComponentScan
public class AutoAppConfig {
}
[@Component - @Configuration이 등록된 곳에서 @Component를 가져오기 위해 사용됩니다.]
[@Autowired - 생성자 의존성 주입에 필요한 설정 정보 대신 의존관계 자동 주입을 해주게 됩니다.]
2. CompoenentScan() _basePackages
- 탐색할 패키지의 시작위치를 지정하고, 해당 패키지의 하위 패키지를 모두 탐색
- @ComponentScan()의 매개변수로 basePackages =""를 줄수있고, 지정하지 않을시 default로 클래스의 패키지가 시작위치
- [설정정보 클래스의 위치를 프로젝트 최상단에 두고 패키지 위치를 지정하지 않는 방법이 가장 편하다]
- 스프링 부트를 사용시, @SpringBootApplication를 프로젝트 시작위치에 두는게 좋음(@ComponentScan()이 들어있음)
3. ComponentScan의 대상
- @Component : 스캔 대상
- @Controller & RestController : 스프링 MVC 및 REST 전용 컨트롤러에서 사용됨
- @Service : 스프링 비지니스 로직에서 사용됨
- 특별한 처리가 없고, 개발자들이 핵심 비지니스 로직이 여기에 있다는 비지니스 계층인식에 도움이됨
- @Repository : 스프링 데이터 접근 계층에서 사용됨
- 스프링 데이터 접근 계층으로, 예외를 스프링 예외로 변환
- @Configuration : 스프링 설정정보 사용하고, 스프링 빈이 싱글톤 유지하도록 추가처리
[extra]
필터
- includeFilters : 컴포넌트 스캔 대상 추가 지정
- excludeFilters : 컴포넌트 스캔에서 제외할 대상 지정
[옵션]
- ANNOTATION: 기본값, 애너테이션으로 인식해서 동작합니다.
- ASSIGNABLE_TYPE: 지정한 타입과 자식 타입을 인식해서 동작합니다.
- ASPECTJ: AspectJ 패턴을 사용합니다.
- REGEX: 정규 표현식을 나타냅니다.
- CUSTOM: TypeFilter라는 인터페이스를 구현해서 처리합니다.
- 피드백 😮
Spring에서 Configuration대신 ComponentScan을 이용하여 설정정보없이(@Bean을 통해 메서드) Bean등를 가져오는 방법을 알아보았다.
이는 Compoenent,Controller등 여러 Scan대상이 있는데 각 애너테이션은 특정 상황에 따라 사용하는 애너테이션들이 달라진다.
따로 필요한 Bean들을 등록해주지 않고 필요한 것에 Component를 붙여 좀더 편리하게 빈들을 등록하고 의존성을 따로 주입해줄 필요가 없어 필요하다.
- 앞으로 해야 될 것 😮