'Sprig Reference 따져보기/Chapter 03.'에 해당되는 글 12건
- 2007/05/06 Customizing Bean nature
- 2007/04/30 Scoped beans as dependencies
- 2007/04/27 Bean Scope : Prototype
- 2007/04/27 Bean Scope : other scope
- 2007/04/27 Bean Scope : Singleton
- 2007/04/27 Checking for Dependencies
- 2007/04/25 Autowiring collaborators (2)
- 2007/04/25 Lazy-instantiating beans
- 2007/04/24 Property elements tag와 Validation
- 2007/04/23 Injecting dependencies
빈이 생성되거나 제거될 때 특별한 작업을 추가적으로 하고 싶을 때가 있습니다. 이 경우 설정 파일을 사용하는 방법과 빈의 라이프 사이클 상에서 호출되는 콜백 메소드를 구현할 수도 있습니다.
1. Initialization callback
빈이 생성되는 경우에 추가 작업을 할 수 있는 방법은 세 가지가 있습니다.
첫 번째 방법은 빈 라이프 사이클 상에서 호출되는 콜백 메소인 afterPropertiesSet() 메소드를 구현하는 방법입니다.
이 메소드는 org.springframework.beans.factory.InitializingBean interface에 선언되어 있으며, 해당 빈의 모든 필수적인 속성 값이 모두 할당된 후에 초기화 작업을 위해서 호출됩니다.
하지만 일반적으로 이 방법은 피해야 하는데요, 그 이유는 이 인터페이스를 구현하는 경우 해당 클래스가 스프링 API에 종속이 되기 때문입니다.
두 번째, 세 번째 방법은 환경 설정 파일을 통해서 선언하는 방법입니다.
초기화를 위해서 호출되는 메소드를 설정 파일에 선언하는 것으로, 적용을 원하는 범위에 따라서 <beas>나 <bean>에 설정할 수 있습니다.
2. Destruction callback
빈을 초기화 하는 경우와 마찬가지로 총 세가지 방법을 제공하고 있습니다.
역시 첫 번째 방법은 빈 라이프 사이클 상에 호출되는 콜백 메소드인 destroy() 메소드를 구현하는 방법입니다.
이 메소드는 org.springframework.beans.factory.DsposableBean interface에 선언되어 있으며, 컨테이너에 포함되어 있는 빈이 없어질 때(destroyed) 호출 됩니다.
하지만 이 방법 역시 피해야 하는 방법입니다. 이유는 초기화 메소드를 사용하면 안되는 것과 같습니다.
두 번째, 세 번째 방법은 설정 파일에 선언하는 방법입니다. 세 번째 방법을 사용하는 경우, 모든 빈의 destroy method 이름을 일관되게 가져가야 합니다. 모든 빈에 적용해야 하는 경우 오히려 Convention을 강요하게 되는 장점을 갖을 수가 있습니다.
1. Initialization callback
빈이 생성되는 경우에 추가 작업을 할 수 있는 방법은 세 가지가 있습니다.
첫 번째 방법은 빈 라이프 사이클 상에서 호출되는 콜백 메소인 afterPropertiesSet() 메소드를 구현하는 방법입니다.
void afterPropertiesSet() throws Exception;
이 메소드는 org.springframework.beans.factory.InitializingBean interface에 선언되어 있으며, 해당 빈의 모든 필수적인 속성 값이 모두 할당된 후에 초기화 작업을 위해서 호출됩니다.
하지만 일반적으로 이 방법은 피해야 하는데요, 그 이유는 이 인터페이스를 구현하는 경우 해당 클래스가 스프링 API에 종속이 되기 때문입니다.
두 번째, 세 번째 방법은 환경 설정 파일을 통해서 선언하는 방법입니다.
초기화를 위해서 호출되는 메소드를 설정 파일에 선언하는 것으로, 적용을 원하는 범위에 따라서 <beas>나 <bean>에 설정할 수 있습니다.
<bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/>
or
<beans default-init-method="init">
<bean id="blogService" class="com.foo.DefaultBlogService">
<property name="blogDao" ref="blogDao" />
</bean>
</beans>
or
<beans default-init-method="init">
<bean id="blogService" class="com.foo.DefaultBlogService">
<property name="blogDao" ref="blogDao" />
</bean>
</beans>
2. Destruction callback
빈을 초기화 하는 경우와 마찬가지로 총 세가지 방법을 제공하고 있습니다.
역시 첫 번째 방법은 빈 라이프 사이클 상에 호출되는 콜백 메소드인 destroy() 메소드를 구현하는 방법입니다.
void destroy() thorws Exception;
이 메소드는 org.springframework.beans.factory.DsposableBean interface에 선언되어 있으며, 컨테이너에 포함되어 있는 빈이 없어질 때(destroyed) 호출 됩니다.
하지만 이 방법 역시 피해야 하는 방법입니다. 이유는 초기화 메소드를 사용하면 안되는 것과 같습니다.
두 번째, 세 번째 방법은 설정 파일에 선언하는 방법입니다. 세 번째 방법을 사용하는 경우, 모든 빈의 destroy method 이름을 일관되게 가져가야 합니다. 모든 빈에 적용해야 하는 경우 오히려 Convention을 강요하게 되는 장점을 갖을 수가 있습니다.
<bean id="exampleInitBean" class="examples.ExampleBean" destroy-method="cleanup"/>
or
<beans default-destroy-method="destroy">
<bean id="blogService" class="com.foo.DefaultBlogService">
<property name="blogDao" ref="blogDao" />
</bean>
</beans>
or
<beans default-destroy-method="destroy">
<bean id="blogService" class="com.foo.DefaultBlogService">
<property name="blogDao" ref="blogDao" />
</bean>
</beans>
이전 글에서 Session scope을 적용해봤는데, 한 가지 문제점이 발생했습니다.
Bean scope과 Injection time 문제
userService는 기본 빈 scope인 싱글톤입니다. 이 말은 userService가 의존성 확인을 최초 빈 생성 시기에 한 번만 한다는 뜻입니다. 하지만 userPreference 빈은 session scope으로 세션 마다 빈의 인스턴스가 생성됩니다. 하지만 userPreferences를 참조하고 있는 userService가 싱글톤 빈이기 때문에 userPreferences를 userService 빈 인스턴스가 생성될 때만 빈의 주입(injection)이 가능합니다.
이전 글에서 봤듯이 위 코드를 그대로 테스트를 한 경우 userPreferences가 session scope으로 선언되어 있다 하더라도 사실상 싱글톤 빈으로 작동됩니다.
이 문제를 해결하기 위해서 최초 userService 빈 생성 시에 userPreference 빈을 실제 인스턴스를 생성해서 주는 것이 아니라, Proxy객체를 생성해서 주는 방법이 있습니다. 이 방법을 위해서 스프링에서는 다음 태그를 제공하고 있습니다.
이 child element를 포함하고 있는 빈은 다른 빈에 주입이 될 때 실제 인스턴스가 아닌 Proxy 객체가 주입되게 됩니다. userPreferneces 빈을 호출할 경우 실제로는 userPreferences의 Proxy 객체를 호출하게 되는 것이죠..
이제 userService가 싱글톤 빈이고 userPreferences가 session(request, globalSession도 동일하게 적용)인 경우에도, userPreferences에 대한 실제 인스턴스가 아닌 Proxy 객체를 참조하고 있으므로, 세션마다 생성되는 userPreferences 빈의 인스턴스의 참조를 제대로 주입할 수가 있게되었습니다.
그럼 마지막으로 실제 변경된 코드로 확인해보겠습니다.
Bean scope과 Injection time 문제
<bean id="userPreferences" class="com.foo.UserPreferences" scope="session">
</bean>
<bean id="userService" class="com.foo.SimpleUserService">
<property name="userPreferences" ref="userPreferences"/>
</bean>
</bean>
<bean id="userService" class="com.foo.SimpleUserService">
<property name="userPreferences" ref="userPreferences"/>
</bean>
userService는 기본 빈 scope인 싱글톤입니다. 이 말은 userService가 의존성 확인을 최초 빈 생성 시기에 한 번만 한다는 뜻입니다. 하지만 userPreference 빈은 session scope으로 세션 마다 빈의 인스턴스가 생성됩니다. 하지만 userPreferences를 참조하고 있는 userService가 싱글톤 빈이기 때문에 userPreferences를 userService 빈 인스턴스가 생성될 때만 빈의 주입(injection)이 가능합니다.
이전 글에서 봤듯이 위 코드를 그대로 테스트를 한 경우 userPreferences가 session scope으로 선언되어 있다 하더라도 사실상 싱글톤 빈으로 작동됩니다.
이 문제를 해결하기 위해서 최초 userService 빈 생성 시에 userPreference 빈을 실제 인스턴스를 생성해서 주는 것이 아니라, Proxy객체를 생성해서 주는 방법이 있습니다. 이 방법을 위해서 스프링에서는 다음 태그를 제공하고 있습니다.
<aop:scoped-proxy/>
이 child element를 포함하고 있는 빈은 다른 빈에 주입이 될 때 실제 인스턴스가 아닌 Proxy 객체가 주입되게 됩니다. userPreferneces 빈을 호출할 경우 실제로는 userPreferences의 Proxy 객체를 호출하게 되는 것이죠..
이제 userService가 싱글톤 빈이고 userPreferences가 session(request, globalSession도 동일하게 적용)인 경우에도, userPreferences에 대한 실제 인스턴스가 아닌 Proxy 객체를 참조하고 있으므로, 세션마다 생성되는 userPreferences 빈의 인스턴스의 참조를 제대로 주입할 수가 있게되었습니다.
그럼 마지막으로 실제 변경된 코드로 확인해보겠습니다.
<bean id="userPreferences" class="com.foo.UserPreferences" scope="session">
<aop:scoped-proxy/>
</bean>
<bean id="userService" class="com.foo.SimpleUserService">
<property name="userPreferences" ref="userPreferences"/>
</bean>
<aop:scoped-proxy/>
</bean>
<bean id="userService" class="com.foo.SimpleUserService">
<property name="userPreferences" ref="userPreferences"/>
</bean>
특정 빈에 요청이 올 때마다 매번 빈의 인스턴스를 생성하는 방법입니다. Singleton과 Prototype의 차이점을 간단히 정리해 볼 수가 있겠습니다.
Singleton의 경우 매번 같은 인스턴스가 반복되어서 사용되기 때문에 상태를 유지할 수가 없습니다. 그러므로 상태를 유지해야 하는 경우에는 Prototype을 사용할 수가 있겠습니다. 레퍼런스에서는 DAO의 경우 프로토타입으로 사용하지 말라고 충고하고 있습니다. 상태를 유지해야 하는 성격이 아니라는 말입니다.
하지만 제 생각에는 단순히 DAO만이 아니라 Infrastructure, Service layer, 기타 등등 사실 이런 기반이 되는 클래스는 당연히 특정 상태를 갖으면 안되고, 싱글톤으로 사용되야 맞는 것 같습니다.
어떤 성격의 클래스를 빈으로 선언해서 사용할 것이냐와 같은 고민이 선행되야 할 것 같습니다. 물론 도메인 객체와 같이 상태를 유지해야 하는 경우에는 프로토타입으로 사용하면 되지만, 그 전에 도메인 객체를 왜 빈으로 선언해야 하냐 하는 고민을 먼저해봐야 될 것 같습니다. (도메인 객체를 빈으로 선언해야 하는 경우가 있을까요? 있다면 장점은?)
설정 방법은 2.0 이상인 경우에는 'scope=prototype' 으로, 1.x 인 경우에는 'singleton=false'로 하면 되겠습니다.
주의할 점은 프로토타입을 사용할 때 빈의 생명주기(lifecycle)가 약간 변경된다는 것입니다.
빈이 프로토타입일 경우에, 빈의 초기화(initialization)에 해당하는 라이프사이클 콜백 메소드에 대한 호출을 컨테이너가 책임지지만, 설정의 제거(configured destruction)에 관련된 라이프사이클 콜백 메소드는 컨테이너가 호출해야하는 책임을 지지 않습니다. 즉, 호출하지 않는다는 것이죠. 그러므로 프로토타입의 빈을 지우는 삭제하는 작업과 자원을 반환해야 하는 작업을 직접 처리해줘야 합니다. 보통 이런 처리를 위해서 bean post processor를 사용합니다.
Singleton : stateless
Prototype : stateful
Prototype : stateful
Singleton의 경우 매번 같은 인스턴스가 반복되어서 사용되기 때문에 상태를 유지할 수가 없습니다. 그러므로 상태를 유지해야 하는 경우에는 Prototype을 사용할 수가 있겠습니다. 레퍼런스에서는 DAO의 경우 프로토타입으로 사용하지 말라고 충고하고 있습니다. 상태를 유지해야 하는 성격이 아니라는 말입니다.
하지만 제 생각에는 단순히 DAO만이 아니라 Infrastructure, Service layer, 기타 등등 사실 이런 기반이 되는 클래스는 당연히 특정 상태를 갖으면 안되고, 싱글톤으로 사용되야 맞는 것 같습니다.
어떤 성격의 클래스를 빈으로 선언해서 사용할 것이냐와 같은 고민이 선행되야 할 것 같습니다. 물론 도메인 객체와 같이 상태를 유지해야 하는 경우에는 프로토타입으로 사용하면 되지만, 그 전에 도메인 객체를 왜 빈으로 선언해야 하냐 하는 고민을 먼저해봐야 될 것 같습니다. (도메인 객체를 빈으로 선언해야 하는 경우가 있을까요? 있다면 장점은?)
설정 방법은 2.0 이상인 경우에는 'scope=prototype' 으로, 1.x 인 경우에는 'singleton=false'로 하면 되겠습니다.
<bean id="accountService" class="com.foo.DefaultAccountService" scope="prototype"/>
<bean id="accountService" class="com.foo.DefaultAccountService" singleton="false"/>
<bean id="accountService" class="com.foo.DefaultAccountService" singleton="false"/>
주의할 점은 프로토타입을 사용할 때 빈의 생명주기(lifecycle)가 약간 변경된다는 것입니다.
빈이 프로토타입일 경우에, 빈의 초기화(initialization)에 해당하는 라이프사이클 콜백 메소드에 대한 호출을 컨테이너가 책임지지만, 설정의 제거(configured destruction)에 관련된 라이프사이클 콜백 메소드는 컨테이너가 호출해야하는 책임을 지지 않습니다. 즉, 호출하지 않는다는 것이죠. 그러므로 프로토타입의 빈을 지우는 삭제하는 작업과 자원을 반환해야 하는 작업을 직접 처리해줘야 합니다. 보통 이런 처리를 위해서 bean post processor를 사용합니다.
* 참고 *
스프링 2.0에서 해당하는 DTD와 새로 추가된 XSD 스키마의 경우에는 빈의 scope 설정을 'scope' attribute로 설정해야 합니다. 1.x 대의 DTD 때는 'singleton' attribute를 사용했지만, 이 attribute는 2.0 이상 버전에서는 삭제되었습니다. 빈의 scope이 기본 두 가지에서 더 확장되었기 때문에 사용하는 attribute도 변경된 것으로 생각됩니다.
스프링 2.0에서 해당하는 DTD와 새로 추가된 XSD 스키마의 경우에는 빈의 scope 설정을 'scope' attribute로 설정해야 합니다. 1.x 대의 DTD 때는 'singleton' attribute를 사용했지만, 이 attribute는 2.0 이상 버전에서는 삭제되었습니다. 빈의 scope이 기본 두 가지에서 더 확장되었기 때문에 사용하는 attribute도 변경된 것으로 생각됩니다.
2.x DTD or Schema
scope = "singleton | prototype | request | session | globalSession "
1.x DTD
singleton = "true|false"
scope = "singleton | prototype | request | session | globalSession "
1.x DTD
singleton = "true|false"
스프링 2.0 부터 새로운 빈의 scope이 추가되었습니다. request, session, globalSessino 인데요, 이름에서 진하게 풍기듯이, 웹 기반의 어플리케이션에서 사용될 수 있는 빈의 scope입니다.
새로 추가된 빈의 scope은 웹 기반에서만 사용될 수 있으므로, 웹과 관련된 ApplicationContext에만 사용 선언될 수 있습니다.(예를 들어, XmlWebApplicationContext) 만약 일반 ApplicationContext나 BeanFactory에서 이 scope을 사용하면 IllegalStateException이 발생하게 됩니다.
새로 추가된 scope을 사용하기 위해서는 그 전에 해줘야 할 작업이 있습니다. 정말 간단한 작업 한 가지만 하면 됩니다. 사용하는 서블릿 버전이 2.4 이상 일 때는 ContextListener를 선언하고, 그 이하일 때는 Filter를 등록해주면 됩니다.
이 두 개의 내용은 같고, 단지 버전에 따라서 적용되는 방법이 다를 뿐 입니다.
이제 새로 추가된 빈의 scope을 사용할 준비가 되었습니다.
request는 HTTP request마다 새로 빈의 인스턴스를 생성하는 방법이며, session은 HTTP Session마다 빈의 인스턴스를 생성합니다. globalSession은 session과 비슷하지만 portlet 기반의 웹 어플리케이션일 때만 적용이 가능합니다.
새로 추가된 빈의 scope은 웹 기반에서만 사용될 수 있으므로, 웹과 관련된 ApplicationContext에만 사용 선언될 수 있습니다.(예를 들어, XmlWebApplicationContext) 만약 일반 ApplicationContext나 BeanFactory에서 이 scope을 사용하면 IllegalStateException이 발생하게 됩니다.
새로 추가된 scope을 사용하기 위해서는 그 전에 해줘야 할 작업이 있습니다. 정말 간단한 작업 한 가지만 하면 됩니다. 사용하는 서블릿 버전이 2.4 이상 일 때는 ContextListener를 선언하고, 그 이하일 때는 Filter를 등록해주면 됩니다.
2.4 이상일 때
<web-app>
...
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
...
</web-app>
2.4 이하일 때
<web-app>
..
<filter>
<filter-name>requestContextFilter</filter-name>
<filter-class>org.springframework.web.filter.RequestContextFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>requestContextFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
...
</web-app>
<web-app>
...
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
...
</web-app>
2.4 이하일 때
<web-app>
..
<filter>
<filter-name>requestContextFilter</filter-name>
<filter-class>org.springframework.web.filter.RequestContextFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>requestContextFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
...
</web-app>
이 두 개의 내용은 같고, 단지 버전에 따라서 적용되는 방법이 다를 뿐 입니다.
이제 새로 추가된 빈의 scope을 사용할 준비가 되었습니다.
request는 HTTP request마다 새로 빈의 인스턴스를 생성하는 방법이며, session은 HTTP Session마다 빈의 인스턴스를 생성합니다. globalSession은 session과 비슷하지만 portlet 기반의 웹 어플리케이션일 때만 적용이 가능합니다.
<bean id="loginAction" class="com.foo.LoginAction" scope="request"/>
<bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/>
<bean id="userPreferences" class="com.foo.UserPreferences" scope="globalSession"/>
<bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/>
<bean id="userPreferences" class="com.foo.UserPreferences" scope="globalSession"/>
일단 레퍼런스에서 GoF의 싱글톤 패턴과 햇갈리지 말라고 충고하고 있습니다.
위의 정리 처럼 만약 하나의 컨테이너에 선언되어 있는 하나의 빈이 있다 가정하면, 스프링 컨테이너는 오로지, 정말로 하나의 인스턴스만을 생성하게 됩니다.
컨테이너는 bean definition에 맞추어 정확히 하나의 빈만을 생성하며, 이 생성된 빈은 singleton cache에 저장합니다. 그 후 요청이 올 때마다 저정되어 있는 빈의 참조값을 주어 사용하게 하는 방식입니다.
singleton이 빈의 기본 scope입니다. 기본값이기 때문에 아무 설정을 하지 않으면 싱글톤으로 처리 됩니다.
스프링 2.0에서 부터 제공되는 스키마나 2.0 dtd를 사용할 경우에는 'scope' attribute를 사용해서 설정해줄 수 있습니다.
1.x의 dtd를 사용할 경우에는 'singleton' attribute를 사용할 수 있습니다.
GoF의 Singleton pattern : instance created per ClassLoader
Spring의 Singleton scope : instance created per container and per bean
Spring의 Singleton scope : instance created per container and per bean
위의 정리 처럼 만약 하나의 컨테이너에 선언되어 있는 하나의 빈이 있다 가정하면, 스프링 컨테이너는 오로지, 정말로 하나의 인스턴스만을 생성하게 됩니다.
컨테이너는 bean definition에 맞추어 정확히 하나의 빈만을 생성하며, 이 생성된 빈은 singleton cache에 저장합니다. 그 후 요청이 올 때마다 저정되어 있는 빈의 참조값을 주어 사용하게 하는 방식입니다.
singleton이 빈의 기본 scope입니다. 기본값이기 때문에 아무 설정을 하지 않으면 싱글톤으로 처리 됩니다.
<bean id="accountService" class="com.foo.DefaultAccountService"/>
스프링 2.0에서 부터 제공되는 스키마나 2.0 dtd를 사용할 경우에는 'scope' attribute를 사용해서 설정해줄 수 있습니다.
<bean id="accountService" class="com.foo.DefaultAccountService" scope="singleton"/>
1.x의 dtd를 사용할 경우에는 'singleton' attribute를 사용할 수 있습니다.
<bean id="accountService" class="com.foo.DefaultAccountService" singleton="true"/>
스프링 IoC 컨테이너는 컨테이너에 빈이 deploy 될 때 해결되지 못한 의존성이 있는지를 체크해 줍니다. 해결되지 못한 의존성의 경우, 즉 특정 속성의 값을 찾지 못 했을 때는 autowire 기능을 사용해서 의존성을 해결해 줄 수 있습니다.
이 방법은 특히 빈의 모든 속성 값이 반드시 존재해야 함을 보장하고 싶을 때 유용하게 사용된다고 합니다.
XML에 'dependency-checking' attribute로 설정해 줄 수 있으며, 각 각의 빈마다 설정이 가능합니다.
이 방법은 특히 빈의 모든 속성 값이 반드시 존재해야 함을 보장하고 싶을 때 유용하게 사용된다고 합니다.
XML에 'dependency-checking' attribute로 설정해 줄 수 있으며, 각 각의 빈마다 설정이 가능합니다.
none : 의존성 체크를 하지 않음. 빈의 속성들 중 값이 할당되지 않는 속성은 간단히 값이 들어가지 않습니다(^^)
simple : primitive type과 collection에 대해 의존성 확인 수행
object : collaborator에 대해서만 의존성 확인 수행
all : primitivie type, collection, collaborator에 대해 모두 의존성 확인 수행
simple : primitive type과 collection에 대해 의존성 확인 수행
object : collaborator에 대해서만 의존성 확인 수행
all : primitivie type, collection, collaborator에 대해 모두 의존성 확인 수행
참조하고 있는 빈을 정해진 옵션에 따라서 자동으로 찾아줍니다. Autowire를 잘 사용할 경우에 설정 파일의 양을 극단적으로 줄여줄 수 있다 합니다.
사용할 수 있는 옵션은 5가지가 있습니다.
레퍼런스에서는 일단 primitive type에 대해서는 사용하지 말라고 조언하고 있습니다. 그리고 장단점을 기술하고 있습니다.
역시 무엇이 좋다, 아니다가 아니라 선택의 문제입니다. 상황에 맞추어서 적당한 방법을 선택해야 할 것 같습니다. 저도 Autowire에 대해서는 나쁘게만 생각하고 있었는데, 토비님의 강연을 듣고 생각을 조금 바꾸게 되었습니다.
마지막으로 특정 XML 파일에 있는 빈들을 Autowire의 대상에서 제외하고 싶은 경우에는 'autowire-candiate' attribute를 'false'로 설정하면 됩니다.
사용할 수 있는 옵션은 5가지가 있습니다.
1. no
Autowire를 사용하지 않으며, 빈을 참조하려는 경우 반드시 ref 엘리먼트를 반드사 사용해야 합니다.
2. byName
빈의 이름을 통해서 참조하는 빈을 찾습니다.
3. byType
속성의 타입으로 컨테이너에 등록되어 있는 빈을 찾아 줍니다. 만약 해당 타입으로 하나 이상의 빈이 등록되어 있는 경우에는 fatal exception이 발생되지만, 하나도 없는 경우에는 단지 참조만 하지 않을 뿐이지 별다른 예외를 발생시키지는 않습니다.
4. constructor
byType과 비슷하지만, 생성자 인자를 사용합니다. byType고 차이점은 constructor는 정확히 해당 타입의 빈을 하나만 찾아야 합니다. constructor의 경우 찾는 타입의 빈이 없는 경우에도 fatal exception을 발생시킵니다.
5. autoDetect
일단 constructor로 찾음 다음, byType을 이용해서 참조하는 빈을 찾습니다.
Autowire를 사용하지 않으며, 빈을 참조하려는 경우 반드시 ref 엘리먼트를 반드사 사용해야 합니다.
2. byName
빈의 이름을 통해서 참조하는 빈을 찾습니다.
3. byType
속성의 타입으로 컨테이너에 등록되어 있는 빈을 찾아 줍니다. 만약 해당 타입으로 하나 이상의 빈이 등록되어 있는 경우에는 fatal exception이 발생되지만, 하나도 없는 경우에는 단지 참조만 하지 않을 뿐이지 별다른 예외를 발생시키지는 않습니다.
4. constructor
byType과 비슷하지만, 생성자 인자를 사용합니다. byType고 차이점은 constructor는 정확히 해당 타입의 빈을 하나만 찾아야 합니다. constructor의 경우 찾는 타입의 빈이 없는 경우에도 fatal exception을 발생시킵니다.
5. autoDetect
일단 constructor로 찾음 다음, byType을 이용해서 참조하는 빈을 찾습니다.
레퍼런스에서는 일단 primitive type에 대해서는 사용하지 말라고 조언하고 있습니다. 그리고 장단점을 기술하고 있습니다.
장점
- Autowiring의 사용을 심각히 고려하게 만들 정도로 설정 파일의 양(volume)을 줄여줍니다.
- 빈에 필드 추가 후에도 설정 파일을 수정할 필요가 없습니다.
단점
- Magin. 직접 컨트롤이 아니라 내부적으로 참조 관계를 처리하기 때문에, 어떤 예기치 못한 상황이 발생한지 모른다는 겁니다.
- 컨테이너에 있는 정보를 문서화해주는 경우 Autowire로 처리한 부분은 누락될 수 밖에 없습니다.
- byType이나 constructor를 사용한 경우에는 하나의 타입에 하나의 빈만 사용할 수 밖에 없습니다.
- Autowiring의 사용을 심각히 고려하게 만들 정도로 설정 파일의 양(volume)을 줄여줍니다.
- 빈에 필드 추가 후에도 설정 파일을 수정할 필요가 없습니다.
단점
- Magin. 직접 컨트롤이 아니라 내부적으로 참조 관계를 처리하기 때문에, 어떤 예기치 못한 상황이 발생한지 모른다는 겁니다.
- 컨테이너에 있는 정보를 문서화해주는 경우 Autowire로 처리한 부분은 누락될 수 밖에 없습니다.
- byType이나 constructor를 사용한 경우에는 하나의 타입에 하나의 빈만 사용할 수 밖에 없습니다.
역시 무엇이 좋다, 아니다가 아니라 선택의 문제입니다. 상황에 맞추어서 적당한 방법을 선택해야 할 것 같습니다. 저도 Autowire에 대해서는 나쁘게만 생각하고 있었는데, 토비님의 강연을 듣고 생각을 조금 바꾸게 되었습니다.
마지막으로 특정 XML 파일에 있는 빈들을 Autowire의 대상에서 제외하고 싶은 경우에는 'autowire-candiate' attribute를 'false'로 설정하면 됩니다.
ApplicationContext에는 빈이 singleton mode로 되어 있을 때 eagerly pre-instantiate이 기본으로 되어 있습니다. 컨테이너 생성 시 초기화 프로세스의 일부로 빈이 생성되고 구성 작업을 실시하고 있습니다. pre-instantiate의 장점은 빈 설정의 문제점을 초기에 파악할 수 있다는 것 입니다.
이런 장점에도 불구하고 빈을 초기에 초기화하는 것이 아니라, 실제 필요한 시점에 초기화하고 싶은 경우, 즉 lazy instantiate을 원하는 경우에는 XML 설정 파일에 'lazy-init' attribute를 설정함으로서 가능해집니다.
여기서 한 가지 주의할 점은 아무리 lazy-init이 true로 설정되어 있더라도, 해당 빈이 pre-instantiate되는 singleton mode의 빈과 의존성을 갖고 있다면, lazy instantiate되는 것이 아니라 pre instantiate 됩니다.
컨테이너 내에서 이런 의존성을 걱정하지 않고, 모든 빈을 lazy instantiate하고 싶은 경우에는 <beans>에 설정을 해줄 수 있습니다.
이런 장점에도 불구하고 빈을 초기에 초기화하는 것이 아니라, 실제 필요한 시점에 초기화하고 싶은 경우, 즉 lazy instantiate을 원하는 경우에는 XML 설정 파일에 'lazy-init' attribute를 설정함으로서 가능해집니다.
lazy 빈은 pre-instantiation 대상에서 제외됩니다.<bean id="lazy" class="com.foo.ExpensiveToCreateBean" lazy-init="true">
</bean>
여기서 한 가지 주의할 점은 아무리 lazy-init이 true로 설정되어 있더라도, 해당 빈이 pre-instantiate되는 singleton mode의 빈과 의존성을 갖고 있다면, lazy instantiate되는 것이 아니라 pre instantiate 됩니다.
컨테이너 내에서 이런 의존성을 걱정하지 않고, 모든 빈을 lazy instantiate하고 싶은 경우에는 <beans>에 설정을 해줄 수 있습니다.
<beans default-lazy-init="true">
</beans>
2007/04/24 17:07
Property elements tag와 Validation
2007/04/24 17:07 in Sprig Reference 따져보기/Chapter 03.

스프링 레퍼런스에 <idref>에 대해서 'simply an error-proof way' 라는 표현을 쓰고 있습니다. 왜 이런 표현을 사용했는지 알아보기 위해, 다음 두 개의 설정 파일을 비교해 보겠습니다.
targetName의 속성 값을 하나는 <idref>를 사용했고, 다른 하나는 <value>를 사용했습니다. 이 둘의 가장 큰 차이점은 유효성 검증(validation)의 시점입니다. <idref>를 사용할 경우 컨테이너가 참조하고 있는 빈이 유효성을 deployment time에 검증하게 됩니다.
만약 위의 client가 prototype인 경우, <value>를 사용했을 때는 실제 해당 빈이 사용될 때가 되서야 유효성 검증을 하게 됩니다. 즉, 참조하는 빈에 대한 에러가 있는 경우에도 이 것을 발견할 수 있는 시점은 컨테이너가 올라간 한참 후인 실제 사용할 시기라는 점입니다. <idref>를 사용하는 장점이 충분히 와닿습니다.
여기서 한 단계 더 나아가 <idref bean="" />이 대신 <idref local="" /> 을 사용하게 되면 XML parser가 XML을 파싱할 때(XML document parse time) 빈 id 값의 유효성 검증을 수행한다고 합니다.
이번에는 <constructor-arg />나 <property> 내에 사용되는 <ref> 태그를 살펴보죠.
bean 안에는 참조하고자 하는 빈의 id나 name 값을 적을 수 있습니다. 여기서도 'bean' 대신에 'local' 로 변경해서 사용할 수 있습니다. 물론 참조하는 빈이 같은 XML 파일 내에 있는 경우에만 사용할 수 있습니다.
bean일 때 빈의 id나 name 값을 적을 수 있었던 것에 반해서, local일 때는 id 값만을 적을 수가 있습니다. local을 쓸 때 장점은 역시나 XML paser가 id 값에 대한 유효성 검증을 해준다는 것입니다. 레퍼런스에서는 같은 XML 파일 내에 있는 경우에는 local을 쓰는게 best choice 라 적어두었군요.
<bean id="theTargetBean" class="..."/>
<bean id="theClientBean" class="...">
<property name="targetName">
<idref bean="theTargetBean" />
</property>
</bean>
<bean id="theTargetBean" class="..."/>
<bean id="client" class="...">
<property name="targetName">
<value>theTargetBean</value>
</property>
</bean>
만약 위의 client가 prototype인 경우, <value>를 사용했을 때는 실제 해당 빈이 사용될 때가 되서야 유효성 검증을 하게 됩니다. 즉, 참조하는 빈에 대한 에러가 있는 경우에도 이 것을 발견할 수 있는 시점은 컨테이너가 올라간 한참 후인 실제 사용할 시기라는 점입니다. <idref>를 사용하는 장점이 충분히 와닿습니다.
여기서 한 단계 더 나아가 <idref bean="" />이 대신 <idref local="" /> 을 사용하게 되면 XML parser가 XML을 파싱할 때(XML document parse time) 빈 id 값의 유효성 검증을 수행한다고 합니다.
이번에는 <constructor-arg />나 <property> 내에 사용되는 <ref> 태그를 살펴보죠.
<ref bean="someBean" />
bean 안에는 참조하고자 하는 빈의 id나 name 값을 적을 수 있습니다. 여기서도 'bean' 대신에 'local' 로 변경해서 사용할 수 있습니다. 물론 참조하는 빈이 같은 XML 파일 내에 있는 경우에만 사용할 수 있습니다.
<ref local="someLocalBean" />
bean일 때 빈의 id나 name 값을 적을 수 있었던 것에 반해서, local일 때는 id 값만을 적을 수가 있습니다. local을 쓸 때 장점은 역시나 XML paser가 id 값에 대한 유효성 검증을 해준다는 것입니다. 레퍼런스에서는 같은 XML 파일 내에 있는 경우에는 local을 쓰는게 best choice 라 적어두었군요.
의존성 주입 Injecting Dependencies.
Dependency Injection(DI)의 가장 기본적인 원칙은 인스턴스의 생성자가 호출되거나 팩토리 메소드에서 인스턴스가 리턴된 후, 생성자의 인자나 팩토리 메소드의 인자를 통해서 받은 객체들과의 의존성 삽입이 다는 것 입니다. 매우 추상적이기는 하지만 DI를 적용할 때의 장점을 레페런스에서 간단히 언급하고 있습니다.
DI는 크게 두 가지 방법이 있습니다.
1. Setter Injection
빈의 생성 후 setter 메소드를 호출해서 의존성을 삽입하는 방법입니다.
생성자를 이용하는 방법입니다.
Constructor냐 Setter 냐?
스프링 팀의 의견은 일반적으로 setter injection이면 충분하다고 말하고 있습니다. 하지만 생성자를 이용한 방법도 몇 몇 purist(순수주의자^^)에 의해서 선호된다고 합니다. 그 이유는 setter 메소드를 클라이언트에게 공개하지 않아도 되기 때문입니다. 이 경우 객체를 다시 구성하기가 매우 까다로워 지는데, (레퍼런스의 표현 : less amenable to re-configuration, re-injection) 상황에 따라 장, 단점이 있을꺼라고 생각됩니다. 정해진 룰이나 답은 없죠~(no hard and fast Rule)
빈 의존성 주입 시에 무슨 일이 일어나나?
컨테이너가 생성될 때 스프링은 각 빈의 설정 정보와 속성들이 참조하고 있는 빈에 대한 유효성을 검증(validation) 합니다. 하지만 각 속성들에는 아직 실제 정보나 의존성이 주입된 것은 아닙니다. 이런 작업이 이루어지는 것은 실제 빈이 생성될 때 입니다. 만약 빈이 singleton-scope 이면서, pre-instantiated 상태라면 컨테이너가 생성될 때 실제 빈이 생성되는 작업이 수행됩니다.
빈에 대한 설정 정보와 구성 정보를 실제 생성하기 이전에 유효성을 검증한 다음에 실제 주입을 하는 작업은 가능한 늦게 수행하고 있습니다. 이런 작업을 통해서 실제 의존성을 주입하는 시점에서는 의존성 주입에 관한 정보 자체에 대한 신뢰성을 확보하려는 시도인 것 같습니다.
static factory method를 통해서 빈을 생성할 경우에는 의존성 주입에 관한 정보는 <constructor-args> 를 통해서 전달해 주면 됩니다.
Dependency Injection(DI)의 가장 기본적인 원칙은 인스턴스의 생성자가 호출되거나 팩토리 메소드에서 인스턴스가 리턴된 후, 생성자의 인자나 팩토리 메소드의 인자를 통해서 받은 객체들과의 의존성 삽입이 다는 것 입니다. 매우 추상적이기는 하지만 DI를 적용할 때의 장점을 레페런스에서 간단히 언급하고 있습니다.
- Much cleaner code
- higher grade of decoupling
- higher grade of decoupling
DI는 크게 두 가지 방법이 있습니다.
1. Setter Injection
빈의 생성 후 setter 메소드를 호출해서 의존성을 삽입하는 방법입니다.
2. Constructor Injectionpublic class SimpleMovieLister {
private MovieFinder movieFinder;
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
}
생성자를 이용하는 방법입니다.
public class SimpleMovieLister {
private MovieFinder movieFinder;
public SimpleMovieLister(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
}
Constructor냐 Setter 냐?
스프링 팀의 의견은 일반적으로 setter injection이면 충분하다고 말하고 있습니다. 하지만 생성자를 이용한 방법도 몇 몇 purist(순수주의자^^)에 의해서 선호된다고 합니다. 그 이유는 setter 메소드를 클라이언트에게 공개하지 않아도 되기 때문입니다. 이 경우 객체를 다시 구성하기가 매우 까다로워 지는데, (레퍼런스의 표현 : less amenable to re-configuration, re-injection) 상황에 따라 장, 단점이 있을꺼라고 생각됩니다. 정해진 룰이나 답은 없죠~(no hard and fast Rule)
빈 의존성 주입 시에 무슨 일이 일어나나?
1. BeanFactory(ApplicationContext)가 모든 빈을 초기화합니다.
2. 빈의 의존성은 properties 형태나, 생성자의 인자, static-factory 메소드의 인자 등으로 받아올 수 있습니다. 이러한 의존성은 실제 빈이 생성될 때 빈에게 주입되게 됩니다.
3. 각 속성이나 생성자 인자는 실제 값이거나 컨테이너 내에 있는 또 다른 빈의 참조 값입니다.
4. 스프링에서 제공되는 PropertyEditor에 의해서 각 속성이나 생성자 인자가 갖는 실제 타입으로 변경됩니다.
2. 빈의 의존성은 properties 형태나, 생성자의 인자, static-factory 메소드의 인자 등으로 받아올 수 있습니다. 이러한 의존성은 실제 빈이 생성될 때 빈에게 주입되게 됩니다.
3. 각 속성이나 생성자 인자는 실제 값이거나 컨테이너 내에 있는 또 다른 빈의 참조 값입니다.
4. 스프링에서 제공되는 PropertyEditor에 의해서 각 속성이나 생성자 인자가 갖는 실제 타입으로 변경됩니다.
컨테이너가 생성될 때 스프링은 각 빈의 설정 정보와 속성들이 참조하고 있는 빈에 대한 유효성을 검증(validation) 합니다. 하지만 각 속성들에는 아직 실제 정보나 의존성이 주입된 것은 아닙니다. 이런 작업이 이루어지는 것은 실제 빈이 생성될 때 입니다. 만약 빈이 singleton-scope 이면서, pre-instantiated 상태라면 컨테이너가 생성될 때 실제 빈이 생성되는 작업이 수행됩니다.
빈에 대한 설정 정보와 구성 정보를 실제 생성하기 이전에 유효성을 검증한 다음에 실제 주입을 하는 작업은 가능한 늦게 수행하고 있습니다. 이런 작업을 통해서 실제 의존성을 주입하는 시점에서는 의존성 주입에 관한 정보 자체에 대한 신뢰성을 확보하려는 시도인 것 같습니다.
상호참조.
두 개의 인스턴스가 상호 참조 하고 있는 경우 생성자를 통한 의존성 주입일 할 때 BeanCurrentlyCreationException이 발생합니다. 해결책을 두 가지 제시하고 있는데요, 결국 요즘은 이럴 경우에는 setter injection을 사용하라는 것 입니다.
두 개의 인스턴스가 상호 참조 하고 있는 경우 생성자를 통한 의존성 주입일 할 때 BeanCurrentlyCreationException이 발생합니다. 해결책을 두 가지 제시하고 있는데요, 결국 요즘은 이럴 경우에는 setter injection을 사용하라는 것 입니다.
static factory method를 통해서 빈을 생성할 경우에는 의존성 주입에 관한 정보는 <constructor-args> 를 통해서 전달해 주면 됩니다.
<bean id="exampleBean" class="examples.ExampleBean"
factory-method="createInstance">
<constructor-arg ref="anotherExampleBean"/>
<constructor-arg ref="yetAnotherBean"/>
<constructor-arg value="1"/>
</bean>
<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
Prev

Rss Feed