private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
@Autowired
public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
생성자가 1개만 있으면 @Autowired를 생략해도 자동 주입 된다. 물론 스프링 빈에만 해당한다.
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 의 기본 동작은 주입할 대상이 없으면 오류가 발생한다. 주입할 대상이 없어도 동작하게 하려면 @Autowired(required = false) 로 지정하면 된다.
자바빈 프로퍼티 규약
class Data {
private int age;
public void setAge(int age) {
this.age = age;
}
public int getAge() {
return age;
}
}
자바에서는 과거부터 필드의 값을 직접 변경하지 않고, setXxx, getXxx라는 메서드를 통해서 값을 읽거나 수정하는 규칙
@Autowired
private MemberRepository memberRepository;
@Autowired
private DiscountPolicy discountPolicy;
private MemberRepository memberRepository;
private DiscountPolicy discountPolicy;
@Autowired
public void init(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
참고: 의존관계 자동 주입은 스프링 컨테이너가 관리하는 스프링 빈이어야 동작한다. 스프링 빈이 아닌 Member 같은 클래스에서 @Autowired 코드를 적용해도 아무 기능도 동작하지 않는다.
자동 주입 대상을 옵션으로 처리하는 방법
Test
java/com/example/springex1/autowired/AutowiredTest.java
package com.example.springex1.autowired;
import com.example.springex1.member.Member;
import jakarta.annotation.Nullable;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import java.util.Optional;
public class AutowiredTest {
@Test
void AutowiredOption(){
ApplicationContext ac = new AnnotationConfigApplicationContext(TestBean.class);
}
static class TestBean{
@Autowired(required = false)
public void setNoBean1(Member noBean1){
System.out.println("noBean1 = " + noBean1);
}
@Autowired
public void setNoBean2(@Nullable Member noBean2){
System.out.println("noBean2 = " + noBean2);
}
@Autowired
public void setNoBean3(Optional<Member> noBean3){
System.out.println("noBean3 = " + noBean3);
}
}
}
과거에는 수정자 주입과 필드 주입을 많이 사용했지만, 최근에는 스프링을 포함한 DI 프레임워크 대부분이 생성자 주입을 권장한다.
프레임워크 없이 순수한 자바 코드를 단위 테스트 하는 경우에 다음과 같이 수정자 의존관계인 경우
OrderServiceImpl를 수정자 의존관계(setter 의존관계)로 설정하고 테스트 진행
private MemberRepository memberRepository;
private DiscountPolicy discountPolicy;
// 수정자 주입(setter)
@Autowired
public void setMemberRepository(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
@Autowired
public void setDiscountPolicy(DiscountPolicy discountPolicy) {
this.discountPolicy = discountPolicy;
}
java/com/example/springex1/order/OrderServiceImplTest.java
package com.example.springex1.order;
import org.junit.jupiter.api.Test;
public class OrderServiceImplTest {
@Test
void createOrder() {
OrderServiceImpl orderService = new OrderServiceImpl();
orderService.createOrder(1L, "itemA", 10000);
}
}
테스트 결과
OrderServiceImpl를 생성자 중비을 사용하고 테스트 할려할때
TEST 코드
package com.example.springex1.order;
import com.example.springex1.discount.FixDiscountPolicy;
import com.example.springex1.member.Grade;
import com.example.springex1.member.Member;
import com.example.springex1.member.MemoryMemberRepository;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
public class OrderServiceImplTest {
@Test
void createOrder() {
MemoryMemberRepository memberRepository = new MemoryMemberRepository();
memberRepository.save(new Member(1L, "name", Grade.VIP));
OrderServiceImpl orderService = new OrderServiceImpl(memberRepository, new FixDiscountPolicy());
Order order = orderService.createOrder(1L, "itemA", 10000);
assertThat(order.getDiscountPrice()).isEqualTo(1000);
}
}
결과
final 키워드
수정자 주입을 포함한 나머지 주입 방식은 모두 생성자 이후에 호출되므로, 필드에 final 키워드를 사용할 수 없다. 오직 생성자 주입 방식만 final 키워드를 사용할 수 있다.
참고
김영한: 스프링 핵심 원리 - 기본편(인프런)
Github - https://github.com/b2b2004/Spring_ex