[김영한님] 스프링 핵심 원리 - 기본편
https://inf.run/d1LX

스프링 핵심 원리 - 기본편 - 인프런 | 강의

스프링 입문자가 예제를 만들어가면서 스프링의 핵심 원리를 이해하고, 스프링 기본기를 확실히 다질 수 있습니다., - 강의 소개 | 인프런...

www.inflearn.com

커리큘럼

65 강의 (12시간 7분)

빈 생명주기 콜백

강의 제목 시간 배운 점
51 빈 생명주기 콜백 시작 20 데이터베이스 커넥션 풀이나, 네트워크 소켓처럼 애플리케이션 시작 시점에 필요한 연결을 미리 해두고,애플리케이션 종료 시점에 연결을 모두 종료하는 작업을 진행하려면, 객체의 초기화와 종료 작업이필요하다.

스프링 빈의 이벤트 라이프 사이클
- 스프링 컨테이너 생성
- 스프링 빈 생성
- 의존관계 주입
- 초기화 콜백 (빈이 생성되고, 빈의 의존관계 주입이 완료된 후 호출)
- 사용
- 소멸전 콜백 (빈이 소멸되기 직전에 호출)
- 스프링 종료

스프링은 의존관계 주입이 완료되면 스프링 빈에게 콜백 메서드를 통해서 초기화 시점을 알려주는 다양한 기능을 제공한다. 

객체의 생성과 초기화를 분리하자. ( 생성자는 메모리를 할당해서 객체를 생성하는 책임을 가진다. 초기화는 무거운 동작 수행함. )
유지보수 관점에서 분리가 좋다.
초기화가 내부 값 약간 변경하는 단순한 것은 생성자에서 처리하는게 나을 수 있다.  
52 인터페이스 InitializationBean, DisposableBean 5 Spring 전용 메서드라 의존적임.
메서드 이름을 내가 원하는 방식으로 바꿀 수 없음
인터페이스를 사용하는 초기화, 종료 방법은 스프링 초창기 방법. 지금은 사용하지 않는다.
53 빈 등록 초기화, 소멸 메서드 5 @Bean(initMethod = "init", destroyMethod = "close")

특징
- 메서드 이름 자유
- 스프링 빈이 스프링 코드에 의존하지 않는다.
- 코드가 아니라 설정 정보를 사용하기 때문에, 코드를 고칠 수 없는 외부라이브러리에도 초기화, 종료 메서드를 적용할 수 있다. (가장 큰 장점)

destroyMethod 기능에는 특별한 기능이 있다.
String destroyMethod() default AbstractBeanDefinition.INFER_METHOD;
String INFER_METHOD = "(inferred)"

이 추론(inferred) 기능은  close, shutdown 이름의 메서드를 자동으로 호출해준다. 종료메서드를 추론해서 호출해준다. 직접 스프링 빈으로 등록하면 종료메서드를 적어주지 않아도 잘 동작한다.
사용하기 싫으면 공백으로 >>  destroyMethod =""
54 애노테이션 @PostConstruct, @PreDestory 3 결론인데 무조건 이 방법 쓰자~
최신 스프링에서 가장 권장하는 방법이다. 
    @PostConstruct
    public void init() {    }

    @PreDestroy
    public void close() {    }

'javax.~~' package는 스프링에 종속적인 기술이 아니라 JSR-250 이라는 자바 기술 표준이다. 
단점은 외부 라이브러리에 적용하지 못한다. >> 이 때는 Bean 기능 사용하면 된다.

 

빈 스코프

강의 제목 시간 배운 점
54 빈 스코프란 3 스코프 = 빈이 존재할 수 있는 범위
싱글톤, 프로토, request 알면된다!!

스프링이 지원하는 스코프
- 싱글톤: 스프링 컨테이너의 시작과 종료까지 유지되는 가장 넓은 범위의 스코프
- 프로토타입: 스프링 컨테이너의 프로토타입 빈의 생성과 의존관계 주입까지만 관여하고 더는 관리하지 않는 매우 짧은 범위의 스코프

웹 관련 스코프 
- request: 웹 요청 들어오고 나갈때 까지 유지 스코프
- session: 웹 세션이 생성되고 종료될 때 까지 유지 스코프
- applicaion: 웹의 서블릿 컨텍스와 같은 범위로 유지되는 스코프
55 프로토타입 스코프 12 프로토타입 빈 요청
클라이언트가 요청 -> 새로운빈 생성 -> 빈 반환, 관리X 
(이후에 스프링 컨테이너에 같은 요청이 오면 항상 새로운 프로토타입 빈을 생성해서 반환한다.)

스프링 컨테이너는 프로토타입 빈을 생성하고, 의존관계 주입, 초기화까지만 처리함
이후 빈을 관리하지 않기 때문에, 이 빈을 관리할 책임은 프로토 타입 빈을 받은 클라이언트에 있다.  (종료 메서드 호출안됨 -> 클라이언트가 직접해야한다.)
56 프로토타입 스코프 - 싱글톤 빈과 함께 사용시 문제점 19 스프링은 일반적으로 싱글톤 빈을 사용하므로, 싱글톤 빈이 프로토타입 빈을 사용하게 된다. 싱글톤은 생성 시점에만 의존관계 주입을 받기 때문에, 프로토타입 빈이 새로 생성되기는 하지만 싱글톤 빈과 함께 계속 유지되는 것이 문제다!!

프로토 타입 빈을 주입 시점에만 새로 생성하는게 아니라 사용할 때  마다 새로 생성해서 사용하는 것을 원할텐데..? -> 다음시간
57 프로토타입 스코프 - 싱글톤 빈과 함께 사용시 Provider로 문제 해결 20 의존관계 조회(탐색) = Dependency Lookup
의존관계를 외부에서 주입받는게 아니라 직접 필요한 의존관계를 찾는 것 

스프링 컨텍스트전체를 주입받기 보다는, 스프링 컨테이너가 필요한 프로토타입 빈을 대신 찾아주는 기능이 있으면 된다.

스프링에 의존함
// 내부에서 스프링 컨테이너를 통해 해당 빈을 찾아서 반환한다.
private ObjectProvider<PrototypeBean> prototypeBeanObjectProvider;
//항상 새로운 프로토타입 빈이 생성된다.
PrototypeBean prototypeBean = prototypeBeanObjectProvider.getObject();

스프링에 의존하지 않는 방법: JSR-330 자바 표준 사용 - Provider
simple하고 단순해서 좋음. 하지만 라이브러리가 필요하기 때문에 스프링 자체 기능인 ObjectProvider 추천)
implementation 'javax.inject:javax.inject:1'
private Provider<PrototypeBean> prototypeBeanObjectProvider;
PrototypeBean prototypeBean = prototypeBeanObjectProvider.get();

프로토타입 빈을 언제 사용할까?
- 매번 사용할 때 마다 의존관계 주입이 완료된 새로운 객체가 필요하면 사용하면 된다. 그런데 싱글톤 빈으로 대부분의 문제가 해결이 되어서 사용하는 일은 매우 드물다. (거의 사용할 일 없음!!!!!! 하지만 스프링의 개념이라 알고있어야함)

cc. 공식문서 Provider 라이브러리 사용 예제
- retrieving multiple instances.
- lazy or optional retrieval of an instance.
- breaking circular dependencies.
- abstracting scope so you can look up an instance in a smaller scope from an instance in a containing scope.
58 웹스코프 4 웹 스코프 -웹 환경에서만 존재한다.
스프링이 스코프의 종료시점까지 관리한다. 

Http request에 맞춰서 요청당 각각 할당된다.
(request의 대한 응답이 나갈때까지 관리되고 destroy 된다)
59 request 스코프 예제 만들기 21 spring-boot-starter-web
스프링 부트가 내장 톰켓 서버를 활용해서 웹 서버와 스프링을 함께 실행시킨다.

@Scope(value = "request") 
MyLogger class를 annotaion을 활용하여 request 스코프로 지정했다.
그리고 private final MyLogger; 코드를 실행했다.
하지만 request 요청이 들어오노 나서 빈이 생성되기 때문에, 스프링 어플리케이션이 뜰 때 빈이 초기화되지 않는 문제가 발생한다.
-> Provider를 쓰면 해결된다.
=> ObjectProvider<MyLogger> myLoggerProvider;
60 스코프와 Provider 9 ObjectProvider<MyLogger> myLoggerProvider로 request scope 빈의 생성을 지연했다. 
Provider지만 Controller, service든 같은 request요청이면 같은 Bean이 반환된다.

[5d1fc1a02dbf] [http://localhost:8080/log-demo] controller test
[5d1fc1a02dbf] [http://localhost:8080/log-demo] Service id = testId
[5d1fc1a02dbf] request scope bean create:hello.core.common.MyLogger@36873e68

근데 기존 코드처럼  private final MyLogger myLogger;로 작동시킬 수 있는 방법은 없을까? -> 다음시간
61 스코프와 프록시 11 @Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)

proxyMode를 사용하면 Provider가 필요없음
타겟 대상이 클래스여서 class. 인터페이스면 inteface mode.

class 자체를 출력해보면 $$SpringCGLIB$가 출력됨
내 클래스를 상속받은 가짜 프록시 객체를 만들어서 주입한다.
mylogger = class hello.core.common.MyLogger$$SpringCGLIB$$0

우리가 순수 등록한 클래스가 아닌, 가짜이름으로 가짜 프록시 객체를 등록함
스프링 컨테이너에 가짜 프록시 객체를 등록함
의존관계 주입도 가짜 프록시 객체가 주입된다.

# 가짜 프록시 객체는 요청이 오면 그때 내부에서 진짜 빈을 요청하는 위임 로직이 들어있다.
# 가짜 프록시 객체는 원본 클래스를 상속 받아서 만들어졌기 때문에 이 객체를 사용하는 클라이언트 입장에서는 사실 원본인지 아닌지도 모르게 동일하게 사용할 수 있다. (다형성)
# request scope 와는 관계 없음. 싱글톤 처럼 동작한다.
# 진짜 객체 조회를 꼭 필요한 시점까지 지연처리 한다.

이런 객체들은 주의해서 사용. 무분별한 사용 자제