TIL

217일차(모험 126일차) - 다양한 의존관계 주입 방법

haedal-uni 2022. 4. 19. 22:26
728x90

다양한 의존관계 주입 방법

 

의존관계 주입은 크게 4가지 방법이 있다.

- 생성자 주입

- 수정자 주입(setter 주입)

- 필드 주입

- 일반 메서드 주입

 

 

 

 

생성자 주입

이름 그대로 생성자를 통해서 의존 관계를 주입 받는 방법

 

 생성자 호출시점에 딱 1번만 호출되는 것이 보장된다.

 불변, 필수 의존관계에 사용

@Component
public class OrderServiceImpl implements OrderService{

    // final이 있으면 값이 있어야 한다. - 필수
    private final MemberRepository memberRepository;
    private final DiscountPolicy discountPolicy; 


    // 불변
    @Autowired
    public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
        this.memberRepository = memberRepository;
        this.discountPolicy = discountPolicy;
    }
}

 

 

 

 

생성자가 딱 1개만 있으면 @Autowired를 생략해도 자동 주입 된다.

@Component
public class OrderServiceImpl implements OrderService{

    // final이 있으면 값이 있어야 한다. - 필수
    private final MemberRepository memberRepository;
    private final DiscountPolicy discountPolicy; 


    // 불변
    public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
        this.memberRepository = memberRepository;
        this.discountPolicy = discountPolicy;
    }
}

 

 

 

 

생성자가 2개 있으면 @Autowired를 지정해줘야한다.

@Component
public class OrderServiceImpl implements OrderService{
    private final MemberRepository memberRepository;
    private final DiscountPolicy discountPolicy; 


    // 생성자가 2개
    public OrderServiceImpl() {
    }

    @Autowired
    public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
        this.memberRepository = memberRepository;
        this.discountPolicy = discountPolicy;
    }
}

 

 

 

 

테스트 해보기

더보기

 

@Component
public class OrderServiceImpl implements OrderService{

    private final MemberRepository memberRepository;
    private final DiscountPolicy discountPolicy; 
 

    public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
        System.out.println("memberRepository = " + memberRepository); // test 해보기
        System.out.println("discountPolicy = " + discountPolicy);  // test 해보기
        this.memberRepository = memberRepository;
        this.discountPolicy = discountPolicy;
    }
}

 

 

AutoAppConfigTest를 실행시켜본다.

스프링 빈이 들어오면 null이 아닌 값이 있을 것이다.

 

 

 

값이 들어온 것을 확인할 수 있다.

 

 

 

* Autowired를 추가해도 값이 들어온다.

 

 

 

 

 

 

 


 

수정자 주입(setter 주입)

setter라 불리는 필드의 값을 변경하는 수정자 메서드를 통해서 의존관계를 주입하는 방법

 

선택, 변경 가능성이 있는 의존관계에 사용

   ▪ MemberRepository가 스프링 빈에 등록이 안되어있을 수도 있다. 그럴 때도 사용할 수 있다.

    선택적으로 의존관계 주입

    선택적으로 할 경우 @Autowired(required = false) 로 지정

 자바빈 프로퍼티 규약의 수정자 메서드 방식을 사용하는 방법

 

@Component
public class OrderServiceImpl implements OrderService {
    private MemberRepository memberRepository; // final 지움
    private DiscountPolicy discountPolicy;  // final 지움
    
    // = = = = 추가 = = = 
    @Autowired
    public void setMemberRepository(MemberRepository memberRepository) {
        System.out.println("memberRepository = " + memberRepository);
        this.memberRepository = memberRepository;
    }

    @Autowired
    public void setDiscountPolicy(DiscountPolicy discountPolicy) {
        System.out.println("discountPolicy = " + discountPolicy);
        this.discountPolicy = discountPolicy;
    }
    // = = = = = = = = 
    
    
    @Autowired
    public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
        this.memberRepository = memberRepository;
        this.discountPolicy = discountPolicy;
    }
}

 

 

AutoAppConfigTest 실행

 

값이 들어와 있는 것을 알 수 있다.

 

OrderServiceImpl을 스프링 컨테이너에 등록

스프링 컨테이너는 스프링 빈을 등록 → 연관관계를 자동으로 주입

@Autowired가 있는 것들을 자동으로 주입한다. 

 

 

관련 그림은 pdf 스프링 컨테이너의 생성 과정에 있는데

2번과 3번은 똑같은 그림이고

4번의 스프링 빈 의존관계 설정 그림이(의존관계 주입 단계) Autowired를 말한다.

 

 

생성자 주입은 빈을 등록하면서 의존관계 주입도 같이 일어난다.

(빈 등록할 때 자동 주입이 일어나는 것)

setter와 같은 경우들은 의존관계 주입 단계에서 일어나게 된다. 

 

@Component
public class OrderServiceImpl implements OrderService {
    private MemberRepository memberRepository; 
    private DiscountPolicy discountPolicy;  
    
    // = = = = 2 ~ 3번 (순서 보장x) = = = 
    @Autowired
    public void setMemberRepository(MemberRepository memberRepository) {
        System.out.println("memberRepository = " + memberRepository);
        this.memberRepository = memberRepository;
    }

    @Autowired
    public void setDiscountPolicy(DiscountPolicy discountPolicy) {
        System.out.println("discountPolicy = " + discountPolicy);
        this.discountPolicy = discountPolicy;
    }
    // = = = = = = = = 
    
    
    @Autowired // 1번
    public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
        System.out.println("1. OrderServiceImpl");
        this.memberRepository = memberRepository;
        this.discountPolicy = discountPolicy;
    }
}

 

AutoAppConfigTest

 

 

 

@Component
public class OrderServiceImpl implements OrderService {
    private MemberRepository memberRepository; 
    private DiscountPolicy discountPolicy;  
    
    @Autowired
    public void setMemberRepository(MemberRepository memberRepository) {
        System.out.println("memberRepository = " + memberRepository);
        this.memberRepository = memberRepository;
    }

    @Autowired
    public void setDiscountPolicy(DiscountPolicy discountPolicy) {
        System.out.println("discountPolicy = " + discountPolicy);
        this.discountPolicy = discountPolicy;
    }

    
//    @Autowired // 1번
//    public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
//        System.out.println("1. OrderServiceImpl");
//        this.memberRepository = memberRepository;
//         this.discountPolicy = discountPolicy;
//    }
}

 

아래 생성자는 필요 없으므로 주석처리 할 수 있다. (값 들을 다 주입해주기 때문에)

but, 기존 코드 때문에 오류가 날 수 있으므로 지우지는 않고 이번 강의에서만 주석처리했다.

 

 

 

 

선택적 의존관계 주입 

@Component
public class OrderServiceImpl implements OrderService {
    private MemberRepository memberRepository; 
    private DiscountPolicy discountPolicy;  
    
    @Autowired(required=false)
    public void setMemberRepository(MemberRepository memberRepository) {
        System.out.println("memberRepository = " + memberRepository);
        this.memberRepository = memberRepository;
    }
}

필수 값이 아니므로 있어도 되고 없어도 된다할 때 @Autowired(required = false) 사용

 

* @Autowired 의 기본 동작은 주입할 대상이 없으면 오류가 발생한다.

  주입할 대상이 없어도 동작하게 하려면 @Autowired(required = false) 로 지정하면 된다.

 

 

 

 

자바빈 프로퍼티 규약 예시

자바빈 프로퍼티, 자바에서는 과거부터 필드의 값을 직접 변경하지 않고,

setXxx, getXxx 라는 메서드를 통해서 값을 읽거나 수정하는 규칙을 만들었는데,

그것이 자바빈 프로퍼티 규약이다.

class Data {
    private int age;
    public void setAge(int age) {
        this.age = age;
    }
    
    public int getAge() {
        return age;
    }
}

필드가 있다.(age)

필드의 값을 바꿀 때는 set필드명, 가져올 때는 get필드명   * 앞에 대문자

 

 

 

 


 

필드 주입

이름 그대로 필드에 바로 주입하는 방법이다.

 

• 코드가 간결해서 많은 개발자들을 유혹하지만 외부에서 변경이 불가능해서

  테스트 하기 힘들다는 치명적인 단점이 있다.

 DI 프레임워크가 없으면 아무것도 할 수 없다.

사용하지 말자 (사용하는 경우는 아래와 같다.)

   ▪ 애플리케이션의 실제 코드와 관계 없는 테스트 코드

    스프링 설정을 목적으로 하는 @Configuration 같은 곳에서만 특별한 용도로 사용

 

 

@Component
public class OrderServiceImpl implements OrderService{

    // 필드 주입
    @Autowired private MemberRepository memberRepository;
    @Autowired private DiscountPolicy discountPolicy; 



    @Autowired
    public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
        this.memberRepository = memberRepository;
        this.discountPolicy = discountPolicy;
      }
}

 

 

AutoAppConfigTest를 실행하면 AppConfig에서 컴파일 에러가 뜬다.

 

AppConfig 코드

 

 

아래 코드로 수정한다.

@Bean
public OrderService orderService() {
    System.out.println("call AppConfig.orderService"); 
//    return new OrderServiceImpl(memberRepository(), discountPolicy());  //주석처리
     return null; // 오류가 나지 않게 null로 설정
}

 

 

 

실행하기 전에 AutoAppConfigTest에서 값을 잘 가지고 있는지 확인하는 코드 작성

public class AutoAppConfigTest {

    @Test
    void basicScan(){
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AutoAppConfig.class);
        MemberService memberService = ac.getBean(MemberService.class);
        assertThat(memberService).isInstanceOf(MemberService.class);

        /////////////  ⬇️  코드 추가    ///////////////
        OrderServiceImpl bean = ac.getBean(OrderServiceImpl.class);
        MemberRepository memberRepository = bean.getMemberRepository();
        System.out.println("memberRepository = " + memberRepository);
    }
}

 

 

잘 출력되는 것을 알 수 있다.

 

private임에도 불구하고 의존관계를 필드에 넣어준다.

 

 

* 필드 주입은 지양하는 방법이다.

 

 

 

 


 

일반 메서드 주입

• 일반 메서드를 통해서 주입 받을 수 있다.

 

 한번에 여러 필드를 주입 받을 수 있다.

 일반적으로 잘 사용하지 않는다.

 

@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가 있는 것

 

 

 

의존관계 자동 주입은 스프링 컨테이너가 관리하는 스프링 빈이어야 동작한다.

스프링 빈이 아닌 Member 같은 클래스에서 @Autowired 코드를 적용해도 아무 기능도 동작하지 않는다.

 

728x90

'TIL' 카테고리의 다른 글

219일차(모험 128일차)  (0) 2022.04.22
218일차(모험 127일차) - 옵션 처리  (0) 2022.04.20
216일차(모험 125일차) - 오류  (0) 2022.04.18
213일차(모험 122일차) - 웹소켓 삽질 중  (0) 2022.04.15
212일차(모험 121일차)  (0) 2022.04.14