정리하다 보니 문제가 생겼다.
중요한 문장이라고 생각되면 적으려 하다보니, 책을 옮겨적는 수준이 되가고 있다.
ㅠㅠ
스프링의 핵심을 담당하는 건 바로 빈 팩토리 또는 애플리케이션 컨텍스트라고 불리는 것이다.
1. 애플리케이션 컨텍스트와 설정정보.
스프링에서는 스프링이 제어권을 가지고 직접 만들고 관계를 부여하는 오브젝트를 빈(bean)이라고 부른다.
오브젝트 단위의 애플리케이션 컴포넌트를 말한다. 동시에 스프링 빈은 스프링 컨테이너가 생성과 관계설정, 사용 등을 제어해주는 제어의 역전이 적용된 오브젝트를 가리키는 말이다.
"스프링 빈은 오브젝트 단위의 애플리케이션 컴포넌트를 말하며, 스프링 컨테이너가 생성과 관계설정, 사용 등을 제어해주는 제어의 역전이 적용된 오브젝트를 가리키는 말이다."
스프링에서는 빈의 생성과 관계설정 같은 제어를 담당하는 IoC 오브젝트를 빈 팩토리라고 부른다. 보통 빈 팩터리보다는 이를 좀 더 확장한 애플리케이션 컨텍스트(application context) 를 주로 사용한다. 애플리케이션 컨텍스트는 IoC 방식을 따라 만들어진 일종의 빈 팩토리라고 생각하면 된다.
"IoC, 제어의 역전을 가능케 하는 오브젝트를 빈 팩토리라고 부른다. 대표적으로, 각 오브젝트가 사용하는 다른 오브젝트를 직접 생성하지 않는다. 스프링의 애플리케이션 컨텍스트가 직접 의존성을 설정해준다."
"빈 팩터리라 말할 때는 빈을 생성하고 관계를 설정하는 IoC의 기본 기능에 초점을 맞춘 것이고, 애플리케이션 컨텍스트라고 말할 때는 애플리케이션 전반에 걸쳐 모든 구성요소의 제어 작업을 담당하는 IoC 엔진이라는 의미가 좀 더 부각된다고 보면 된다."
애플리케이션 컨텍스트는 별도의 정보를 참고해서 빈의 생성, 관계설정 등의 제어작업을 총괄한다. 애플리케이션 컨텍스트가 직접 이런 정보를 담고 있는게 아니라, 별도로 설정 정보를 담고 있는 무엇인가를 가져와 이를 활용하는 범용적인 IoC 엔진 같은 것이라고 볼 수 있다.
빈 팩토리 또는 애플리케이션 컨텍스트가 사용하는 설정정보를 만드는 방법은 여러가지가 있다.
그 자체로는 애플리케이션 로직을 담당하지는 않지만, IoC 방식을 이용해 애플리케이션 컴포넌트를 생성하고, 사용할 관계를 맺어주는 등의 책임을 담당하는 것이 애플리케이션 컨텍스트 + 설정정보의 역할이다.
마치 건물이 설계도면을 따라서 만들어지듯, 애플리케이션도 애플리케이션 컨텍스트와 그 설정정보를 따라서 만들어지고 구성된다고 생각할 수 있다.
@Configuration 어노테이션을 해당 클래스에 붙임으로서, 스프링은 해당 클래스가 "스프링 빈 팩토리를 위한 오브젝트 설정을 담당하는 클래스" 라고 인식할 수 있다.
"@Configuration: 스프링 빈 팩토리에게, 해당 클래스를 오브젝트 설정을 담당하는 클래스로 인식하게 한다."
그리고 오브젝트를 만들어주는 메소드에는 @Bean 이라는 어노테이션을 붙여준다.
"@Bean: 스프링 컨테이너가 생성과 관계설정, 사용 등을 제어해주는 제어의 역전이 적용된 오브젝트 를 만드는 메소드라고 명시한다."
애플리케이션 컨텍스트는 ApplicationContext 타입의 오브젝트이다. @Configuration 이 붙은 자바 코드를 설정정보로 사용하기 위해 AnnotationConfigApplicationContext를 이용할 수 있다.
ApplicationContext context = new AnnotationConfigApplicationContext(DaoFactory.class);
UserDao userDao = context.getBean("userDao", UserDao.class);
getBean() 메소드는 ApplicationContext가 관리하는 오브젝트를 요청하는 메소드이다.
2. 애플리케이션 컨텍스트의 동작방식
2.1 애플리케이션 컨텍스트의 기본 동작 방식
스프링에서는 이 애플리케이션 컨텍스트를 IoC 컨테이너라 하기도 하고, 간단히 스프링 컨테이너라 부르기도 한다.
애플리케이션 컨텍스트는 ApplicationContext 인터페이스를 구현하는데, ApplicationContext는 빈 팩토리가 구현하는 BeanFactory 인터페이스를 상속했으므로, 애플리케이션 컨텍스트는 일종의 빈 팩토리인 셈이다.
애플리케이션 컨텍스트는 애플리케이션에서 IoC를 적용해서 관리할 모든 오브젝트에 대한 생성과 관계설정을 담당한다.
ApplicationContext에는 직접 오브젝트를 생성하고 관계를 맺어주는 코드가 없다. 그런 생성정보와 연관관계 정보를 별도의 설정정보를 통해 얻는다.
@Configuration 이 붙은 클래스는 이 애플리케이션 컨텍스트가 활용하는 IoC 설정 정보이다. 클라이언트가 해당 Bean 을 원할 때, Application Context는 @Configuration 클래스에게 요청해 Bean 을 생성 및 관리하고, 이 Bean을 요청한 클라이언트에게 전달해준다.
(일단 클라이언트는 해당 Bean 을 사용하는 객체라고 생각하자.)
애플리케이션 컨텍스트는 자신의 빈 목록에서 클라이언트가 요청한 이름이 있는지 찾고, 있다면 빈을 생성하는 메소드를 호출해서 오브젝트를 생성시킨 후 클라이언트에게 전달한다.
애플리케이션 컨텍스트를 사용할 때 얻는 장점.
애플리케이션 컨텍스트를 사용했을 때 얻는 장점은 다음과 같다.
1. 클라이언트는 구체적인 팩토리 클래스를 알 필요가 없다.
클라이언트가 필요한 오브젝트(빈) 을 가져오기 위해 어떤 팩토리 클래스를 사용해야 하는지 알 필요가 사라진다. 실제로 해당 오브젝트를 생성하는 놈이 누군지 알필요가 없다, 스프링 관점에선 @Configuration 클래스가 뭔지 알 필요가 없다는 뜻이다.
UserAddressParser 라는 오브젝트(빈) 이 있고, 이 빈을 생성하는 놈이 UserAddressParserFactory 라고 해보자.
만약 User 정보를 얻어내는 클래스가 있다고 생각해보자. UserInformationSearcher 라는 클래스가 있다.
이 UserInformationSearcher 클래느는 new UserAddressParserFactory(); 라고 하고 userAddressParserFactory.getUserAddressParser() 이런식의 흐름을 갖을 것이다.
여기서 UserInformationSearcher 클래스가 필요한건 오직 "UserAddressParser" 인스턴스일 뿐이다. 이 걸 누가 만들었는지는 알 필요가 없다. (이런 걸 DL 이라고 부르기도 한다.)
2. 애플리케이션 컨텍스트는 "종합" IoC 서비스를 제공해준다.
단순히 오브젝트 생성과 다른 오브젝트와의 관계설정만 해주는 것이 아니다. 오브젝트가 만들어지는 방식, 시점과 전략을 다르게 가져갈 수도 있으며, 이에 부가적으로 자동생성, 오브젝트에 대한 후처리, 정보의 조합, 설정 방식의 다변화, 인터셉팅 등 오브젝트를 효과적으로 화룡ㅇ할 수 있는 다양한 기능을 제공한다.
또 빈이 사용할 수 있는 기반기술 서비스나 외부 시스템과의 연동 등을 컨테이너 차원에서 제공해주기도 한다.
3. 애플리케이션 컨텍스트는 빈을 검색하는 다양한 방법을 제공한다.
애플리케이션 컨텍스트의 getBean() 메소드는 빈의 이름을 이요앻 빈을 찾아준다. 타입만으로 빈을 검색하거나 특별한 애노테이션 설정이 되어 있는 빈을 찾을 수도 있다.
**설정정보/ 설정 메타정보
스프링 설정정보란 애플리케이션 컨텍스트 또는 빈 팩토리가 IoC를 적용하기 위해 사용하는 메타정보를 말한다. 영어로 configuration 이라고 하는데, 이는 구성정보 내지는 형상정보라는 의미이다.
스프링의 설정정보는 IoC 컨테이너에 의해 관리되는 애플리케이션 오브젝틀르 생성하고 구성할 때 사용된다.
** 컨테이너
컨테이너라는 말 자체가 IoC의 개념을 담고 있다. 따라서 이름이 긴 애플리케이션 컨텍스트 대신에 스프링 컨테이너라고 부르는걸 선호하는 사람들이 있다. (애플리케이션 컨텍스트를 그냥 스프링 컨테이너라고 부르기도 한다.)
애플리케이션 컨텍스트는 그 자체로 ApplicationContext 인터페이스를 구현한 오브젝트를 가리키기도 한다. 애플리케이션 컨텍스트 오브젝트는 하나의 애플리케이션에서 보통 여러 개가 만들어져 사용된다. 이를 통틀어서 스프링 컨테이너라고 부를 수 있다. 때로는 이런 스프링 컨테이너를 그냥 스프링이라 부르기도 한다. 따라서 "스프링에 빈을 등록하고...라고 표현할 수도 있다."
오브젝트의 동일성과 동등성
자바에서는 두 개의 오브젝트가 완전히 같은 동일한 오브젝트라고 말하는 것과, 동일한 정보를 담고 있는 오브젝트라고 말하는 것은 분명한 차이가 있다. 전자는 동일성 비교라고 하고 후자를 동등성 비교라고 한다.
두개의 오브젝트가 동일하다면 사실은 하나의 오브젝트만 존재하는 것이고, 두 개의 오브젝트 레퍼런스 변수를 갖고 있을 뿐이다. 두개의 오브젝트가 동일하지 않고 등등하다 할 때는 두 개의 오브젝트는 완전 다른 메모리상에 존재하는 것인데, 오브젝트의 동등성 기준에 따라 두 오브젝트의 정보가 동등하다고 판단하는 것일 뿐이다.
equal 메소드를 따로 구현하지 않으면 Object 의 equals() 메소드를 사용하게 되는데, Object의 equals() 메소드는 두 오브젝트의 동일성을 비교해서 그 결과를 돌려준다.
2.2 싱글톤 레지스트리로서의 애플리케이션 컨텍스트
애플리케이션 컨텍스트는 싱글톤을 저장하고 관리하는 싱글톤 레지스트리(singleton registry) 이기도하다.
스프링은 기본적으로 별다른 설정을 하지 않으면 내부에서 생성하는 빈 오브젝트를 모두 싱글톤으로 만든다.
태생적으로 스프링은 엔터프라이즈 시스템을 위해 고안된 기술이기 때문에 서버환경에서 사용될 때 그 가치가 있다. 실제로 스프링은 대부분 서버환경에서 사용된다.
서버 환경에서 사용되기 때문에 매 요청마다 오브젝트를 생성하면 서버가 감당하기 힘들다. 따라서 엔터프라이즈 분야에서는 서비스 오브젝트라는 개념을 일찍부터 사용해왔다. 서블릿은 자바 엔터프라이즈 기술의 가장 기본이 되는 서비스 오브젝트라고 할 수 있다. 서블릿은 대부분 멀티스레드 환경에서 싱글톤으로 동작한다. 서블릿 클래스당 하나의 오브젝트만 만들어두고 ,사용자의 요청을 담당하는 여러 스레드에서 하나의 오브젝트를 공유해 동시에 사용한다.
누군가는 싱글톤을 안티패턴이라고 부른다. 흔히들 말하는 싱글톤 패턴을 제대로 구현하면, 인스턴스는 한 애플리케이션에 하나만 존재해야 하며, 이를 위해 생성자에 private 을 붙여 두번의 생성을 막고 있으며... 등등의 제약 조건이 있기때문에,
결과적으로 여러 문제가 생긴다.
- 싱글톤 객체가 정말 하나만 생긴 것을 어찌 보장할 것인가?
- 싱글톤은 전역 상태를 만들 수 있으므로 옳지 못하다.
- 싱글톤은 상속이 안되므로, 객체지향을 파괴한다.
- 테스트하기 힘들다..
등등의 문제로 싱글톤 패턴을 싫어하는 사람들이 많다.
자바의 기본적인 싱글톤 패턴의 구현 방식은 이런 단점들이 있기 때문에ㅡ 스프링은 직접 싱글톤 형태의 오브젝트를 만들고 관리하는 기능을 제공한다. 그것이 바로 싱글톤 레지스트리이다.
스프링은 스태틱 메소드와 private 생성자를 사용해야하는 비정상적인 클래스가 아니라 평범한 자바 클래스를 싱글토으로 활용하게 해준다.
스프링은 기본적으로 별다른 설정을 하지 않으면 내부에서 생성하는 빈 오브젝트를 모두 싱글톤으로 만든다.
=>
멀티스레드 환경이라면 여러 스레드가 동시에 접근해서 사용할 수 있다. 따라서 상태 관리에 주의를 기울여야 한다. 상태정보를 내부에 갖고 있지 않은 무상태 방식으로 만들어져야 한다.
2.3 의존 관계 주입(DI)
스프링을 단순히 IoC 컨테이너라고만 하면, 스프링이 서블릿 컨테이너처럼 서버에서 동작하는 서비스 컨테이너라는 뜻인지, 아니면 단순히 IoC개념이 적용된 템플릿 메소드 패턴을 이용해 만들어진 프레임워크인지, 아니면 또 다른 ioC 특징을 지닌 기술이라는 것인지 파악하기 힘들다. 그래서 스프링이 제공하는 IoC 방식의 핵심을 짚어주는 의존관계 주입(DI) 라는 이름을 사용하기 싲가했다.
이제 DI 컨테이너라고 부른다.
DI는 오브젝트 레퍼런스를 외부로브테 제공받고 이를 통해 여타 오브젝트와 다이내믹하게 의존관계가 만들어지는 것이 핵심이다.
의존관계란 무엇인가? 의존한다는 것은 A -> B 일때 의존대상인 B가 변하면 그것이 A에게 영향을 미친다는 뜻이다.
의존관계 주입은 구체적인 의존 오브젝트와 그것을 사용할 주체, 보통 클라이언트라고 부르는 오브젝트를 런타임 시에 연결해주는 작업을 말한다.
2.4 의존관계 검색과 주입
스프링이 제공하는 IoC 방법에는 의존관계 주입만 있는 것이 아니다. 코드에서는 구체적인 클래스에 의존하지 않고, 런타임 시에 의존관계를 결정한다는 점에서 의존관계 주입과 비슷하지만, 의존관계를 맺는 방법이 외부로부터의 주입이 아니라 스스로 검색을 이용하기 때문에 의존관계 검색(Dependency Lookup) 이라고 불리는 것도 있다.
물론 자신이 어떤 클래스의 오브젝트를 이용할지 정하는 것은 아니다. 의존관계 검색은 런타임 시 의존관계를 맺을 오브젝트를 결정하는 것과 오브젝트의 생성 작업은 외부 컨테이너에게 IoC 로 맡기지만, 이를 가져올 때는 메소드나 생성자를 통한 주입 대신 스스로 컨테이너에게 요청하는 방법을 사용한다.
예를들면 다음과 같이...
public UserDao() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplciationContext(DaoFactory.class);
this.connectionMaker = context.getBean("connectionMaker", ConnectionMaker.class);
}
위에서 DaoFactory.class 와 ConnectionMaker.class 를 보고, 사실상 의존관계 다 드러나는 것 아닌가? 하는 생각을 할 수도 있다.
...맞다.
ConnectionMaker.class 는 그냥 인터페이스를 명시할 뿐이다, 실제로 어떤 인스턴스가 생성됐는지는 UserDao는 전혀 알지 못한다.
하지만 AnnotationContext 와 설정 정보인 DaoFactory.class 를 직접 언급하고 있다는 점이 마이너스.
의존관계 검색 방법은 코드 안에 오브젝트 팩토리 클래스나 스프링 API 가 나타나므로, 결국 애플리케이션 컴포넌트가 컨테이너와 같이 성격이 다른 오브젝트에 의존하게 되는 것이므로 그다지 바람직하지 않다.
따라서 대개는 의존관계 주입 방식을 사용하는 편이 낫다.
의존관계 주입과 의존관계 검색에 결정적인 차이점은 하나 더 있다.
의존관계 주입과는 달리 의존관계 검색은, "Bean 을 필요로 하는 인스턴스가 Bean 일 필요가 없다."
DI 를 원하는 오브젝트는 먼저 자신의 컨테이너가 관리하는 빈이 돼야 한다.
3. XML 을 이용한 설정
XML을 이용해서도 DI 의존관계 설정정보를 만들 수 있다. XML은 단순한 텍스트 파일이기 때문에 다루기 쉬우며, 쉽게 이해할 수 있고, 컴파일과 같은 별도의 빌드작업이 필요 없다는 것 또한 장점이다. 환경이 달라져서 오브젝트의 관계가 바뀌는 경우에도 빠르게 변경사항을 반영할 수 있다.
4. Datasource 관련
자바에는 DB 커넥션을 가져오는 오브젝트의 기능을 추상화한 DataSource라는 인터페이스가 이미 존재한다. 이미 다양한 방법으로 DB 연결과 풀링(pooling) 기능을 갖춘 많은 Datasource 구현 클래스가 존재하고, 이를 가져다 사용하면 대부분 충분하다.
'개발' 카테고리의 다른 글
스프링 기본 정리(4) AOP (0) | 2020.01.19 |
---|---|
스프링 기본 정리(3) 간단한 트랜잭션 정리 (1) | 2020.01.19 |
스프링 기본 정리(1) 스프링은 무엇인가, 왜 좋은가, IoC란 무엇인가 (0) | 2020.01.18 |
Voice Diary -2.2- 간단한 서버 구축하기 (0) | 2019.08.18 |
Voice Diary -2.1- 스프링 부트 간단 복습(1) (0) | 2019.08.18 |