Skip to content

Latest commit

 

History

History
644 lines (404 loc) · 31.9 KB

TIL220313.md

File metadata and controls

644 lines (404 loc) · 31.9 KB

Daily to do list

Java


Spring


CS


알고리즘


오늘의 회고

의존관계 자동 주입

————————— 다양한 의존관계 주입 방법

의존관계 주입은 크게 4가지 : 생성자 주입, 수정자 주입(setter), 필드 주입, 일반 메서드 주입

  • 생성자 주입 이름 그대로 생성자를 통해서 의존 관계 주입.

특징 : 생성자 호출 시점에서 딱 1번 호출되는 것이 보장! 불변, 필수 의존관계에 사용 (예) 공연을 시작하기 전에 배우들을 모두 정하고 공연을 시작하는 것!! 공연하는 중간에 배우들을 못바꾸게 하고 싶은것

불변이 필요한 것이라면 수정자를 아예 만들지 말자..

?? final 은 최종적이라는 뜻으로 초기값이 저장되면 도중에 수정을 할 수 없다. + 값이 무조건 있어야 한다는 뜻, 객체자체는 변경 불가능, 객체 내부 변수는 변경 가능

final 필드 : 초기값을 줄 수 있는 방법이 2가지 필드 선언시, 생성자 (초기화 안되어있으면 컴파일 에러) final 객체 : 객체자체는 변경 불가능하지만 객체 내부 변수는 변경 가능 + 초기화가 안되어 있으면 컴파일 에러 final 클래스 : 상속이 불가능한 클래스 필드는 Setter를 통해 변경 가능 final 메서드 : 상속받은 클래스에서 부모의 final 메서드를 재정의 할 수 없다.(시스템 코어) final 메서드 인자값 : 인자값 변경 불가능(잘사용은 안함)

final : 생성자, 초기값 지정 안하면 컴파일 에러(필수로 값을 받고 싶을 때), 메서드에 쓰면 오버라이드 불가능(시스템 코어 등 바뀌면 안되는 것들)

필수에 사용하는 이유 : 생성자가 필수로 필요한 인자를 받아서1?!

**** 중요!!! 생성자가 딱 1개만 있으면 @Autowired를 생략해도 됨

  • 수정자 주입 필드명에 set을 앞에 붙이는 것을 보통 수정자(setter)라고 함. 필드값을 그냥 수정할수는 없으니 수정자 이용

특징 : 선택적(수정자 부를 때! 의존관계를 주입하니), 변경 가능(잘 사용하진 않을듯 객체 지향 위반)

참고 : @Autowired의 기본 동작은 주입할 대상이 없으면 오류가 발생, 주입할 대상이 없어도 동작하게 하려면 @Autowired(required = false) 로 지정

참고 2: 자바빈 프로퍼티, 자바에서는 과거부터 필드의 값을 직접 변경하지 않고 set,get라는 메서드를 통해서 값을 읽거나 수정하는 규칙을 만들었다. 그것이 자바빈 프로퍼티 규약. 쓰는 이유가 좀 궁금한데..

참고 3 스프링 컨테이너의 라이프 사이클 :

  1. 스프링 컨테이너 생성
  2. 스프링 빈 등록
  3. 스프링 빈 의존관계 설정 - 준비
  4. 스프링 빈 의존관계 설정 - 완료 ** 생성자 주입의 경우 스프링 빈 등록과 함꼐 이루어짐(어쩔 수 없는 생성자..호출 때문에)
  • 필드 주입 ( 안씀.., 테스트 코드에서만 가끔 씀) 그냥 바로 필드에서 @Autowired 박는거… 의존관계를 그냥 박음 private여도 가능;; 외부에서 변경이 불가능해서 테스트하기 힘들다.

  • 일반 메서드 주입 그냥 아무 메서드에서나 해놓고 @Autowired를 붙이는 방법… 생각해보면 수정자 주입이랑 비슷, 거의 사용하지 않음..

———————————— 옵션처리

주입할 스프링 빈이 없어도 동작해야 할 때가 있다..!

@Autowird가 붙은 애노테이션 객체를 봤는데 스프링 빈이 아니면?! 이라는 이야기였음..

@Autowired(required = false) >>>> 대상이 없으면 수정자 메서드 자체가 호출x @Nullable >>> null Optional<> >>>> Optional.empty로 출력됨

참고 : @Nullable, Optional은 스프링 전반에 걸쳐서 지원된다..

———————————— 왜 생성자 주입을 선택해야 하는가?!

과거에는 수정자 주입과 필드 주입을 많이 사용했지만! 최근에는 스프링을 포함한 DI 프레임워크 대부분이 생성자 주입을 권장한다!!

  • 불변 대부분의 의존관계 주입은… 한번! 일어나면 어플 종료까지 의존관계 변경할 일이 없다.. (오히려 대부분의 의존관계는 종료 전까지 변하면 안된다. 불변해야 한다..!)

수정자 주입을 사용하면, setter를 열어놔야 한다.

즉, 생성자 주입은 객체를 생성할 떄 딱 1번만 호출되므로 이후에 호출될 일이 없다!. 불변

  • 누락 프레임워크 없이 순수한 자바 코드를 단위 테스트 하는 경우에

생상자 주입을 할 경우에는 단위 테스트를 진행하면서도 혹여나 생성자 인자를 안넣을 경우에 컴파일 에러가 발생한다. 하지만 수정자로 넣거나 할 경우 직접 객체에 들어가서 필요한 인자들을 확인해야 한다.

또 좋은거 !!! 생성자 주입을 하면 final로 객체를 만들 수 있음 그러면 생성자에서 값을 안넣으면 컴파일 에러가 뜨고 이렇게 하면 누락이 되는 경우를 방지 final 은 생성자에서 값 주입 해야만 가능 즉 생성자 주입을 해주면 가능

즉, final에다가 생성자 주입을 하면 누락을 막아준다.

컴파일 오류가 세상에서 가장 빠르고 좋은 오류이다!!

참고, 수정자 주입을 포함한 나머지 주입 방식은 모두 생성자 이후에 호출되므로, 필드에 final 키워드를 사용할 수 없다 오직 생성자 주입만 final키워드 사용 가능

  • 정리 생성자 주입 방식을 선택하는 이유는 여러가지가 있지만, 프레임워크에 의존하지 않고, 순수한 자바 언어의 특징을 잘 살리는 방법이기도 함.

기본으로 생성자 주입을 사용하고 필수 값이 아닌 경우에는 수정자 주입 방식을 옵션으로 부여하면 된다.

결론:!? 항상 생성자 주입을 선택해라!! 그리고 가끔 옵션으로 선택자 주입을 선택하고 필드 주입은 절대 쓰지마라…

———————————— 롬복과 최신 트랜드

막상 개발해보면 대부분 불변, final키워드에 생성자를 쓴다 근데 이렇게 생성자 만들고 주입 받는 값을 대입하는 코드 만들어야 하는 이것조차.. 귀찮았던 사람들이 “필드주입”처럼 편리하게 사용하는 방법을 고민하다가 만든 것

롬복 = 게터 세터 등 어노테이션도 가능하고 뭐 생성자 등등 다양하게 사용하는데 많이 씀 @RequiredArgsConstructor… 이건 final 붙은 생성자를 자동으로 만들어 줌;;;;

  • 정리 최근에는 생성자를 딱 1개 두고, @Autowired 생락을 주로ㅅ 씀 여기다가 롬복 의 @RequiredArgsConstructor 까지 쓰면.. 진짜 짧아짐

—————————————— 조회 빈이 2개 이상이면 - 문제

@Autowired 는 타입으로 조회를 한다

마치 ac.getBean(DiscountPolicy.class) 이런식으로 타입으로 조회를 함

자동으로 주입할 경우 2개 이상 즉 discount에서 fix,rate 객체를 두개 다 @Component로 하면 타입으로 조회하기 때문에 두개가 선택되게 된다..

즉, 자동 의존관계 주입으로 하면 타입으로 검색하기 때문에 두 개 이상일 경우는 자동 의존관계 주입으로 하면 안되는 것이다. NoUniqueBean

이때 하위 타입으로 지정할 수도 있지만, 하위 타입으로 지정하는 것은 DIP를 위배(객체를 선택하게 됨)하고 유연성이 떨어진다. 이름만 다르고 완전히 똑같은 타입의 스프링 빈이 2개 있을 때 해결이 안된다.

스프링 빈을 수동으로 등록해서 문제를 해결해도 되지만, 자동 주입으로 해결할 수 있는 여러가지 방법이 있다!

—————————————— @Autowired 필드 명, @Qualifier, @primary

위 문제 해결 방법

  • @Autowired 필드 명 매칭

  • @Quilifier >> @Quilifier 끼리 매칭 >> 빈 이름 매칭

  • @Primary 사용

  • @Autowired 필드 명 매칭 @Autowired는 타입 매칭을 시도, 이 때 여러 빈이 있으면 필드 이름이나 파라미터 이름으로 빈을 추가 매칭

필드 명을 빈 이름으로 변경 : @Autowired private DiscountPolicy rateDiscountPolicy

이런 식으로 필드 이름을 빈 이름으로 변경

정리 :

  1. 타입 매칭
  2. 타입 매칭 결과가 2개 이상일 때 필드 명, 파라미터 명으로 빈 이름 매칭
  • @Quilifier 사용 추가 구분자를 붙여주는 방법, 주입 시 추가적인 방법을 제공하는 것이지만 빈 이름 변경은 아님

 이런식으로 애노테이션을 붙이면 됨

만약에 못찾으면??? 애노테이션 안의 이름의 스프링 빈을 추가로 찾는다. 하지만…. 명확하게 사용하는 것이 좋다 이렇게 해서는 안된다!

그래도 안되면 예외가 발생(NoSuchBeanDefinitionException

 이렇게 빈 등록시에도 사용 가능

  • @Primary 사용

자주 사용 @Primary 는 우선순위를 정하는 방법

 이런식으로 애노테이션 하나만 붙이면 가장 최우선 순위로 생각하고 실행

정리: 여기까지 보면 @Primary, @Quilifier 중 어떤 것을 사용하면 좋을지 고민이 된다. @Quilifier의 단점은 주입 받을 때 모든 코드에 붙여줘야한다는 점이다.

 @Primary 보다 @Quilifier가 우선순위가 높다

——————————————— 애노테이션 직접 만들기

애노테이션은 상속이라는 개념이 없다. 이렇게 여러 애노테이션을 모아서 사용하는 기능을 스프링이 지원해주는 기능이다. 조합해서도 쓰고 재정의할수도 있다. 근데 뚜렷한 목적 없이 하는 것은 혼란만 더 만든다

————————————— 조회한 빈이 모두 필요할 떄, List, Map

의도적으로 해당 타입의 스프링 빈이 다 필요한 경우도 있다.

(예) 할인 서비스를 제공하는데, 클라이언트가 할인의 종류를 선택할 수 있다고 가정하면 스프링을 사용하면 소위 말하는 전략 패턴을 매우 간단하게 구현 가능

DiscountService는 Map으로 모든 DiscountPolicy를 주입 받는다.

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

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

결론으론 스프링이 나오고 시간이 갈수록 점점 자동을 선호하는 추세. 스프링은 @Component 뿐만 아니라 @Controller, @Service, @Repository 처럼 계층에 맞추어 일반적인 애플리케이션 로직을 자동으로 스캔할 수 있도록 지원한다. 거기에 더해 최근 스프링 부트는 컴포넌트 스캔을 기본으로 사용하고, 스프링 부트의 다양한 스프링 빈들도 조건이 맞으면 자동으로 등록하도록 설계

실무에서) 개발자 입장에서 스프링 빈을 하나 등록할 때! @Component만 넣어주면 끝나는 일을 @Configuration 설정 정보에 가서 @Bean을 적고… 객체를 생성하고.. 주입할 대상을 일일이 적어주는 과정이 상당히 번거롭다.

또 관리할 빈이 많아서 설정 정보가 커지면 설정 정보 관리하는 것 자체가 부담이 된다.

그리고 아주 결정적 ******* 자동 빈 등록을 해도 OCP, DIP를 지킬 수 있다.

그렇다면 수동 빈 등록은 언제 사용?????

애플리케이션은 크게 업무 로직과 기술 지원 로직으로 나눌 수 있다. 업무 로직 빈 : 웹을 지원하는 컨트롤러, 핵심 비즈니스 로직이 있는 서비스, 데이터 계층의 로직을 처리하는 리포지토리 등… 모두 업무 로직이다. 보통 비즈니스 요구사항을 개발할 때 추가되거나 변경된다.

기술 지원 빈 : 기술적인 문제나 공통 관심사(AOP)를 처리할 때 주로 사용된다. 데이터베이스 연결, 공통 로그 처리, 하부 기술이나 공통 기술들

업무 로직은 숫자도 매우 많고, 한번 개발해야 하면 컨트롤러, 서비스 리포지토리 처럼 어느 정도 유사한 패턴이 있다. 이런 경우 자동 기능을 적극 사용하는 것이 좋다. 보통 문제가 발생해도 어떤 곳에서 문제가 발생했는지 명확하게 파악하기 쉽다.

기술 지원 로직은 업무 로직과 비교해서 그 수가 매우 적고, 보통 매플리케이션 전반에 걸쳐서 광범위하게 영향을 미친다. 그리고 업무 로직은 문제가 발생햇을 때 어디가 문제인지 명확하게 잘 들어나지만, 기술 지원 로직은 적용이 잘 되고 있는지 아닌지 조차 파악하기 어려운 경우가 많다. 그래서 이런 기술 지원 로직들은 가급적 수동 빈 등록을 사용해서 명확하게 들어내는 것이 좋다.

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

비즈니스 로직 중에서 다형성을 적극 활용할 때도 수동 빈 등록이 좋다!!

의존관계 자동 주입으로 주입을 받는 경우를 보면 각 빈들의 이름은 무엇인지 코드만 보고 한번에 쉽게 파악할 수 있을까?;; 힘들다 이럴 때 수동 빈으로 등록하거나 자동으로 하면 ** 특정 패키지에 같이 묶어 두는게 좋다.  즉, 이렇게 수동처럼 한 눈에 딱 보고 이해가 되어야 한다.

정답은 없다 하지만 한눈에 볼 수 있게 혹은 편하게 등 명확한 목적에 맞추면 될 것 같다!

참고로 “스프링과 스프링 부트가 자동으로 등록하는 수많은 빈들은 예외” 이런 부분들은 스프링 자체를 이해하고 스프링의 의도대로 사용하는게 중요

혹시나 스프링 부트가 아니라 내가 직접 기술 지원 객체를 스프링 빈으로 등록한다면 수동으로 등록해서 명확하게 들어내는 것이 좋다!!!

  • 정리
  1. 편리한 자동 등록 기능을 기본으로 사용하자
  2. 직접 등록하는 기술 지원 객체는 수동으로 등록
  3. 다형성을 적극 활용하는 비즈니스 로직은 수동 등록을 고민해보자!

빈 생명주기 콜백 —————————— 빈 생명주기 콜백 시작

데이터베이스 커넥션 풀이나(미리 서버와 데이터베이스를 연결.), 네트워크 소켓처럼 애플리케이션 시작 시점에 필요한 연결을 미리 해두고, 애플리케이션 종료 시점에 연결을 모두 종료하는 작업을 진행하려면,

객체의 초기화와 종료 작업이 필요하다.

스프링을 통해 이러한 초기화작업, 종료 작업은 어떻게 진행되는가?!

스프링 빈은 간단하게 다음과 같은 라이프사이클을 가진다. 객체 생성 >>> 의존관계 주입 (생성자 주입은 어쩔 수 없이 객체 생성과 동시에 이루어짐)

스프링 빈은 객체를 생성하고 의존관계 주입이 다 끝난 후에 필요한 데이터를 사용할 수 있는 준비가 완료된다. 즉, 초기화 작업은 객체 생성과 의존관계 주입까지 모드 완료된 후 호출해야 한다. 그런데 개발자가 이 의존관계 주입까지 끝난 시점을 알기는 힘들다.

그래서!!! 스프링은 의존관계 주입이 완료되면 스프링 빈에게 콜백 메서드를 통해서 초기화 시점을 알려주는 다양한 기능을 제공, 또한 스프링은 스프링 컨테이너가 종료되기 직전에 소멸 콜백을 준다.

스프링 빈의 이벤트 라이프 사이클 스프링 컨테이너 생성 >> 스프링 빈 생성 >> 의존관계 주입 >> 초기화 콜백

코드 사용 >> 소멸전 콜백 >> 스프링 종료

초기화 콜백 : 빈이 생성되고, 빈의 의존관계 주입이 완료된 후 호출 소멸전 콜백 : 빈이 소멸되기 직전에 호출

스프링은 다양한 생명 주기 콜백을 지원

참고 : 객체의 생성과 초기화를 분리하자. (단일 책임 원칙?에 맞지 않나)

  • 생성자는 필수 정보(파라미터)를 받고, 메모리를 할당해서 객체를 생성하는 책임을 가진다.
  • 초기화는 이렇게 생성된 값들을 활용해서 외부 커넥션을 연결하는 등 무거운 동작을 수행

즉, 생성자 안에서 무거운 초기화 작업을 함께 하는 것보다는 객체를 생성하는 부분과 초기화 하는 부분을 명확하게 나누는 것이 유지보수 관점에서 좋다. 물론 초기화가 단순하면 생성자가 한번에 해도 나을 수 있다.

———————————— 스프링은 크게 3가지 방법으로 빈 생명주기 콜백을 지원

  • 인터페이스
  • 설정 정보에 초기화 메서드, 종료 메서드 지정
  • 애노테이션 지원 ( @PostConstruct, @PreDestory) ————————————
  • 인터페이스  InitializingBean, DisposableBean을 인터페이스로 받으면 afterPropertiesSet, destroy 메서드를 사용할 수 있음

단점… : 이것은 스프링 전용 인터페이스라 스프링 전용 인터페이스에 의존 초기화, 소멸 메서드의 이름 변경 불가 내가 코드를 고칠 수 없는 외부 라이브러리에 적용할 수 없다.

참고 : 인터페이스 초기화는 스프링 초기에 나온 것으로 잘 사용 안함

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

설정 정보에 @Bean(initMethod = “init”, destroyMethod = “close”)  하면 된다.

설정 정보 사용 특징: 메서드 이름을 자유롭게 할 수 있다. 스프링 빈이 스프링 코드에 의존하지 않는다. 코드가 아니라 설정 정보를 사용하기 때문에 코드를 고칠 수 없는 외부 라이브러리에도 사용 가능 (제일 큰 장점)

종료 메서드 추론: @Bean의 destroyMethod 속성엔 특별한 기능이 있따. 라이브러리 대부분 close, shutdown이라는 이름의 종료 메서드를 사용한다. @Bean의 destroyMethod는 기본값이 (interred)추론 으로 등록되어 있다. 이 기능은 close, shutdown 이라는 이름의 메서드를 자동으로 호출 해준다.

따라서 직접 스프링 빈으로 등록하면 종료 메서드는 따로 적어주지 않아도 잘 동작한다. 추론 기능이 싫으면 destroyMethod=“” 처럼 빈 공백을 지정하면 된다…하지만 쓰진않는다

——————————————— 애노테이션으로 설정 (이걸 쓰면 된다 ㅎㅎ.) @PostConstruct, @preDestroy

최신 스프링에서 가장 권장하는 방법 애노테이션만 붙이면 된다 편리 패키지를 보면 javax 즉 스프링 종속이 아닌 자바, 스프링이 아닌 다른 컨테이너에서도 동작 컴포넌트 스캔과도 잘 어울린다.

유일한 단점: 외부 라이브러리에 적용 못함 혹시 외부 라이브러리를 초기화, 종료 해야 하면 @Bean 기능을 사용해야 한다

  • 정리 @PostConstruct, @PreDestroy를 사용하면 된다 ㅋ; 코드를 고칠 수 없는 외부 라이브러리를 초기화 종료해야하면 @Bean 의 initMethod, destroyMethod를 사용하면 된다!!!

—————————————— 빈 스코프란?

———————— 빈 스코프

지금까지 우리는 스프링 빈이 스프링 컨테이너의 시작과 함께 생성, 종료까지 유지로 학습함 이것은 스프링 빈이 기본적으로 싱글톤 스코프로 생성되기 떄문이다. 스코프란 말은 말 그대로 빈이 존재할 수 있는 범위

스프링은 다양한 스코프를 지원함 싱글톤 : 기본 스코프 시작과 종료까지

프로토타입 : 초기화까지 불러주고 클라이언트에게 반환하고 끝 (종료 메서드 호출x)

웹 관련 스코프 : request : 웹 요청이 들어오고 나갈때까지 유지 session : 웹 세션이 생성되고 종료될 때까지 유지 application : 웹의 서블릿 컨텍스와 같은 범위로 유지

 이렇게 컴포넌트 위에 자동등록 가능, 빈 위에 수동 등록도 가능

———————————— 프로토타입 스코프

싱글톤 스코프의 빈을 조회하면 스프링 컨테이너는 항상 같은 인스턴스의 스프링 빈을 반환. 하지만 프로토 타입은 항상 새로운 인스턴스를 생성해서 반환!

프로토 타입  ***요청을 받으면 그 때 생성해서 DI + 초기화까지 하고!

생성된 빈을 반환이나 관리하지 않음

즉, 똑같이 요청을 해도 항상 새로운 프로토 타입 빈을 반환한다.

정리 : 핵심은 스프링 컨테이너는 프로토타입 빈을 생성하고, 의존관계 주입, 초기화까지만 처리한다.

프로토 타입 빈을 관리할 책임은 프로토타입 빈을 받은 클라이언트에 있다. ** 그래서 @PreDestroy같은 종료 메서드가 실행되지 않는다.

프로토타입 빈은 스프링 컨테이너가 생성과 의존관계 주입 그리고 초기화까지만 관여함

  • 프로토타입 빈 특징 정리 **스프링 컨테이너에 요청할 때 마다 새로 생성된다. 스프링 컨테이너는 프로토타입 빈의 생성과 의존관계 주입 그리고 초기화까지만 관여 종료 메서드가 호출되지 않는다 그래서 프로토타입 빈은 프로토타입 빈을 조회한 클라이언트가 관리해야 한다.

———————————————————— 프로토타입 스코프 - 싱글톤 빈과 함께 사용시 문제점…..

섞어서 쓰면 생각한대로 잘 안된다..고함

싱글톤 빈 안에다가 프로토타입 빈을 생성할 경우 싱글톤 빈을 생성할 떄 프로토타입 빈 객체를 생성하기 때문에 다시 싱글톤 빈을 요청할 때 프로토타입 빈은 이전 것을 사용, 즉, 프로토타입이 아닌 싱글톤 타입처럼 동작하는 것.. 으로  아마 원한 것이 보통 이렇게 하는거게 아닐 것이다. 프로톤 빈을 주입 시점에만 새로 생성하는 것이 아니라 사용할 때 마다 새로 생성해서 사용하기를 원할 것이다. (안그러면 그냥 전부 다 싱글톤 빈에 싱글톤 빈을 할당 받아서 썻을 것..;)

스프링은 일반적으로 싱글톤 빈을 사용, 싱글톤 빈이 프로토타입 빈을 사용하게 된다. 그런데 싱글톤 빈은 생성 시점에만 의존관계 주입을 받아, 프로토타입 빈이 새로 생성되기는 하지만 싱글톤 빈과 함께 계속 유지되는 것이 문제.

참고 : 여러 빈에서 같은 프로토타입 빈을 주입 빋으면, “주입 받는 시점에 각각 새로운 프로토타입 빈이 생성”, 하지만 물론 사용할 때마다 생성되는 것은 아님.

—————————————————— 프로토타입 스코프 - 싱글톤 빈과 함꼐 사용시 Provider로 문제 해결

해결방법(쓰면안되지만)은 로직을 호출할 때마다 새로운 프로토타입 빈을 getBean을 통해서 만드는 것, 이렇게 만드는 것은 의존관계 주입이 아니라 직접 필요한 의존관계를 찾는 것을 Dependency Lookup(DL)의존관계 조회 라고 한다.

근데.. 이렇게 전체를 주입 받으면,, 종속적인 코드가 됨, 단위 테스트도 어려워진다.

지금 필요한 기능은 프로토타입 빈을 컨테이너에서 대신 찾아주는 딲!”DL” 정도의 기능만 제공해주면 됨.

ObejctProvider,ObjectFactory : ObjectFactory가 있었는데, 여기에 편의 기능을 추가해서 ObejctProvider가 나옴 정확히 찾아주는 기능만. 즉 프로토타입을 찾아서 연결해주는 역할을 함 그러면 프로토타입을 불러오기 때문에 새로운 프로토타입 빈이 생성되어서 클라이언트 빈으로 들어가게 됨.

이 기능은 프로토타입 빈을 찾는 전용 그런 명령은 아니지만 ObejctProvider을 호출하면 내부에서는 스프링 컨테이너를 통해 해당하는 빈을 찾아서 반환하는 기능임 해당하는 빈을 찾아서 기능을 반환하는 기능 (DL)

스프링이 제공하는 기능을 사용하지만, 기능이 단순하므로 단위테스트를 만들거나 mock코드를 만들기 훨씬 쉽다.

특징 : ObjectFactory: 기능 단순, 별도의 라이브러리 필요 없음, 스프링에 의존 ObjectProvider: ObjectFactory 상속, 옵션, 스트림 처리, 별도의 라이브러리 필요 x 스프링 의존

JSR-330 Provider javax.inject.Provider라는 JSR-330 자바 표준을 사용하는 방법.. 이 방법을 사용하려면 javax.inject:javax.inject를 gradle에 추가해야 함..

실행해보면, provider.get() 을 통해서 항상 새로운 프로토타입 빈이 생성됨 get()을 호출하면 내부에서는 스프링 컨테이너를 통해 해당 빈을 찾아서 반환(“DL”)

특징 : get()메서드 하나로 기능이 단순 별도의 라이브러리가 필요 자바 표준이므로 스프링이 아닌 다른 컨테이너에서도 사용 가능

정리 : 프로토타입 빈은 언제 사용할까? 매번 사용할 때 마다 의존관계 주입이 완료된 새로운 객체가 필요하면 사용하면 된다. 그런데……… 실무에서 웹 애플리케이션을 개발하다보면, 싱글톤 빈으로 대부분의 문제가 해결 ObjectProvider, JSR330 Provider 등은 프로토타입 뿐만 아니라 DL이 필요한 경우 언제든 사용 가능

스프링 기능과 자바 표준 기술 중 선택하는 기준.. jpa는 하이버네이트 개발자를 데려와서 jpa 표준이 하이버네이트가 되고 jpa 표준이 승리한 모습.. 하지만 지금의 스프링과 자바는 대부분 스프링 기술을 사용하는 편 (자바 표준이 조금 더 불편해서) 그래서 지금 같은 경우는 무엇을 사용해도 상관은 없지만 장단점을 생각하고 기술을 선택해야함.

————————————— 웹 스코프

웹 환경에서만 동작 프로토타입과 다르게 스코프의 종료시점까지 관리  웹 스코프 종류

  • request : HTTP 요청 하나가 들어오고 나갈 떄까지 유지되는 스코프, 각각의 HTTP 요청마다 별도의 빈 인스턴스가 생성되고, 관리된다.
  • session: “HTTP Session”과 동일한 생명주기를 가지는 스코프
  • application: “서블릿 컨텍스트(ServletContext)”와 동일한 생명주기를 가지는 스코프
  • websocket: 웹 소켓과 동일한 생명주기를 가지는 스코프

———————————— request 스코프 예제

웹 환경 추가

웹 스코프는 웹 환경에서만 동작, 라이브러리 추가

참고 : 'org.springframework.boot:spring-boot-starter-web' 라이브러리 추가 시 스프링 부트는 내장 톰켓 서버를 활용해서 웹 서버와 스프링을 함께 실행.

참고 : 스프링 부트는 웹 라이브러리가 없으면 지금까지 했던 ‘AnnotationConfigApplicationContext’를 기반으로 애플리케이션을 구동한다. 근데 웹을 추가하면 웹과 관련된 추가 설정과 환경들이 필요하므로 ‘AnnotationConfigServletWebServerApplicationContext’를 기반으로 구동

  • 동시에 여러 HTTP 요청이 오면 정확히 어떤 요청이 남긴 로그인지 구분하기 어렵다. 이럴때 사용하기 딱 좋은 것이 바로 request 스코프이다.  이렇게 UUID를 사용해서 요청 구분을 할 수 있게 로그를 남기면 좋음

  • 로그를 출력하기 위한 MyLogger라는 클래스를 만들어서 로그를 만들었다.

  • @Scope(value = “request”)를 사용해서 request 스코프로 지정하면, 이 빈은 HTTP요청 당 하나씩 생성되고, HTTP요청이 끝나는 시점에 소멸된다. ** 이 빈이 생성되는 시점에 자동으로 @PostConstruct 초기화 메서드를 사용해서 uuid를 생성해서 저장하면 이 빈은 HTTP 요청 당 하나씩 생성되므로, uuid를 저장해두면 다른 HTTP 요청과 구분할 수 있다.

  • 이 빈이 소멸되는 시점에 @PreDestroy를 사용해서 종료 메시지

  • requestURL은 이 빈이 생성되는 시점에서 알 수 없으므로, 외부에서 setter로 입력.

No thread-bound request found: 에러 : 컨트롤러에서 MyLogger를 부름 근데 MyLogger는 스코프가 Request 즉 아직 안ㄴ나옴;;

이건 Provider로 해결 가능하다고 함 이후에 알려줌 ㅎ;

??????????? HTTP 서블릿, 서블릿

정리 :

비즈니스 로직이 있는 서비스 계층에서도 로그를 출력해보면 request scope를 사용하지 않고 파라미터로 이 모든 정보를 서비스 계층에 넘긴다면, 파라미터가 많아서 지저분해진다. 더더더 문제는 requestURL 같은 웹 관련 정보가 웹과 관련없는 “서비스”계층까지 넘어가게 된다. 웹과 관련된 부분은 “컨트롤러”까지만 사용해야 한다. 서비스 계층은 웹 기술에 종속되지 않고, 가급적 순수하게 유지하는 것이 유지보수 관점에서 좋다.

———————————— 스코프와 Provider

 이렇게 기존 코드를 ObjectProvider myLoggerProvider; 이렇게 선언해주고

MyLogger myLogger = myLoggerProvider.getObject(); 로 빈을 받아옴

 이렇게 보면 uuid로 각 request스코프가 되어 있으면 로그 보기가 아주 편함

ObjectProvider 덕분에 리퀘스트 스코프가 빈의 생성을 지연 OjbectProvider.getObject()를 호출하는 시점에는 HTTP 요청이 진행중이므로 requestscope 빈의 생성이 정상 처리 된다.

이정도에서 끝내도 될 것 같지만…. 개발자들이 더 줄이고 싶어 한다고.. 했다.

—————————————

스코프와 프록시

프록시 방식 사용  스코프에 이렇게 proxyMode = ScopedProxyMode.TARGET_CLASS (적용 대상이 인터페이스면 TARGET_INTERFACES) 아니면 클래스

이렇게 하면 MyLogger의 가짜 프록시 클래스를 넣어준다.  이렇게 직접 찍어보면…. 가짜 강제 정보 변경이 있다.. 그리고 실제 기능을 호출할 때 진짜를 찾아서 동작한다!!

  • CGLIB이라는 라이브러리로 내 클래스를 상속 받은 가짜 프록시 객체를 만들어서 주입한다. 스프링 컨테이너는 CGLIB라는 바이트 코드 조작 가라이브러리를 사용해서 가짜 프록시 객체 생성

그리고 스프링 컨테이너에 myLogger라는 이름으로 진짜 대신에 이 가짜 프록시 객체를 등록한다. (ac.getBean(“myLogger”, MyLogger.class)로 조회해도 프록시 객체가 조회되는 것을 확인할 수 있다…ㅎ 그래서 의존관계 주입도 이 가짜 프록시 객체가 주입된다..  이 가짜 프록시 빈은 내부에 실제 MyLogger를 찾는 방법을 가지고 있다. 클라이언트가 myLogger.logic()을 호출하면 사실은 가짜 프록시 객체를 호출한 것

***가짜 프록시 객체는 원본 클래스를 상속 받아서 만들어졌기 때믄에 이 객체를 사용하는 클라이언트 입장에서는 사실 원본인지 아닌지 모르게, 동일하게 사용 가능(다형성) ㅎㅎ;;

동작 정리:

  • CGLIB 라이브러리로 내 클래스를 상속 받은 가짜 프록시 객체를 만들어줌
  • 이 가짜 프록시 객체는 실제 요청이 오면 그때 내부에서 실제 빈을 요청하는 위임 로직이 있다.
  • 가짜 프록시 객체는 실제 request scope와는 관계 없음, 내부에 단순한 위임 로직만 있고 싱글톤처럼 동작

개사기..네

특징 정리:

  • 프록시 객체 덕분에 클라이언트는 마치 싱글톤 빈을 사용하듯이 편리하게 사용 가능.
  • 사실 Provider를 사용하든, 프록시를 사용하든 핵심 아이디어는 *************** 진짜 객체 조회를 꼭 필요한 시점까지 지연처리 한다는 점
  • 단지 애노테이션 설정 변경만으로 원본 객체를 프록시 객체로 대체할 수 있고, 이것이 바로 *다형성과 DI 컨테이너가 가진 큰 강점
  • 꼭 웹 스코프가 아니어도 프록시는 사용 가능!

주의점 :

  • 마치 싱글톤처럼 사용하는 것 같지만 다르게 동작, 결국 주의해야함 동작 로직을 알아야 함
  • 이런 특별한 scope는 꼭 필요한 곳에서만 최소화해서 사용, 무분별하게 사용하면 안됨…