Spring Core - IoC, DI, Context And Bean

2020. 10. 22. 17:02cloud&platform

 

보통 main메소드에서 시작해서 개발자의 의도대로 오브젝트가 생성되고 실행된다. 그런데 서블릿은 컨테이너가 권한을 가진다. 그렇다. 이미 제어 역전은 폭넓게 적용되고 있는 것이다.

 

템블릿 메소드 패턴도 슈퍼클래스로 템플릿 메소드를 통해 제어를 넘긴 것이다. 이것도 Ioc를 기반으로한 패턴이다.

 

팩토리 패턴도 객체생성에 대한 제어를 애플리케이션 코드에서 분리하는 IoC의 적용 패턴으로 볼 수 있다. > 팩토리 클래스를 하나의 Configuration Context 로 보면 이해됨

 

※ 라이브러리 VS 프레임워크

- 라이브러리는 사용자가 능동적으로 애플리케이션의 흐름을 직접 제어하면서 사용하는 것

- 프레임워크는 사용자의 애플리케이션 코드를 프레임워크의 기반하에 흐름을 주도하는 것이다. 즉, 프레임워크는 분명한 제어역전의 개념이 적용되어 있어야 한다.

 

 

IoC Container

1. Bean

- 스프링 Managed Object. 스프링 IoC 컨테이너에서 생성과 제어를 담당하는 Object.

2. Bean Factory

- 스프링 IoC를 담당하는 핵심 컨테이너. 빈을 등록.생성.조회 한다.

3. Application Context

- Bean Factory를 확장(extends)한 IoC 컨테이너.

- 싱글톤을 저장하고 관리하는 Singleton Registry.

더보기

※ 싱글톤 패턴과 비슷한 개념이나 구현 방법은 확연히 다르다.

1) 왜 싱글톤 인가?

- 적용 대상이 엔터프라이즈 기술을 사용하는 서버 환경이기 때문이다.

- 즉, 수많은 클라이언트 요청에 대한 성능상의 이슈 발생 > 서비스 오브젝트 개념 생성 > 서블릿은 엔터프라이즈 기술의 서비스 오브젝트 (이미 싱글톤으로 구현되었다)

2) 자바 싱글톤패턴의 문제점

- private 생성자로 인한 상속 불가등의 제약이 많다.

- 그래서 테스트 하기 어렵다 - 테스트용 오브젝트(Mock Object) 대체하기 힘들다.

- 서버환경에서는 한개를 보장할 수 없다 - 서버의 클래스로더 구성 방식 영향

- 자연스러운 Global state 화

3) 스프링 싱글톤 레지스트리

- 자바 싱글톤 패턴의 단점 보완을 위한 스프링의 싱글톤 생성/관리 레지스트리

- private 생성자, 스태틱필드가 필요 없다. 일반 자바 객체를 싱글톤으로 활용하게 해준다.

- Spring 싱글톤은 컨테이너당 싱글톤이다.

- 싱글톤 Bean 이기 때문에 > Bean은 stateless 해야 한다. - 읽기 전용 값만 허용한다.

 

4. Configuraion Metadata

- 컨테이너가 IoC를 적용하기 위해 사용하는 메타정보.

Spring IoC Container

 

※ Context Life Cycle

context life cycle

1) Load definition : XML등 리소스에 정의한 설정 수집

2) BFPP (Bean Factory Post Processor) : 빈의 메타 데이터(클래스, 프로퍼티 값 등등)를 조작하는 후처리기. 즉, 메타 데이터만 조작할 수 있는 Interface를 확장 할 수 있다.(Bean이 초기화 되기 전 단계 임을 인지해야 한다.) 이것으로 인해, 클래스를 통한 @Configuration 빈 설정 이나, 프로퍼티 파일의 ${} 치환이 가능할 수 있다.

3) Instantiation : Bean 객체 생성

4) Property Inject : DI

5) BPP (Bean Post Processor) : 빈의 인스턴스 값을 조작하는 후처리기. 즉, 빈의 객체화후에 추가적인 조작을 할 수 있는 Interface를 확장 할 수 있다.

 

※ Bean Life Cycle

Bean Life Cycle

 

1) Instantiate : Bean 객체 생성

2) Populate Properties : DI 적용

- 생성자, 필드, setter메소드 등 (setter는 도중에 변경할 위험이 있지만, Test 적용을 위해서 setter메소드를 이용한 DI가 유리한 경우도 있다.)

- @Autowired는 기본적으로 Type based 하여, 해당 타입이 application내에 유일하다면 문제가 없지만, 보통 인터페이스 기반일 것이므로 @Qualifier("bean name") 를 같이 사용하는 습관을 들이는 것이 좋다.

- @Qualifier는 Type based annotation의 세밀한 제어를 가능하게 해주는 것.

- 타입이 동일한 빈이 여러개일 경우 @Autowired 는 Error를 유발한다. 하지만, Collection<BeanType> beans; 형태로 모두 받을 수는 있음. 단, 에러를 피하기 위해서라면 사용하지 말자. 명시적으로 필요에 의해서만 사용 해야 한다.

- 빈 자체가 Collection인 경우 : 타입방식 인식 불가. @Resource를 써야 한다.

- @Resource(name="bean name")는 Name based이며(name지정을 생략하면 필드명), 명확성과 스프링 의존성(@Resource는 javax 패키지)면에서 더 낫다.

3) Aware 대표 3종 세트

스프링은 빈이 필요로 하는 인프라적인 의존성을 빈이 컨테이너에 알릴 수 있도록하는 Aware 인터페이스들을 제공. Bean 초기화 전에 프로그래밍적으로 이를 핸들링 할 수 있게 한다.

- BeanNameAware : Bean정의에서 부여한 이름을 제공 받는다.

- BeanFactoryAware : BeanFactory를 제공 받는다.

- ApplicationContextAware : ApplicationContext를 제공 받는다.

4) PreInitialization BPP : 빈의 초기화 메소드 호출 전의 BPP

5) InitializingBean : 빈의 초기화 메소드 제공. init-method 속성을 통한 custom초기화 메소드도 가능.

6) PostInitialization BPP : 빈의 초기화 메소드 호출 이후의 BPP

7) DisposableBean : Container 종료시 빈의 소멸 콜백 메소드. custom지정 가능.

 

 

 

DI (Dependency Injection)

IoC는 객체지향설계, 디자인패턴, 컨테이너 서버 기술 등, 자주 사용되는 일반적인 개념. 따라서, 스프링의 특징을 나타내기에는 IoC는 너무 일반적이다.

그래서 스프링 IoC의 핵심을 짚어주는 특징을 가진 용어를 발굴해는데, 이것이 DI (Dependency Injection) 다. 즉, IoC의 하나의 타입이다.

그래서 스프링 컨테이너(Application Context)를 IoC 컨테이너 또는 DI 컨테이너라고 부르는 것이다.

더보기

※ DI VS DL(의존관계 검색 - Dependency Lookup)

- DI 는 의존 관계의 두 객체가 모두 스프링 빈이 되어야 한다.

- DL은 검색을 하는 오브젝트는 스프링 빈이 아니어도 된다. 검색을 받는 오브젝트만 스프링 빈이면 됨.

 

 

Bean Scope

1. singleton VS prototype

- Spring Bean은 default singleton. 컨테이너당 유일하다. (ApplicationContext 당 유일)

- 따라서, Spring Bean은 상태를 저장하고 연산 하려고 해선 안된다.

- 하지만, 늘 그렇듯이 상태를 저장하고 연산을 반드시 해야하는데, Spring Bean이어야 하는 경우가 생길 수 있다.

- bean 정의 시, scope="prototype"으로 설정하면, 매번 새로운 객체가 DI됨을 보장한다.

- 매번 새로운 객체라는 말은 상대적이다.

- 즉, A/B/C Bean이 prototype bean D를 각각 DI하면, A와 B와 C에 있는 D는 다른 객체임을 보장한다.

- 하지만, A/B/C가 default singletone 이면, 이 DI 이후로는 더이상 새로운 D가 생기지 않는다. 새로운 E가 DI를 요청할때까진.

- 만약, A/B/C가 getBean될때마다, 새로운 D가 DI되어 있어야 한다면?

- 일단, A/B/C의 D Bean에 대한 DI를 제거하고, 항상 D를 사용 시점에 getBean을 이용해 사용하는 방법이 있다.

- prototype Bean은 객체 생성 이후 더이상 컨테이너가 관리하는 Bean이 아니다. 따라서 일반 객체를 다루듯이, 해제할 자원이 있다면 알아서 해제해야 한다. 사실 이 부분이 간과 되는 경우가 많은데, Bean의 소멸은 컨테이너가 종료되지 않는다면 실행되지 않을 것이라는 잘못된 믿음에 있다. Singleton Bean의 경우는 어느정도는 맞을 것이다. 하지만 Prototype의 경우는 Singleton Bean에 DI된 경우가 아니라면 객체의 스코프가 다하고 더이상 사용되지 않는 경우 가비지 대상이 될 것인데, 이 경우 자원이 계속 물리고 있다면 결국 아웃오브메모리에 빠지게 될 것이다. (Singleton이라 할지라도, 컨텍스트 구현 방식에 따라서는 더이상 사용되지 않는 빈을 제거할 수도 있을 수 있지 않을까? lazy-init도 가능하니깐.)

따라서, Prototype의 경우는 이 녀석을 참조하는 Bean에서 커스텀 destroy가 있다면 명시적으로 호출 해줘야한다. 컨테이너가 관리를 버렸으니깐. 그리고 D를 다 사용했다면 참조를 해제 시키는 것이 좋은 습관이다. 그리고 여기에 더하여 Singleton의 경우라도, 명시적 해제의 사유가 있다면 destroy 메소드를 구현해 두는 것이 좋다.

 

2. request, session, global session

- Web 기반 ApplicationContext에서만 유효한 Scope.

- 기본 singleton Bean은 Container와 생명주기를 같이 한다. 즉, 특정한 scope를 지닌 request,session,global session 보다 길다.

- request, session, global session bean A를 만들면, 이 A는 주어진 기간만 생존하고, 교체되어야 한다.

- 하지만, 생명주기가 긴 singleton Bean S에 주입되면, S가 초기화될때 주입된 A가 끝까지 함께한다. 생각해 보자. Session 정보나 Request 정보가 한 번 지정되면 끝까지 간다? 완전 이상한 경우가 된다.

- 그럼 A를 DI하는 곳은 일반 기본 Bean이면 안되는 것인가.

- 왜안되겠느냐. Spring은 서비스를 처리하는 거의 모든 Bean이 singleton인데.

- A와 같이 Scope가 한정된 Bean을 정의 시, scoped-proxy를 선언해 주면, 해당 Bean은 본체 대신 Proxy가 S에 DI 되며, 본체는 사용 하는 시점에 맞는 A가 할당되도록, 한 단계를 더 추상화 한 메카니즘을 제공한다.

 

 

 

※ Bean 설정 메타 정보

· beanClassName : 빈 오브젝트의 클래스 이름. 빈 오브젝트는 이 클래스의 인스턴스가 된다.

default : 없음. 필수항목

· parentName : 빈 메타정보를 상속받을 부모 BeanDefinition의 이름. 빈의 메타정보는 계층구조로 상속할 수 있다. default : 없음.

 

· factoryBeanName : 팩토리 역할을 하는 빈을 이용해 빈 오브젝트를 생성하는 경우에 팩토리 빈의 이름을 지정한다. default : 없음.

· factoryMethodName : 다른 빈 또는 클래스의 메소드를 통해 빈 오브젝트를 생성하는 경우 그 메소드 이름을 지정한다 .default : 없음.

 

· scope : 빈 오브젝트의 생명주기를 결정하는 스코프를 지정한다. 크게 싱글톤과 비싱글톤 스코프로 구분할 수 있다. default : 싱글톤.

자바 애노테이션 Config를 사용해서 @Bean 메소드에 return new Object() 라고 해도, Spring은 해당 Object를 default값인 싱글톤으로 리턴한다.

 

· lazyInit : 빈 오브젝트의 생성을 최대한 지연할 것인지를 지정한다. 이 값이 true이면 컨테이너는 빈 오브젝트의 생성을 꼭 필요한 시점까지 미룬다. default : false

 

· dependsOn : 먼저 만들어져야 하는 빈을 지정할 수 있다. 빈 오브젝트의 생성 순서가 보장돼야 하는 경우 이용한다. 하나 이상의 빈 이름을 지정할 수 있다. default : 없음.

 

· autowireCandidate : 명시적인 설정이 없어도 미리 정해진 규칙을 가지고 자동으로 DI 후보를 결정하는 자동와이어링의 대상으로 포함시킬지의 여부. default : true

· autowireMode : 오토와이어링 전략. 이름,타입,생성자,자동인식 등의 방법이 있다. default : 없음.

 

· primary : 자동와이어링 작업 중에 DI 대상 후보가 여러 개가 발생하는 경우가 있다. 이때 최종 선택의 우선권을 부여할지 여부. primary가 지정된 빈이 없이 여러 개의 후보가 존재하면 자동와이어링 예외가 발생한다. default : false

 

· abstract : 메타정보 상속에만 사용할 추상 빈으로 만들지의 여부. 추상 빈이 되면 그 자체는 오브젝트가 생성되지 않고 다른 빈의 부모 빈으로만 사용된다. default : false

 

· dependencyCheck : 프로퍼티 값 또는 레퍼런스가 모두 설정되어 있는지 검증하는 작업의 종류. default : 체크하지 않음.

 

· initMethod : 빈이 생성되고 DI를 마친 뒤에 실행할 초기화 메소드의 이름. default : 없음.

· destroyMethod : 빈의 생명주기가 다 돼서 제거하기 전에 호출할 메소드의 이름. default : 없음.

 

· propertyValues : 프로퍼티의 이름과 설정 값 또는 레퍼런스. 수정자 메소드를 통한 DI작업에서 사용한다. default : 없음.

· constructorArgumentValues : 생성자의 이름과 설정 값 또는 레퍼런스. 생성자를 통한 DI작업에서 사용한다. default : 없음.

 

· annotationMetadata : 빈 클래스에 담긴 애노테이션과 그 애트리뷰트 값. 애노테이션을 이용하는 설정에서 활용한다. default : 없음.

'cloud&platform' 카테고리의 다른 글

Docker 기본 개념  (0) 2020.10.26
Kubernetes 설치 가이드 - v1.11.3  (0) 2020.10.23
Spring Overview  (0) 2020.10.20
Kubectl command  (0) 2020.10.16
Kubernetes 기본 개념  (0) 2020.10.05