Open TGKim-Peter opened 1 year ago
static class TestBean {
// 1. 자동주입할 빈이 없으면 -> 해당 메서드 자체가 호출안됨
@Autowired(required = false)
public void setNoBean1(Member noBean1) {
System.out.println("noBean1 = " + noBean1);
}
// 2. 자동주입할 빈이 없으면 -> null 반환
@Autowired
public void setNoBean2(@Nullable Member noBean2) {
System.out.println("noBean2 = " + noBean2);
}
// 3. 자동주입할 빈이 없으면 -> Optional.empty 반환
@Autowired
public void setNoBean3(Optional<Member> noBean3) {
System.out.println("noBean3 = " + noBean3);
}
}
@Getter, @Setter, @ToString 등 유용한 기능 제공
@RequiredArgsConstructor 를 사용해 생성자 주입도 생략할 수 있음
@Component
@RequiredArgsConstructor // 생성자 주입 생략해도 있는 것 처럼 동작
public class OrderServiceImpl implements OrderService {
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
}
@Autowired 필드명 매칭
@Quilifier -> @Quilifier끼리 매칭 -> 빈 이름 매칭
@Component
@Qualifier("mainDiscountPolicy")
public class RateDiscountPolicy implements DiscountPolicy {
}
@Quilifier는 @Quilifier를 찾는 용도로만 사용하는게 좋다.
@Primary 매칭
권장 방법
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@Qualifier("mainDiscountPolicy")
public @interface MainDiscountPolicy {
}
편리한 자동 빈 등록을 기본으로 사용하자. @Component 등등...
수동 빈 등록을 사용하면 좋은 케이스
기술 지원 빈: 기술적인 문제나 AOP를 처리할 때 주로 사용. 데이터베이스 연결, 공통 로그 처리 등 업무 로직을 처리하기 위한 하부 기술이나 공통 기술들
다형성을 적극 활용한 빈들 한눈에 보기 쉽게 할 때
@Configuration
public class DiscountPolicyConfig {
@Bean
public DiscountPolicy rateDiscountPolicy() {
return new RateDiscountPolicy();
}
@Bean
public DiscountPolicy fixDiscountPolicy() {
return new FixDiscountPolicy();
}
}
불변, 필수
의존관계에 사용@Autowired
를 생략해도 자동 주입 된다. 스프링 빈( / Component)에만 해당@Component
public class OrderServiceImpl implements OrderService {
private MemberRepository memberRepository;
@Autowired(required = false) // 수정자 주입
public void setMemberRepository(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
}
@Autowired
의 기본 동작은 주입할 대상이 없으면 오류 발생, 주입할 대상이 없어도 동작하게 하려면@Autowired(required = false)
지정
@Component
public class OrderServiceImpl implements OrderService {
@Autowired
private MemberRepository memberRepository;
}
@Configuration
같은 곳에서만 특별한 용도로 사용@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;
}
}
@Autowired
만 사용하면 required
옵션 default 값이 true
로 되어 있어서 자동 주입 대상이 없으면 오류 발생자동 주입 대상을 옵션으로 처리하는 방법은 다음과 같다.
@Autowired(required = false)
: 자동 주입할 대상이 없으면 수정자 메서드 자체가 호출 안됨org.springframework.lang.@Nullable
: 자동 주입할 대상이 없으면 null이 입력된다.Optional<>
: 자동 주입할 대상이 없으면 Optional.empty
가 입력된다.// 출력 결과
setNoBean2 = null
setNoBean3 = Optional.empty
참고:
@Nullable, Optional
은 스프링 전반에 걸쳐서 지원되기 때문에 생성자 자동 주입에서 특정 필드에만 사용해도 된다.
과거에는 수정자 주입과 필드 주입을 많이 사용했지만, 스프링을 포함한 DI 프레임워크 대부분이 생성자 주입을 권장한다. 그 이유는 다음과 같다.
- final 키워드를 사용하면 객체 생성 안했을 때 컴파일 오류가 발생하여 실수 방지할 수 있음
- 수정자 주입을 포함한 나머지 주입 방식은 생성자 이후에 호출되므로,
final
키워드를 사용할 수 없다.- 생성자 주입 방식을 선택하면 프레임워크에 의존하지 않고, 순수한 자바 언어의 특징을 잘 살리는 방법임
- 생성자 주입과 수정자 주입 동시에 사용할 수 있기 때문에 필수 값이 아닌 경우에 수정자 주입 방식을 옵션으로 부여(진짜?)
- 필드 주입 방식은 사용하지 않는게 좋다.(실제로 이런코드 많음, 보통
@Autowired
어노테이션 지우고@RequiredArgsConstructor
어노테이션 적용했음)
불변
누락
막상 개발해보면 대부분 불변.. 생성자 주입 자동으로 해줄 수 없을까?
- [start.spring.io](http://start.spring.io/) 에서 프로젝트 생성할 때 dependency 추가 가능
- Plugin 에서 Lombok 추가
- Preferences > Compiler > Annotation Processors 에서 Enable annotation processing 체크
- 대부분
@RequiredArgsConstructor
와final
키워드 함께 사용
@Autowired 는 타입으로 조회하기 때문에 등록된 Bean 이 2개 이상인 경우 에러 발생
NoUniqueBeanDefinitionException: No qualifying bean of type ..
조회 대상 Bean 이 2개 이상일 때 해결 방법
@Autowired
필드 명 매칭
@Autowired
public OrderServiceImpl(DiscountPolicy rateDiscountPolicy) {
this.discountPolicy = rateDiscountPolicy;
}
@Qualifier
사용
구분자를 붙여주는 방법
@Qualifier
끼리 매칭 (어디까지 매칭해야 하는가?)- 빈 이름 매칭
NoSuchBeanDefinitionException
예외 발생
@Primary
사용
우선순위를 정하는 방법으로 @Autowired 시에 여러 빈 매칭되면
@Primary
가 우선권을 가짐
- 주로 DB Connection 에서 사용 Main DB 연결시에 사용
우선순위는 @Qualifier
가 @Primary
보다 높다.
Annotation 방식
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Qualifier("mainDiscountPolicy")
public @interface MainDiscountPolicy {
}
@Component
@MainDiscountPolicy
public class RateDiscountPolicy implements DiscountPolicy {}
// 생성자 자동 주입
@Autowired
public OrderServiceImpl(MemberRepository memberRepository,
@MainDiscountPolicy DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
// 수정자 자동 주입도 사용 가능
애노테이션에는 상속이라는 개념이 없다. 애노테이션을 모아서 사용하는 기능은 스프링이 지원해주는 기능임.
스프링을 사용하면 전략 패턴을 간단하게 구현 가능하다.
@RequiredArgsConstructor
static class DiscountService {
private final Map<String, DiscountPolicy> policyMap;
private final List<DiscountPolicy> policies;
public int discount(Member member, int price, String discountCode) {
DiscountPolicy discountPolicy = policyMap.get(discountCode);
return discountPolicy.discount(member, price);
}
}
Map<String, DiscountPolicy>
: map 의 키에 스프링 빈의 이름(fixDiscountPolicy, rateDiscountPolicy)을 넣어주고, 그 값으로 DiscountPolicy
타입으로 조회한 모든 스프링 빈을 담아준다.List<DiscountPolicy>
: DiscountPolicy
타입으로 조회한 모든 스프링 빈을 담아준다.“**편리한 자동 기능을 기본으로 사용하자”**
“그러면 수동 빈 등록은 언제 사용하면 좋을까?”
비즈니스 로직 중 다형성을 적극 활용할 때 특정 패키지에 같이 묶어 두는게 좋다. 딱 보고 이해가 되서 유지보수 용이함
@Configuration
public class DiscountPolicyConfig {
@Bean
public DiscountPolicy rateDiscountPolicy() {
return new RateDiscountPolicy();
}
@Bean
public DiscountPolicy fixDiscountPolicy() {
return new FixDiscountPolicy();
}
}
“정리”
편리한 자동 기능을 기본으로 사용하자
직접 등록하는 기술 지원 객체는 수동 등록
다형성을 적극 활용하는 비즈니스 로직은 수동 등록을 고민해보자
@myeongho2 aws sdk bean 생성할 때, 주입코드 공유
local, test
@Profile({"local", "test"})
@Configuration
public class LocalS3Config {
@Value("${aws.s3.region}")
private String region;
@Value("${aws.s3.bucket}")
private String bucket;
@Bean(initMethod = "start", destroyMethod = "stop")
public S3Mock s3Mock() {
return new S3Mock.Builder().withPort(port).withInMemoryBackend().build();
}
@Bean
public S3Client amazonS3(S3Mock s3Mock) {
S3Client client = S3Client.builder()
... // region, endpoint, bucket settings...
return client;
}
}
@Configuration
@Profile({"!local"})
public class S3Config {
@Value("${aws.s3.region:us-east-1}")
private String region;
@Bean(destroyMethod = "close")
public S3Client s3Client() {
return S3Client.builder().region(Region.of(region)).build();
}
}
의존관계 자동 주입
의존 관계 주입 방법
생성자 주입
ex) Component Annotation을 통해 Spring Bean에 등록되는 시점에 생성자 호출되며 의존 관계 주입
수정자 주입 (setter 주입)
필드 주입
일반 메서드 주입
옵션 처리
생성자 주입을 선택해라!
롬복과 최신 트렌드
조회 빈이 2개 이상일 때,
위 상황 시 각각의 bean 명도 컴파일 시 알 수 있음.
@Autowired 필드 명, @Qualifier, @Primary
@Autowired 필드 명 매칭
@Qualifier > @Qualifier끼리 매칭 빈 이름 매칭 > 빈 이름 매칭
ex) @Qualifier("mainDiscountPolicy")
@Primary 사용
애노테이션 직접 만들기 -- 생략
조회한 빈이 모두 필요할 때, List, Map -- 생략
자동, 수동의 올바른 실무 운영 기준