✔️생성자 주입
@Autowired
를 생략해도 자동 주입이 된다. 이는 스프링 빈에만 해당한다.private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
@Autowired
public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
✔️수정자 주입(setter 주입)
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 private MemberRepository memberRepository;
@Autowired private DiscountPolicy discountPolicy;
✔️일반 메서드 주입
주입할 스프링 빈이 없어도 동작해야 할 때가 있다.
@Autowired
는 기본값이 required=true
이기 때문에 자동 주입 대상이 만약에 없다면 오류가 발생하게 된다.
@Autowired(required=false)
: 자동 주입할 대상이 없으면 수정자 메서드 자체가 호출 안 됨 org.springframework.lang.@Nullable
: 자동 주입할 대상이 없으면 null이 입력된다.Optional<>
: 자동 주입할 대상이 없으면 Optional.empty
가 입력된다.public class AutowiredTest {
@Test
void AutowiredOption() {
ApplicationContext ac = new AnnotationConfigApplicationContext(TestBean.class);
}
@Configuration
static class TestBean {
@Autowired(required = false)
public void setNoBean1(Member member) {
System.out.println("member = " + member);
}
@Autowired
public void setNoBean2(@Nullable Member member) {
System.out.println("member = " + member);
}
@Autowired(required = false)
public void setNoBean3(Optional<Member> member) {
System.out.println("member = " + member);
}
}
}
✔️불변
setxxx
를 public으로 열어두어야 한다.✔️final 키워드
final
키워드를 사용할 수 있다.@Getter
@Setter
@ToString
public class HelloLombok {
private String name;
private int age;
public static void main(String[] args) {
HelloLombok helloLombok = new HelloLombok();
helloLombok.setName("test");
helloLombok.setAge(20);
System.out.println("helloLombok = " + helloLombok);
}
}
Lombok을 사용하여 Getter/Setter
는 물론 toString()
까지 어노테이션 하나로 직접 적용을 할 수 있다.
뿐만 아니라 @RequiredArgsConstructor
를 사용하면 final
키워드가 붙은 필드를 모아서 생성자를 자동으로 만들어준다.
롬복이 자바의 어노테이션 프로세서라는 기능을 이용해서 컴파일 시점에 생성자 코드를 자동으로 생성해준다. 실제 .class
를 열어보면 확인이 가능하다.
@Component
@RequiredArgsConstructor
public class OrderServiceImpl implements OrderService{
private final MemberRepository memberRepository;
//private final DiscountPolicy discountPolicy = new FixDiscountPolicy();
private final DiscountPolicy discountPolicy;
@Override
public Order createOrder(Long memberId, String itemName, int itemPrice) {
Member findMember = memberRepository.findById(memberId);
int discountPrice = discountPolicy.discount(findMember, itemPrice);
return new Order(memberId, itemName, itemPrice, discountPrice);
}
// MemberRepository 확인 용도
public MemberRepository getMemberRepository() {
return memberRepository;
}
}
@Component
public class FixDiscountPolicy implements DiscountPolicy
@Component
public class RateDiscountPolicy implements DiscountPolicy
✔️문제 원인
DiscountPolicy
의 구현 클래스인 RateDiscountPolicy
와 FixDiscountPolicy
가 모두 스프링 빈으로 등록되어 조회되는 빈이 2개인 문제가 발생✔️문제 해결
public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy fixDiscountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = fixDiscountPolicy;
}
@Component
@Qualifier("mainDiscountPolicy")
public class FixDiscountPolicy implements DiscountPolicy
public OrderServiceImpl(MemberRepository memberRepository, @Qualifier("mainDiscountPolicy") DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
@Component
@Primary
public class FixDiscountPolicy implements DiscountPolicy
public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@Qualifier("mainDiscountPolicy")
public @interface MainDiscountPolicy {
}
@Component
@MainDiscountPolicy
public class FixDiscountPolicy implements DiscountPolicy
public OrderServiceImpl(MemberRepository memberRepository, @MainDiscountPolicy DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
public class AllBeanTest {
@Test
void findAllBean() {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AutoAppConfig.class, DiscountService.class);
DiscountService discountService = ac.getBean(DiscountService.class);
Member member = new Member(1L, "userA", Grade.VIP);
int discountPrice1 = discountService.discount(member, 10000, "fixDiscountPolicy");
Assertions.assertThat(discountService).isInstanceOf(DiscountService.class);
Assertions.assertThat(discountPrice1).isEqualTo(1000);
int discountPrice2 = discountService.discount(member, 10000, "rateDiscountPolicy");
Assertions.assertThat(discountService).isInstanceOf(DiscountService.class);
Assertions.assertThat(discountPrice2).isEqualTo(1000);
}
@Configuration
static class DiscountService {
private final Map<String, DiscountPolicy> policyMap;
private final List<DiscountPolicy> policyList;
public DiscountService(Map<String, DiscountPolicy> policyMap, List<DiscountPolicy> policyList) {
this.policyMap = policyMap;
this.policyList = policyList;
System.out.println("policyMap = " + policyMap);
System.out.println("policyList = " + policyList);
}
public int discount(Member member, int price, String discountCode) {
DiscountPolicy discountPolicy = policyMap.get(discountCode);
return discountPolicy.discount(member, price);
}
}
}
✔️편리한 자동 기능을 기본으로 사용하자.
✔️수동 빈 등록의 사용처