1. 컴포넌트 스캔으로 스프링 빈 자동 등록
- @Component 가 붙은 클래스는 스프링 빈으로 자동 등록된다.
- `@Controller`, `@Service`, `@Repository`, `@Configuration` 은 @Component 를 포함하고 있어 자동 등록된다.
- 일반적으로 정형화된 컨트롤러, 서비스, 리포지토리 같은 코드에 사용
ComponentScan 을 통한 탐색 범위 지정
@ComponentScan(
basePackages = "hello.core",
}
- basePackages : 탐색할 패키지의 시작 위치를 지정하면, 해당 패키지를 포함한 하위 패키지까지만 스캔된다.
(ex. "hello.core" 라면, hello.core 패키지를 포함한 하위 패키지만 스캔 (형제 패키지는 등록 X))
(@ComponentScan(basePackages = "패키지명")을 명시하면 스캔 범위를 조정할 수 있다.)
- basePackages 를 지정하지 않으면, 설정 클래스(@Configuration) 위치를 기준으로 스캔된다.
> 일반적으로 @SpringBootApplication (내부에 @ComponentScan 포함됨) 를 프로젝트 최상단 패키지에 위치시키는 것을 권장한다.
>참고<
- 스프링 빈은 싱글톤으로 하나만 등록하여 공유해서 사용한다.
- 애노테이션이 특정 애노테이션이 들고 있다고 인식하는 것은 스프링이 지원하는 기능(자바 지원 X)
컴포넌트 스캔 외 다른 부가 기능
`@Controller` : 스프링 MVC 컨트롤러로 인식
`@Repository` : 스프링 데이터 접근 계층으로 인식하고, 데이터 계층의 예외를 스프링 예외로 변환
`@Configuration` : 스프링 설정 정보로 인식, 스프링 빈이 싱글톤으로 유지하도록 추가 처리
`@Service` : 특별한 처리 X, 개발자들이 비즈니스 계층이라고 인식하도록 도움
필터
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyIncludeComponent {
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyExcludeComponent {
}
@MyIncludeComponent
public class BeanA {
}
@MyExcludeComponent
public class BeanB {
}
이렇게 애노테이션을 만들고, 클래스 위에 애노테이션을 추가하면 된다
@MyIncludeComponent : 컴포넌트 스캔 대상에 추가할 클래스
@MyExcludeComponent : 컴포넌트 스캔 대상에서 제외할 클래스
@Configuration
@ComponentScan(
includeFilters = @Filter(type = FilterType.ANNOTATION, classes = MyIncludeComponent.class),
excludeFilters = @Filter(type = FilterType.ANNOTATION, classes = MyExcludeComponent.class)
)
class ComponentFilterAppConfig {
}
`includeFilters` : 컴포넌트 스캔 대상을 추가로 지정
`excludeFilters` : 컴포넌트 스캔에서 제외할 대상을 지정
그러면 BeanA 는 스프링 빈으로 등록되고, BeanB 는 스프링 빈으로 등록되지 않는다.
중복 등록과 충돌
자동 빈 등록 vs 자동 빈 등록 : `ConfilictingBeanDefinitionException` 예외 발생 (일어날 일 거의 X)
수동 빈 등록 vs 자동 빈 등록 : 수동 빈이 자동 빈을 오버라이딩 하며 아래와 같은 로그를 남긴다.
Overriding bean definition for bean 'memoryMemberRepository' with a different definition: replacing
Spring Boot 2.1 부터는 오버라이딩 하는 옵션의 기본값을 `false`로 바꾸었다.
application.properties 에서 다음 설정으로 허용 가능하다.
spring.main.allow-bean-definition-overriding=true
자동 주입
1. 생성자 주입 (가장 좋은 방법)
- 생성자를 통해 의존 관계를 주입하는 방법이다.
- 생성자 호출 시점에 딱 1번만 호출되는 것이 보장된다.
- 불변, 필수 의존관계에 사용한다.
- 프레임워크에 의존하지 않고 순수한 자바 언어의 특징을 가장 잘 살리는 방법이다.
@Autowired : 등록된 생성자로 스프링이 의존성 주입하여 연결해 준다.
이때, 스프링 빈으로 등록되어 스프링이 관리하는 객체에서만 동작한다.
> 참고 <
- @Autowired 는 생성자가 1개만 있으면 생략 가능하다.
- 생성자 주입을 제외한 나머지 방법은 생성자 이후에 호출되므로 `final` 키워드를 사용할 수 없다.
@Controller
public class MemberController {
private final MemberService memberService;
@Autowired
public MemberController(MemberService memberService) {
this.memberService = memberService;
}
}
@Service
public class MemberService {
private final MemberRepository memberRepository;
@Autowired
public MemberService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
}
2. 수정자 주입 (setter 주입)
- setter 라고 불리는 필드 값을 변경하는 수정자 메서드를 통해 의존 관계를 주입하는 방법이다.
- 선택, 변경 가능성이 있는 의존관계에 사용한다.
- 자바 빈 프로퍼티 규약의 수정자 메서드 방식을 사용하는 방법이다.
- 객체가 불완전한 상태로 생성될 가능성이 있다.
> 참고 <
- 자바 빈 프로퍼티 규약이란 필드의 값을 직접 변경하지 않고 set / get 메서드를 통해 값을 읽거나 수정하는 규칙이다.
@Component
public class OrderServiceImpl implements OrderService {
private MemberRepository memberRepository;
private DiscountPolicy discountPolicy;
@Autowired
public void setMemberRepository(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
@Autowired
public void setDiscountPolicy(DiscountPolicy discountPolicy) {
this.discountPolicy = discountPolicy;
}
}
자동 주입 대상을 옵션으로 처리하는 방법
- `@Autowired(required = false)` : 자동 주입할 대상이 없으면 수정자 메서드 자체가 호출 X
- `org.springframework.lang.@Nullable` : 자동 주입할 대상이 없으면 null 이 입력
- `Optional<>` : 자동 주입할 대상이 없으면 Optional.empty 가 입력
>참고<
- @Nullable 은 생성자 자동 주입에서 특정 필드에만 사용해도 된다.
// 호출 안됨
@Autowired(required = false)
public void setNoBean1(Member member) {
}
// null 호출
@Autowired
public void setNoBean2(@Nullable Member member) {
}
// Optional.empty 호출
@Autowired(required = false)
public void setNoBean3(Optional<Member> member) {
}
3. 필드 주입 (추천 X)
- 필드에 바로 주입하는 방법이다.
- 테스트 코드에서 간편하게 사용할 수 있다.
- 스프링 설정을 목적으로 하는 @Configuration 같은 곳에서만 특별한 용도로 사용하기도 한다.
- 외부에서 변경이 불가능해서 테스트하기 힘들다.
- DI 프레임워크(Spring)가 없으면 사용할 수 없다.
@Component
public class OrderServiceImpl implements OrderService {
@Autowired
private MemberRepository memberRepository;
@Autowired
private DiscountPolicy discountPolicy;
}
4. 일반 메서드 주입
- 일반 메서드를 통해서 주입받을 수 있다.
- 한 번에 여러 개의 빈을 주입받을 수 있다.
- 동적 주입이 필요할 때 사용한다. (특정 조건에 따라 주입하는 경우)
- 일반적으로 사용하지 않는다.
@Component
public class OrderServiceImpl implements OrderService {
private MemberRepository memberRepository;
private DiscountPolicy discountPolicy;
@Autowired
public void init(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
}
2. 자바 코드로 직접 스프링 빈 등록하기
- @Service, @Repository 없이 직접 @Bean 을 사용하여 스프링 빈을 등록한다.
- 구현체 변경이 필요한 경우 유용하다.
- @Configuration을 붙인 설정 클래스(AppConfig)에서 @Bean을 선언하여 등록한다.
- 컴포넌트 스캔과 함께 혼용할 수 있다.
>참고<
- @Configuration 없이 @Bean만 사용해도 스프링 빈으로 등록되지만, 싱글톤을 보장하지 않는다
@Configuration
public class SpringConfig {
@Bean
public MemberService memberService() {
return new MemberService(memberRepository());
}
@Bean
public MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
}
출처 | 스프링 입문(김영한) - 인프런
출처 | 스프링 핵심 강의 - 기본편(김영한) - 인프런
'💠프로그래밍 언어 > Java' 카테고리의 다른 글
[Spring] 순수 JDBC → JdbcTemplate → JPA → 스프링 데이터 JPA (0) | 2024.12.30 |
---|---|
[Spring] H2 데이터베이스 설치 (0) | 2024.12.30 |
[Spring] View 를 처리하는 방법들 (Static, Template, API) (2) | 2024.12.20 |
[Spring] 스프링 프로젝트 생성, 라이브러리, 빌드하기 (0) | 2024.12.20 |
[기본 개념] 9 | (2.2) 스트림 중간 연산 (0) | 2022.02.10 |