목표

1. 스프링은 크게 3가지 방법으로 빈 생명주기 콜백을 지원한다. 각 방식마다의 특징을 기억하자!
    -> 인터페이스, 설정 정보에 초기화 및 소멸 메서드 지정, 애노테이션.

'빈 생명주기 콜백' 목차

1. 빈 생명주기 콜백 시작

2. 인터페이스 InitializingBean, DisposableBean (이번 포스팅)

3. 빈 등록 초기화, 소멸 메서드 

4. 애노테이션 @PostConstruct, @PreDestroy


2.  인터페이스 InitializingBean, DisposableBean 

1) 초기화 콜백: InitializingBean 인터페이스를 구현하자.

들어가보면, afterPropertiesSet() 메서드가 있다. 
"프로퍼티들의 세팅이 끝나면, 즉 의존관계 주입이 끝나면" 호출하는 메서드라는 뜻이다. 

의존관계 주입 후, 초기화 메서드 afterPropertiesSet() 가 호출된다. 

2) 소멸 전 콜백: DisposableBean 인터페이스를 구현하자.

destroy() 메서드는 빈이 소멸될 때 호출된다. 

두 개의 인터페이스를 구현한 후의 코드. 

이전 시간에 작성한 NetworkClient 테스트코드를 실행하고 출력을 확인하자.

스프링 컨테이너를 생성하고 빈을 조회하고, 컨테이너를 종료하는 내용이었다. 

객체 생성(생성자 호출)때는 당연히 url이 값이 없다. 

초기화 콜백  : 의존관계 주입 직후, connect() , call() 이 호출됬다. 

소멸전 콜백  : 컨테이너 종료(빈 소멸) 할 때 close를 출력하는 distroy()가 호출됬다. 

 

3) 초기화, 소멸 인터페이스의 단점

  • 이 인터페이스는 스프링 전용 인터페이스다. 해당 코드가 스프링 전용 인터페이스에 의존한다.
  • 초기화, 소멸 메서드의 이름을 변경할 수 없다.
  • 내가 코드를 고칠 수 없는 외부 라이브러리에 적용할 수 없다. 
  • 인터페이스 방식은 2003년에 나온 방법이고, 지금은 더 나은 방법들이 있어서 거의 안 쓴다.

3. 빈 등록 초기화, 소멸 메서드 

설정 정보에 @Bean(initMethod = "메서드이름" , destroyMethod = "메서드이름") 으로 초기화, 소멸 메서드를 지정할 수 있다. 

1) 특징 

* 메서드 이름을 자유롭게 줄 수 있다.

* '설정 정보'를 사용하니까 외부 라이브러리에도 초기화, 소멸 메서드를 적용할 수 있다. 

 

2) 종료 메서드 추론 (inferred)

@Bean( destroyMethod = "메서드이름")에 특별한 기능이 있다. 

대부분 (외부)라이브러리의 종료 메서드 이름이 close, shutdown 임을 이용한 기능이다. 

@Bean 의 destroyMethod의 기본값이 (inferred) 로 등록되어 있다.

그래서 직접 스프링 빈으로 등록하면, close, shutdown 이름의 메서드를 자동으로 호출해준다. 

추론 기능을 사용하지 않으려면, @Bean( destroyMethod = "") 처럼 공백을 지정하면 된다.


4. [권장] 애노테이션 @PostConstruct, @PreDestroy 

내가 정의한 메서드에 애노테이션을 붙이는 방법이다. 편리하다. 

1) 특징 

* 최신 스프링에서 가장 권장하는 방법이다. 

* 패키지가 javax 로 시작하는데. 자바 표준이라는 의미다. 따라서 스프링이 아닌 다른 컨테이너에서도 잘 동작한다. 

* 유일한 단점은 외부 라이브러리에는 적용하지 못한다는 것이다. 

* 외부 라이브러리에는 @Bean(initMethod = "메서드이름") 방식을 쓰자. 

 


다음 시간에는 빈 스코프를 배운다. 

공부 내용 출처 :  스프링 핵심 원리 기본편 

728x90

목표

1. 스프링 빈이 생성되거나 소멸하기직전에 메서드를 호출해주는 간단한 기능이다. 
2. 스프링은 크게 3가지 방법으로 빈 생명주기 콜백을 지원한다. 각 방식마다의 특징을 기억하자!
    -> 인터페이스, 설정 정보에 초기화 및 소멸 메서드 지정, 애노테이션.

'빈 생명주기 콜백' 목차

1. 빈 생명주기 콜백 시작 (이번 포스팅)

2. 인터페이스 InitializingBean, DisposableBean

3. 빈 등록 초기화, 소멸 메서드 

4. 애노테이션 @PostConstruct, @PreDestroy


1.  빈 생명주기 콜백 시작

데이터베이스 커넥션 풀이나 네트워크 소켓처럼 tcp ip handshaking 등 연결을 위한 작업이 좀 많은 경우, 

미리 연결을 해두고 요청이 오면 연결을 바로 재활용 한다. 소켓도 미리 잡아놓아야 요청이 왔을 때 즉시 응답을 줄 수 있다. 

이런 건 애플리케이션 시작 시점에 필요한 연결을 미리 해둬야 한다.

DB 연결 종료와 소켓 닫는것도 애플리케이션 종료 직전에 해둬야 한다. 

이번 시간에는 스프링을 통해 객체의 초기화 작업과 종료 작업을 어떻게 진행하는지 예제로 알아보자. 

 

간단하게 외부 네트워크에 미리 연결하는 객체를 하나 생성한다고 가정해보자. 

실제 네트워크에 연결하는 건 아니고 문자만 출력한다. 

 

1) NetworkClient 클래스 

  • 애플리케이션 시작 시점에 connect() 를 호출해서 연결을 맺어두어야 한다.
  • 애플리케이션이 종료되면 disConnect() 을 호출해서 연결을 끊어야 한다. 

생성자에서 connect()를 호출하도록 했다. 

 

2) NetworkClient 클래스 테스트 코드 

 

1. 애플리케이션을 시작할 때 스프링 빈이 등록된다. 

    -> NetworkClient 클래스의 생성자에서 connect() 가 호출된다. 

// 스프링 컨테이너 생성
ConfigurableApplicationContext ac = new AnnotationConfigApplicationContext(LifeCycleConfig.class);

2. 애플리케이션 종료할 때 스프링 컨테이너의 close() 를 호출한다. 

ac.close();

 출력 결과는 아래와 같다. 

너무 당연한 얘기지만, 빈이 등록될 때 생성자를 호출하는데 이 때는 url 이 없으니까 url 이 null 로 출력된다. 


3) 스프링 빈의 라이프 사이클 

객체 생성 -> 의존관계 주입 

스프링 빈은 객체를 생성하고, 의존관계 주입이 끝난 다음에야 필요한 데이터를 사용할 수 있는 준비가 완료된다.

즉, 의존관계 주입이 끝나야 필드나 메서드를 변경하거나 사용할 수 있게 된다는 의미다. 필드를 수정하는 이러한 작업을 초기화 작업이라고 한다. 

객체 생성 -> 의존관계 주입 -> 초기화 작업 가능 시점 

 

4) 개발자는 초기화 작업을 할 수 있는 시점을 어떻게 알까 ?

스프링은 의존관계 주입이 끝나면, 스프링 빈에게 콜백 메서드를 통해서 초기화 시점을 알려준다. 

그리고 스프링 컨테이너가 종료되기 직전에, 소멸 콜백을 준다. 따라서 안전하게 종료 작업을 진행할 수 있다. 

 

스프링 빈의 이벤트 라이프사이클 

스프링 컨테이너 생성 -> 스프링 빈 생성 -> 의존관계 주입 -> 초기화 콜백 -> 사용
-> 소멸전 콜백 -> 스프링 종료 

 * 초기화 콜백 : 빈이 생성되고, 빈의 의존관계 주입이 완료된 후 호출되는 콜백.

 * 소멸전 콜백 : 빈이 소멸되기 직전에 호출되는 콜백.

 

5) 객체의 생성과 초기화를 분리하자 

생성자는 필수 정보만 파라미터로 받아서 메모리를 할당해서 객체를 생성하는 것만 해야 한다. 딱 생성하는 것에만 집중해야 한다. 

반면에 초기화는 외부 커넥션을 생성하는 등 무거운 동작을 수행한다. 

대부분의 경우, 둘을 합쳐놓는 것 보다는 객체 생성과 초기화를 분리하는 것이 유지보수 관점에서 좋다. 

물론 초기화 작업이 내부 값을 약간 변경하는 정도의 단순 작업이면 생성자에서 다 처리하는 것이 나을 수 있다. 

 

6) 객체의 생성과 초기화를 분리했을 때 장점

이런 것을 '지연' 이라고 하는데. 

예를 들어, DB 커넥션 풀을 애플리케이션 시작 할 때 100개 연결 생성만 만들어 둔다고 하자. 이 때 초기화는 안 한다. 
최초에 액션이 (
실제 요청이) 들어오면 초기화를 해서 사용하는 동작을 지연시키는 장점이 있다. 

 

[ 참고 ] 

스프링 컨테이너가 종료될 때 싱글톤 빈들이 함께 종료된다. 그래서 스프링이 종료될 때 소멸전 콜백이 일어난다. 

뒤에서 '스코프'를 설명할 때 자세히 배우겠지만, 생명주기가 짧은 빈들은 스프링 컨테이너 종료와는 무관하게 해당 빈이 종료하기 직전에 소멸전 콜백이 일어난다. 


스프링은 3가지 방법으로 빈 생명주기 콜백을 지원한다. 다음 시간에 배우자. 

공부 내용 출처 :  스프링 핵심 원리 기본편 

728x90

목표

1. 의도적으로 해당 타입의 스프링 빈이 전부 필요한 경우가 있다. 스프링으로 '전략 패턴'을 간단히 구현해보자.
2. "언제 수동 빈 등록이 필요할까?" 생각해본다. 

'의존관계 자동 주입' 목차

1. 다양한 의존관계 주입 방법 

2. 옵션 처리 

3. 생성자 주입을 선택해라!  

4. 롬복과 최신 트랜드

5. 조회 빈이 2개 이상 - 문제

6. @Autowired 필드 명, @Qualifier, @Primary

7. 애노테이션 직접 만들기 

8. 조회한 빈이 모두 필요할 때 List, Map (이번 포스팅)

9. 자동, 수동의 올바른 실무 운영 기준


8.  조회한 빈이 모두 필요할 때 List, Map에 담기 

고객이 주문할 때 할인 정책을 어떤 것을 이용할지(Rate or Fix) 선택할 수 있게 됬다. 

인터페이스 DiscountPolicy 를 구현한 클래스 RateDiscountPolicy와 클래스 FixDiscountPolicy 는 타입이 똑같다. 

타입이 같은 스프링 빈을 둘다 등록해서 사용해야 한다. 어떻게 해결할 수 있을까? 

 

1) AutoAppConfig.class -> 컴포넌트 스캔 및 Autowired 로 스프링 빈을 생성하고 의존관계를 주입한다. 

2) DiscountPolicy.class -> DiscountPolicy 타입의 스프링 빈을 등록한다. 

DiscountPolicy 타입들의 빈을 Map 에 담을 때 { 스프링 빈 이름 = 인스턴스 레퍼런스} 쌍으로 담겨있다. 

policyMap = 
{fixDiscountPolicy=hello.corebasic.discount.FixDiscountPolicy@5d1659ea, 
rateDiscountPolicy=hello.corebasic.discount.RateDiscountPolicy@793138bd}

DiscountPolicy 타입들의 빈을 List에 담을 때  [ 인스턴스 레퍼런스 , 인스턴스 레퍼런스, ... ]  로 담겨있다. 

policyList = 
[hello.corebasic.discount.FixDiscountPolicy@5d1659ea, 
hello.corebasic.discount.RateDiscountPolicy@793138bd]

3) 고객이 10000원 주문했다면, VIP 고객일 때 1000원의 할인금액이 계산되는지 확인하자. 

discount() 메서드 : 고객의 주문 금액과 할인코드 문자열을 입력받아서 할인금액을 리턴하는 메서드가 필요하다. 

3) DiscountService.class  에  discount() 메서드를 작성한다. 

"할인코드"에 대응하는 "스프링 빈"이름을 찾아서 인스턴스를 꺼낸다.

인터페이스 DiscountPolicy 에 선언된 discount() 메서드를 호출하여 할인금액을 계산하여 리턴한다. 

rate 할인 정책일 때와 fix 할인 정책일 때의 discount() 메서드 구현 내용은 다르다. -> 다형성을 기반으로 유연한 전략 패턴 제공

4) rate 할인 정책일 때와 fix 할인 정책에 맞게 할인금액을 계산하는지 테스트 코드를 작성해서 확인한다. 

 


9.  자동, 수동의 올바른 실무 운영 기준 

1) 편리한 자동 기능을 기본으로 사용하자. 

스프링은 점점 자동화를 선호하는 추세로 발전하고 있다. 최근에 범용으로 쓰는 스프링 부트는 컴포넌트 스캔을 기본으로 사용한다. 

설정 정보를 기반으로 애플리케이션을 구성하는 구분과, 실제 동작하는 부분을 명확하게 나누는 것이 이상적이긴 하다. 

하지만 개발자 입장에서는 일일히 @Configuration 설정 정보에 가서 @Bean 을 적고 객체 생성, 주입 하는 것은 고역이다. 프로젝트가 커지면 수 백개 클래스에 이 작업이 필요하다. 

그리고 결정적으로 자동 빈 등록을 사용해도 DIP와 OCP를 지킬 수 있다.

 

2) 수동 빈 등록은 언제 사용해야 좋을까? 

애플리케이션 개발해보면 비즈니스 업무 로직과 기술 지원 로직으로 나뉜다. 

* 업무 로직 빈
: 비즈니스 요구사항에 따라 변경된다.
웹을 지원하는 컨트롤러, 핵심 비즈니스 로직이 있는 서비스, 데이터 계층 로직을 처리하는 리포지토리. 

* 기술 지원 빈

: 기술적인 문제나 공통 관심사(AOP)를 처리할 때 주로 사용된다. 로직을 지원하기 위한 하부 기술이나 공통 기술. 
DB 연결이나 공통 로그 처리 등이 있다. 

애플리케이션에 광범위하게 영향을 주는 기술지원 객체는
수동 빈으로 등록해서 설정 정보에 딱! 티나게 나타내는 것이 유지보수 하기 좋다.

* 업무 로직 빈의 경우, 굉장히 개수가 많지만 어느 정도 유사한 패턴이 있다. -> 자동 빈 사용이 적절하다. 

* 기술 지원 빈의 경우, 업무 로직 빈에 비해 개수가 매우 적다. 하지만 애플리케이션 전반에 걸쳐서 광범위하게 영향을 미친다. 올바르게 동작하고 있는지 파악하기가 어려운 경우가 많다. -> 수동 빈 사용이 적절하다. 

 

3) 비즈니스 로직 중에서 다형성을 적극 활용할 때 수동 빈을 쓰자 

방금 전에 DiscountPolicy 인터페이스의 다형성을 기반으로 적절한 인스턴스를 쓰는 방법을 배웠다. 

하지만 실무에서 이러한 코드를 만나면, 인터페이스의 다형성을 이용한다는 점을 바로 알기 어렵다.

자동 등록을 사용하고 있기 때문이다. 파고 들어가야 한다. 
   " DiscountPolicy 관련이긴 한데 Map 은 왜 쓰지 ... (어리둥절) " 내가 할 땐 좋은데. 남이 해논 추상화가 힘들 때가 있다.

다형성을 적극 활용하는 경우에는 이 부분을 별도의 설정 정보로 만들고 수동 등록하자

   " DiscountPolicy 에는 rate와 fix가 있구나! " -> 한 눈에 들어온다. 유지보수에 훨씬 도움되는 구조!

그리고 반드시 특정 패키지에 같이 묶어둬야 한다. 

결론 :
자동을 하든 수동을 하든, 다형성을 사용할 땐 한눈에 들어오도록 신경써줘야 한다. 혼자 개발하는게 아니기 때문이다.


다음 강의에서는 빈 생명주기 콜백을 배운다. 

공부 내용 출처 :  스프링 핵심 원리 기본편 

728x90

목표

1. 애노테이션을 직접 만들어본다.
[참고 : 애노테이션을 모아서 사용하는 기능은 자바가 아니라 스프링이 제공하는 기능이다 ]

'의존관계 자동 주입' 목차

1. 다양한 의존관계 주입 방법 

2. 옵션 처리 

3. 생성자 주입을 선택해라!  

4. 롬복과 최신 트랜드

5. 조회 빈이 2개 이상 - 문제

6. @Autowired 필드 명, @Qualifier, @Primary

7. 애노테이션 직접 만들기 (이번 포스팅)

8. 조회한 빈이 모두 필요할 때, List, Map

9. 자동, 수동의 올바른 실무 운영 기준


 

7.  애노테이션 직접 만들기

지난 시간에 @Qualifier에 @Qualifier("mainDiscountPolicy") 이름을 지정해서 사용했다. 

그런데 컴파일러는 @Qualifier("mainnnDiscountPolicy")  라는 실수가 있는지 @Qualifier("nainDiscountPolicy")  인지 모른다.

이런 문자 실수는 컴파일 타임에 잡을 수 없다. 

 

@Qualifier 용도로 쓸 @MainDiscountPolicy 라는 애노테이션을 만들어서 해결하자. 

 

1) 애노테이션 파일 MainDiscountPolicy 를 생성한다. 

2) @Qualifier 에 있던 애노테이션들을 다 복사해서 애노테이션 파일 MainDiscountPolicy 에 붙여넣는다. 

Qualifier 용도의 애노테이션이니까 @Qualifier("mainDiscountPolicy") 도 넣어준다. 

3) rate할인정책에 main할인정책이라는 @Qualifier 애노테이션을 붙이고 싶다. 

4) 혹시 @MainnnDiscountPolicy 로 애노테이션 이름에 에러가 나면? 

바로 컴파일 에러가 난다. 

 

5) RateDiscountPolicy 를 의존관계로 주입하려는 클래스 OrderServiceImpl 코드를 열어보자. 

DiscountPolicy 에 @Qualifier 를 지정하되, 방금 만든 @MainDiscountPolicy를 쓰자. 

6) 로그를 보면 정상적으로 스프링 빈이 등록되고 의존관계도 주입됬음을 확인할 수 있다. 

Creating shared instance of singleton bean 'orderServiceImpl'
Autowiring by type from bean name 'orderServiceImpl' via constructor to bean named 'memoryMemberRepository'
Autowiring by type from bean name 'orderServiceImpl' via constructor to bean named 'rateDiscountPolicy'

OrderServiceImpl 싱글톤 빈이 생성됬고,

생성자를 통해 memoryMemberRepository와 rateDiscountPolicy 빈이 의존관계 주입이 됬다.


다음 강의에서는 조회한 빈이 모두 필요할 때 List, Map 으로 처리하는 방법을 배운다. 

공부 내용 출처 :  스프링 핵심 원리 기본편 

728x90

목표

1. 조회할 빈이 2개 이상일 때의 문제를 어떻게 처리해야 할지 배운다. 
2. @Autowired 필드 명을 매칭, @Qualifier, @Primary 사용법을 배운다. 

'의존관계 자동 주입' 목차

1. 다양한 의존관계 주입 방법 

2. 옵션 처리 

3. 생성자 주입을 선택해라!  

4. 롬복과 최신 트랜드

5. 조회 빈이 2개 이상 - 문제  (이번 포스팅)

6. @Autowired 필드 명, @Qualifier, @Primary

7. 애노테이션 직접 만들기

8. 조회한 빈이 모두 필요할 때, List, Map

9. 자동, 수동의 올바른 실무 운영 기준


5. 조회할 빈이 2개 이상일 때의 문제 해결하기

@Autowired타입으로 빈을 조회한다.

 

1) 만약 타입이 같은 빈이 2개가 조회되면 문제가 생길까? 

인터페이스 DiscountPolicy구현체 2개가 있다. RateDiscountPolicy와 FixDiscountPolicy 는 타입이 같다. 

RateDiscountPolicy 만 컴포넌트 스캔으로 스프링 빈 등록했었는데. FixDiscountPolicy도 스프링 빈 등록하려고 한다. 

스프링 빈 등록을 위해 컴포넌트 스캔을 하자.

2) orderServiceImple 에서 의존성 예외 발생했는데, DiscountPolicy 싱글 빈이 매칭되기를 기대했는데, 2개가 발견됬기 때문이다. 

beans.factory.UnsatisfiedDependencyException: 
Error creating bean with name 'orderServiceImpl' Unsatisfied dependency 
nested exception is NoUniqueBeanDefinitionException: 
No qualifying bean of type 'DiscountPolicy' available: 
expected single matching bean but found 2: fixDiscountPolicy,rateDiscountPolicy

@Autowired 로 의존관계를 주입하려고 DiscountPolicy 타입의 스프링 빈을 뒤져봤더니

1개 매칭을 기대했는데, 2개의 스프링 빈이 RateDiscountPolicy와 FixDiscountPolicy 발견되서 에러가 났다. 

 

3) 하위 타입인 fixDiscountPolicy 나 rateDiscountPolicy 를 적어야 할까? 아니다. 하위타입에 의존하면 DIP를 위반한다. 

다음 시간에 해결책을 배워보자. 


6. @Autowired 필드 명, @Qualifier, @Primary

타입이 같은 빈이 여러개 조회되면, 3가지 방법으로 해결할 수 있다.

 

1) @Autowired 필드 명을 매칭한다

@Autowired 의 스프링 빈 조회 순서->   1. 타입 매칭2. 필드 명 또는 파라미터 명을 매칭 

@Autowired 는 가장 먼저 스프링 빈의 타입을 매칭해보고. 빈이 2개 이상 조회되면 필드 명을 확인해서 매칭되는 하나를 조회한다. 

 

이전 코드 

@Autowired 필드 명을 매칭시키도록 변경한 코드 

 

2) @Qualifier 추가 구분자를 붙여주기

추가적인 방법을 제공하는 것이지 빈 이름을 변경하는건 아니다. 

 

예를 들어, RateDiscountPolicy 에 @Qualifier("mainDiscountPolicy") 를 지정하고, 

FixDiscountPolicy 에는 @Qualifier("fixDiscountPolicy") 를 지정한다. 

생성자 주입을 할 때 @Qualifier를 써주면 된다.

만약 해당 @Qualifier를 못찾으면 스프링은 그 이름으로 등록된 빈을 찾아본다. 

@Qualifier의 스프링 빈 조회 순서->   1.  @Qualifier 끼리 매칭,  2. 빈 이름 매칭 

@Qualifier 는 @Qualifier 를 찾는 용도로만 사용하는게 명확하고 좋다. 헷갈리게 하는 요소를 만들지 말자. 

 

3) @Primary 우선순위를 지정하기 [ 편하고 자주 사용한다 ] 

rate 할인정책을 항상 먼저 적용하고 싶다면, RateDiscountPolicy에 @Primary 를 붙인다. 

생성자에는 DiscountPolicy 타입 그대로 둔다. 

[ 참고 ] @Primary 는 언제 사용할까? 

예를 들어, 메인 DB connection 을 주로 사용하고, 백업용 DB connection 이 있는 상황에서,

메인 DB connection 에 @Primary 를 걸어두자고 팀내에서 룰을 지정할 수 있다. 


다음 강의에서는 애노테이션을 직접 만드는 방법 배운다. 

공부 내용 출처 :  스프링 핵심 원리 기본편 

728x90

목표

1. 생성자 주입을 선택해야하는 이유를 배운다. 
2. 롬복의 @RequiredArgsConstructor  를 써서 코드를 최적화하자. 

'의존관계 자동 주입' 목차

1. 다양한 의존관계 주입 방법 

2. 옵션 처리 

3. 생성자 주입을 선택해라!  (이번 포스팅)

4. 롬복과 최신 트랜드

5. 조회 빈이 2개 이상 - 문제

6. @Autowired 필드 명, @Qualifier, @Primary

7. 애노테이션 직접 만들기

8. 조회한 빈이 모두 필요할 때, List, Map

9. 자동, 수동의 올바른 실무 운영 기준


3. 생성자 주입을 선택해라! 

최근에는 스프링을 포함한 DI 컨테이너들은 생성자 주입을 권장한다. 그 이유는 다음과 같다. 

 

1)  불변 

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

   * 불변, 필수 의존관계에 사용한다. 

   * 애플리케이션을 종료할 때 까지 의존관계를 변경할 일이 있을까? 없다. 거의 없다. 오히려 불변해야한다. 

   * 수정자 주입을 사용하면, setXxx 메서드를 public으로 열어둬야 한다. 누군가 실수로 setXxx으로 변경할 수 있어서 위험한 설계다. 

2)  누락 

  * 생성자 주입을 사용하면 주입 데이터를 누락했을 때 바로 컴파일 에러를 낸다.

 가장 좋은 에러는 컴파일에러. 그리고 IDE에서 바로 어떤 값을 주입하라고 알려준다.

2)  final 키워드 

 * 생성자 주입을 사용하면 필드에 final 키워드를 사용할 수 있다. 혹시라도 생성자에 값이 주입되지 않은 오류를 컴파일 시점에 막아준다. 

 

A. 수정자 주입으로 테스트 코드 작성

setter로 (수정자 주입)을 사용하여 테스트 코드를 작성한 예시를 보자.

 OrderServiceImpl 에서 수정자 주입으로 의존관계를 주입했다. 

순수 자바 코드를 이용해서 OrderServiceImpl 의 '주문 요청'메서드를 테스트하자. 

문제 없이 동작할까? 테스트를 실행해보자. 

NullPointerException 이 발생한다. 

OrderServiceImpl 을 생성해서 테스트 코드를 작성하는 상황에서는 의존관계를 주입해야 하는 것을 발견하지 못하기 때문이다. 

set은 '객체가 생성된 후에' 의존관계 주입을 하는거니까. 생성시에는 별 문제가 안된다. 

 

B. 생성자 주입으로 테스트 코드 작성

OrderServiceImpl 의 의존관계를 '생성자 주입'으로 변경했다. 

테스트 코드로 돌아가보자. 코드에 빨간 밑줄이 벌써 떠있고 constructor 오류가 난다. 

세상에서 가장 좋은 오류 컴파일 오류.

 

C. [ final 키워드 시용시 ] 개발자가 생성자 주입에서 필드 하나 주입하는것을 잊어버린다면? 

바로 빨간줄 뜨고 자바 컴파일러가 생성자에 의존관계 주입하라고 컴파일 시점에 잡을 수 있다. 

결론 : 항상 생성자 주입을 사용하자. 


4. 롬복과 최신 트랜드 

막상 개발을 해보면 대부분이 다 불변이다. 그래서 생성자에 final 키워드를 붙이게 된다. 

그런데 생성자도 만들고, 주입 받는 코드도 만들고.. 영 반복 작업 같다. 

아래 OrderServiceImpl 을 최적화 해보자. 

1) 생성자가 딱 1개만 있으면 @Autowired  를 생략할 수 있다. 

 

2) 롬복 라이브러리를 적용해서 생성자를 자동으로 만들자. 

@RequiredArgsConstructor 가 final 이 붙은 필드를 가지고 생성자 코드를 그대로 만들어준다. 

[ lombok 적용 방법 ] 

lombok 의존성을 build.gradle 에 추가하자. 

intelliJ에 Annotation Processors 를 사용하도록 체크하자.

getter, setter 메서드를 직접 작성할 필요 없이, 클래스에 @Getter, @Setter 를 달아놓으면 자동으로 만들어준다. 

 

3) OrderServiceImpl 를 최적화한 결과 코드 


다음 강의에서는 조회 빈이 2개 이상일 때의 처리 방법을 배운다. 

공부 내용 출처 :  스프링 핵심 원리 기본편 

728x90

목표

1. 생성자, setter, 필드, 일반 메서드. 이렇게 의존관계 주입 방법 4가지를 배운다. 각 방법의 특징을 기억하자. 
2. 의존관계 주입을 옵션처리 하는 방법을 배운다. 

'의존관계 자동 주입' 목차

1. 다양한 의존관계 주입 방법  (이번 포스팅)

2. 옵션 처리 

3. 생성자 주입을 선택해라!

4. 롬복과 최신 트랜드

5. 조회 빈이 2개 이상 - 문제

6. @Autowired 필드 명, @Qualifier, @Primary

7. 애노테이션 직접 만들기

8. 조회한 빈이 모두 필요할 때, List, Map

9. 자동, 수동의 올바른 실무 운영 기준


1. 다양한 의존관계 주입 방법 

1) 생성자 주입

이름 그대로 생성자를 통해 의존 관계를 주입 받는 방법이다. 지금까지 해온 생성자에 @Autowired 를 붙이는 방식이다. 

특징

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

   * 불변, 필수 의존관계에 사용한다. 

 

# 여담 - 이렇게 생성자 주입으로 의존관계를 세팅하면 좋은 이유?

스프링 빈을 생성할 때 생성자를 호출하니까. 이 시점에 의존관계 주입을 1번 딱 세팅하고 시작하고 싶은 것이다. 

생성자 주입은 (빈 등록할 때 생성자를 호출하니까) 빈 등록과 의존관계 주입이 같이 일어난다.

개발할 때는 불변의 범위를 확실히 잡고가는 것이 중요하다. 

 

# 여담 -  웬만하면 생성자에는 값을 다 채워 넣어야 한다고 생각하는게 관례다.

따로 생성자에 null을 허용한다는 얘기가 없으면, 비워두지 말고 값을 꼭 넣자. 

 

[ 중요 ]

스프링 빈의 생성자가 1개인 경우, 생성자에 @Autowired 를 붙이지 않아도 생성자를 통해 의존관계가 주입된다! 

아래 두 코드는 똑같이 생성자 주입으로 의존관계가 주입된다. 

 

2) 수정자 주입(setter 주입)

필드 값을 변경하는 setter 메서드를 통해 의존관계를 주입하는 방법이다. 

setter 메서드는 변수명 앞에 set을 붙이는 것이 관례다. 아래 처럼 setter 메서드를 만들고  @Autowired를 붙인다. 

특징

   * 선택, 변경 가능성이 있는 의존관계에 사용한다. (사실 거의 변경할 일은 드물다)

단, setter로 주입하니까. private MemberRepository 변수에는 final(상수)를 붙이면 안된다. 

 

[ 참고 ]

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

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

[ 참고 ] 자바빈 프로퍼티 규약 

setXxx, getXxx 형태의 메서드로 필드 값을 수정하거나 읽자는 규약이다. 직접 필드값을 변경 하지 말자는 것이다. 

 

3) 필드 주입

이름 그대로 필드에 @Autowired 붙이면 의존관계를 빡 주입한다. 

하지만 필드 주입을 권장하지 않는다. 안티패턴이다.

특징

   * 코드가 간결해서 많은 개발자들을 유혹하지만 외부에서 변경이 불가능해서 테스트 하기 힘들다는 치명적인 단점이 있다. 

   * DI 프레임워크가 없으면 아무것도 할 수 없다. 순수한 자바코드로 제대로 테스트 할 수 없다. 

   * 애플리케이션의 실제 코드와 관계 없는 테스트 코드에서는 사용할 수도 있다. 

 

4) 일반 메서드 주입

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

특징 

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

   * 일반적으로 잘 안 쓴다. 

[ 참고 ]

어쩌면 당연한 이야기이지만 의존관계 자동주입은 스프링 컨테이너가 관리하는 스프링 빈이어야 동작한다.

 


2. 옵션 처리

주입할 스프링 빈이 없어도 동작해야 할 때가 있다. 자동 주입 대상을 옵션으로 처리하는 방법 3가지를 알아보자. 

 

Member 클래스는 스프링 빈이 아니다. 스프링 빈이 아닌 것을 @Autowired 하면, 자동 주입할 대상이 없는 상황인 것이다. 

이 때 스프링 컨테이너를 생성해서 빈 등록을 시도해보자. 

아래의 자동 주입 대상을 옵션으로 처리하는 방법을 사용하면 에러 없이 테스트가 성공한다. 

 

1) @Autowired (required = false)

@Autowired 는 기본으로 자동주입 대상이 없으면 에러를 낸다. 기본값이 (required = true) 이다. 

(required = false) 의미 : "자동 주입할 대상이 없으면(== 의존관계가 없으면), 수정자 메서드 자체를 호출하지 않는다. "

 

2) @Nullable

자동주입할 대상이 없으면 null이 입력된다.

3) 자바8의 Optional<>

자동주입할 대상이 없으면 Optional.empty 가 입력된다. 

 

스프링 빈이 아닌 것을 @Autowired 할 때, 자동 주입 대상을 옵션으로 처리하는 코드 

public class AutowiredTest { /** 의존관계 옵션 처리 */

    @Test
    void AutowiredOption(){
        AnnotationConfigApplicationContext 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);
        }
    }
}

 

[ 참고 ]

Optional<> 과 @Nullable은 스프링 전반에 전부 지원되는 기능이다. 


다음 강의에서는 생성자 주입을 선택해야 하는 이유를 배운다. 

공부 내용 출처 :  스프링 핵심 원리 기본편 

728x90

+ Recent posts