왜 이 글을 쓰게 되었나요?
생각보다 Spring 이 너무 복잡해서, 한 번쯤 제대로 알아봐야겠다는 생각을 하고 기초부터 알아봐야겠다는 생각을 가지고 진행하게 되었습니다
DispatcherServlet을 밑바닥부터 하나하나 뜯어가면서 배워볼 예정입니다
DispatcherServlet의 계층 구조
DispatcherServlet 은 아래와 같은 상속 관계를 가지고 있습니다
여기서 가장 쉬운 것부터 시작해보도록 하겠습니다.
Aware 인터페이스
/**
* A marker superinterface indicating that a bean is eligible to be notified by the
* Spring container of a particular framework object through a callback-style method.
* The actual method signature is determined by individual subinterfaces but should
* typically consist of just one void-returning method that accepts a single argument.
*
* <p>Note that merely implementing {@link Aware} provides no default functionality.
* Rather, processing must be done explicitly, for example in a
* {@link org.springframework.beans.factory.config.BeanPostProcessor}.
* Refer to {@link org.springframework.context.support.ApplicationContextAwareProcessor}
* for an example of processing specific {@code *Aware} interface callbacks.
*
* @author Chris Beams
* @author Juergen Hoeller
* @since 3.1
*/
public interface Aware {
}
마커 인터페이스입니다.
Aware 인터페이스는 스프링 컨테이너에서 특정 프레임워크 객체에 대한 콜백 스타일 메서드를 통해 알림을 받을 수 있는 빈이 해당하는 것을 나타내는 마커 슈퍼인터페이스입니다. 각 서브인터페이스에 따라 실제 메서드 시그니처가 결정되지만, 일반적으로 단일 인자를 받는 void 반환 메서드로 구성됩니다.
단순히 Aware를 구현하는 것만으로는 기본 기능이 제공되지 않습니다. 대신, 처리는 명시적으로 수행되어야 합니다. 예를 들어 org.springframework.beans.factory.config.BeanPostProcessor에서 처리할 수 있습니다.
BeanPostProcessor 에서는
@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
같은 메서드를 통해서, 콜백을 옵저버 패턴 등을 활용하여 등록해 둔 리스너를 실행시킵니다.
이런 방법을 활용하여, 관리할 때, Aware 인터페이스를 구현함으로써, 명시적으로 표현해 준다고 합니다.
사용 예시는
Aware 인터페이스 콜백의 구체적인 처리 예제는 org.springframework.context.support.ApplicationContextAwareProcessor를 참조하시기 바랍니다.
private void invokeAwareInterfaces(Object bean) {
if (bean instanceof EnvironmentAware) {
((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
}
if (bean instanceof EmbeddedValueResolverAware) {
((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
}
if (bean instanceof ResourceLoaderAware) {
((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
}
if (bean instanceof ApplicationEventPublisherAware) {
((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
}
if (bean instanceof MessageSourceAware) {
((MessageSourceAware) bean).setMessageSource(this.applicationContext);
}
if (bean instanceof ApplicationStartupAware) {
((ApplicationStartupAware) bean).setApplicationStartup(this.applicationContext.getApplicationStartup());
}
if (bean instanceof ApplicationContextAware) {
((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
}
}
직접 가보면, 이런 느낌으로, bean 의 타입에 따라서 직접 미리 등록해 둔 리스너를 실행시킵니다
EnvironmentAware
/**
* Interface to be implemented by any bean that wishes to be notified
* of the {@link Environment} that it runs in.
*
* @author Chris Beams
* @since 3.1
* @see org.springframework.core.env.EnvironmentCapable
*/
public interface EnvironmentAware extends Aware {
/**
* Set the {@code Environment} that this component runs in.
*/
void setEnvironment(Environment environment);
}
실제로 빈을 만들어 등록하게 되면
@Component
public class EnvironmentAwareBean implements EnvironmentAware {
@Override
public void setEnvironment(final Environment environment) {
System.out.println("EnvironmentAwareBean.setEnvironment");
System.out.println("environment.getProperty(\"racingcar.application.name\") = "
+ environment.getProperty("racingcar.application.name"));
}
}
EnvironmentAwareBean.setEnvironment
environment.getProperty("racingcar.application.name") = null
시작 시에 이 메시지가 출력되는 것을 확인할 수 있었습니다
Bean Load 시점에 이미 Environment 객체가 확정되어 있기에, 추후에 environment 가 변경되어도 다시 호출되지는 않습니다
@Component
public class EnvironmentAwareBean implements EnvironmentAware {
@Override
public void setEnvironment(final Environment environment) {
System.out.println("EnvironmentAwareBean.setEnvironment");
System.out.println("environment.getProperty(\"racingcar.application.name\") = "
+ environment.getProperty("racingcar.application.name"));
}
}
@RestController
class EnvironmentAwareController {
private final ConfigurableEnvironment environment;
EnvironmentAwareController(final ConfigurableEnvironment environment) {
this.environment = environment;
}
@GetMapping("/environment")
public String getEnvironment() {
//change Environment
environment.setActiveProfiles("production");
System.out.println("getMethod called");
return environment.getProperty("racingcar.application.name");
}
}
를 통해서 확인해보면
한 번만 위에 setEnvironment 가 호출되는 것을 확인할 수 있었습니다
ApplicationContextAware 인터페이스
이 인터페이스는 ApplicationContext에 대한 정보가 필요할 경우 구현하도록 되어있습니다
/**
* Interface to be implemented by any object that wishes to be notified
* of the {@link ApplicationContext} that it runs in.
*
* <p>Implementing this interface makes sense for example when an object
* requires access to a set of collaborating beans. Note that configuration
* via bean references is preferable to implementing this interface just
* for bean lookup purposes.
*
* <p>This interface can also be implemented if an object needs access to file
* resources, i.e. wants to call {@code getResource}, wants to publish
* an application event, or requires access to the MessageSource. However,
* it is preferable to implement the more specific {@link ResourceLoaderAware},
* {@link ApplicationEventPublisherAware} or {@link MessageSourceAware} interface
* in such a specific scenario.
*
* <p>Note that file resource dependencies can also be exposed as bean properties
* of type {@link org.springframework.core.io.Resource}, populated via Strings
* with automatic type conversion by the bean factory. This removes the need
* for implementing any callback interface just for the purpose of accessing
* a specific file resource.
*
* <p>{@link org.springframework.context.support.ApplicationObjectSupport} is a
* convenience base class for application objects, implementing this interface.
*
* <p>For a list of all bean lifecycle methods, see the
* {@link org.springframework.beans.factory.BeanFactory BeanFactory javadocs}.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @author Chris Beams
* @see ResourceLoaderAware
* @see ApplicationEventPublisherAware
* @see MessageSourceAware
* @see org.springframework.context.support.ApplicationObjectSupport
* @see org.springframework.beans.factory.BeanFactoryAware
*/
public interface ApplicationContextAware extends Aware {
/**
* Set the ApplicationContext that this object runs in.
* Normally this call will be used to initialize the object.
* <p>Invoked after population of normal bean properties but before an init callback such
* as {@link org.springframework.beans.factory.InitializingBean#afterPropertiesSet()}
* or a custom init-method. Invoked after {@link ResourceLoaderAware#setResourceLoader},
* {@link ApplicationEventPublisherAware#setApplicationEventPublisher} and
* {@link MessageSourceAware}, if applicable.
* @param applicationContext the ApplicationContext object to be used by this object
* @throws ApplicationContextException in case of context initialization errors
* @throws BeansException if thrown by application context methods
* @see org.springframework.beans.factory.BeanInitializationException
*/
void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
}
이 인터페이스는 bean의 id를 통해서 직접 조회가 필요하다던가 하는 특수한 상황에 사용됩니다.
보통은 직접 bean을 주입받는 많은 방법이 있는데요
생성자 주입, setter 주입, 필드 주입, 일반 메서드 주입 같은 것들이 가능합니다
파일을 Resource 타입으로 추상화한 결과로 받고 싶거나 했을 때, 사용할 수도 있다고 합니다
이 함수 호출은 위쪽에서 보여드린 afterProperteisSet과 빈이 올라간 시점 사이에 호출된다고 합니다.
다음에는 Servlet 쪽으로 가보려고 합니다
'Spring' 카테고리의 다른 글
Test 의 db 롤백 어디까지 알아보셨나요? (1) | 2023.04.24 |
---|---|
DispatcherServlet 알아보기 - Servlet 편 (0) | 2023.04.22 |
DispatcherServlet 알아보기 - ServletConfig 편 (0) | 2023.04.19 |
모든 객체를 스프링 빈으로 등록해도 괜찮나? (3) | 2023.04.14 |
잘못된 타입이 클라이언트로부터 왔을 때 커스텀 메시지 보여주기 (7) | 2023.03.06 |