들어가며
만약 스프링 프레임워크를 사용해본 적이 있다면, Dependency Injection (DI)에 대해 들어보셨을 것입니다. 또한, 스프링을 사용하는 대부분의 개발자들은 DI를 빈번하게 사용하고 있을것이라 생각합니다. 저 또한 DI라는 기능을 즐겨 사용해왔지만, 이론적인 부분만 알고 있고 실제로 어떻게 동작하는지에 대해서는 깊게 파고들어보지 않았습니다. 앞서 코드 분석을 진행해왔듯이 이번에도 Spring DI가 어떻게 동작하는지 알아보겠습니다.
Application Context
스프링 부트를 사용하여 앱을 실행할 경우 run()을 호출하여 시작하게 됩니다.
public ConfigurableApplicationContext run(String... args) {
long startTime = System.nanoTime();
DefaultBootstrapContext bootstrapContext = this.createBootstrapContext();
ConfigurableApplicationContext context = null;
this.configureHeadlessProperty();
SpringApplicationRunListeners listeners = this.getRunListeners(args);
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);
Banner printedBanner = this.printBanner(environment);
context = this.createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
this.refreshContext(context);
this.afterRefresh(context, applicationArguments);
Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), timeTakenToStartup);
}
listeners.started(context, timeTakenToStartup);
this.callRunners(context, applicationArguments);
} catch (Throwable var12) {
if (var12 instanceof AbandonedRunException) {
throw var12;
}
this.handleRunFailure(context, var12, listeners);
throw new IllegalStateException(var12);
}
...
}
여기서 this.createApplicationContext(); 를 통해 Application Context를 생성합니다.
protected ConfigurableApplicationContext createApplicationContext() {
return this.applicationContextFactory.create(this.webApplicationType);
}

이러한 함수이고 디버깅시 "SERVLET" 타입인 것을 알 수 있습니다.

해당 타입으로 Application Context를 생성했을때, AnnotaionConfigServletWebServerApplicationContext로 초기화 되는것을 알 수 있습니다.
public GenericApplicationContext() {
this.beanFactory = new DefaultListableBeanFactory();
}
AnnotaionConfigServletWebServerApplicationContext의 상위 클래스인 GenericApplicationContext도 초기화 되기때문에, beanFactory의 경우 DefaultListableBeanFactory로 초기화 됩니다. DefaultListableBeanFactory는 Application Context에서 Bean을 관리하는 역할을 맡습니다.
DefaultListableBeanFactory
빈등록 방법
- 싱글톤으로 등록 (registerSingleton)
- BeanDefinition 을 생성하여 등록 (registerBeanDefinition)
refresh
public ConfigurableApplicationContext run(String... args) {
...
try {
...
this.refreshContext(context);
...
} catch (Throwable var12) {
...
}
...
}
이후에는 refreshContext(context)를 호출합니다.
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
beanPostProcess.end();
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
해당 함수를 타고 들어가면 위 함수가 나오게 됩니다. refresh 메소드는 스프링 컨테이너를 초기화하고 빈을 생성하며, 컨텍스트를 사용 가능한 상태로 만드는 역할을 수행합니다.
'Back-end' 카테고리의 다른 글
TDD와 DDD (0) | 2024.07.29 |
---|---|
프록시와 Syncronized (0) | 2024.03.04 |
들어가며
만약 스프링 프레임워크를 사용해본 적이 있다면, Dependency Injection (DI)에 대해 들어보셨을 것입니다. 또한, 스프링을 사용하는 대부분의 개발자들은 DI를 빈번하게 사용하고 있을것이라 생각합니다. 저 또한 DI라는 기능을 즐겨 사용해왔지만, 이론적인 부분만 알고 있고 실제로 어떻게 동작하는지에 대해서는 깊게 파고들어보지 않았습니다. 앞서 코드 분석을 진행해왔듯이 이번에도 Spring DI가 어떻게 동작하는지 알아보겠습니다.
Application Context
스프링 부트를 사용하여 앱을 실행할 경우 run()을 호출하여 시작하게 됩니다.
public ConfigurableApplicationContext run(String... args) {
long startTime = System.nanoTime();
DefaultBootstrapContext bootstrapContext = this.createBootstrapContext();
ConfigurableApplicationContext context = null;
this.configureHeadlessProperty();
SpringApplicationRunListeners listeners = this.getRunListeners(args);
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);
Banner printedBanner = this.printBanner(environment);
context = this.createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
this.refreshContext(context);
this.afterRefresh(context, applicationArguments);
Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), timeTakenToStartup);
}
listeners.started(context, timeTakenToStartup);
this.callRunners(context, applicationArguments);
} catch (Throwable var12) {
if (var12 instanceof AbandonedRunException) {
throw var12;
}
this.handleRunFailure(context, var12, listeners);
throw new IllegalStateException(var12);
}
...
}
여기서 this.createApplicationContext(); 를 통해 Application Context를 생성합니다.
protected ConfigurableApplicationContext createApplicationContext() {
return this.applicationContextFactory.create(this.webApplicationType);
}

이러한 함수이고 디버깅시 "SERVLET" 타입인 것을 알 수 있습니다.

해당 타입으로 Application Context를 생성했을때, AnnotaionConfigServletWebServerApplicationContext로 초기화 되는것을 알 수 있습니다.
public GenericApplicationContext() {
this.beanFactory = new DefaultListableBeanFactory();
}
AnnotaionConfigServletWebServerApplicationContext의 상위 클래스인 GenericApplicationContext도 초기화 되기때문에, beanFactory의 경우 DefaultListableBeanFactory로 초기화 됩니다. DefaultListableBeanFactory는 Application Context에서 Bean을 관리하는 역할을 맡습니다.
DefaultListableBeanFactory
빈등록 방법
- 싱글톤으로 등록 (registerSingleton)
- BeanDefinition 을 생성하여 등록 (registerBeanDefinition)
refresh
public ConfigurableApplicationContext run(String... args) {
...
try {
...
this.refreshContext(context);
...
} catch (Throwable var12) {
...
}
...
}
이후에는 refreshContext(context)를 호출합니다.
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
beanPostProcess.end();
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
해당 함수를 타고 들어가면 위 함수가 나오게 됩니다. refresh 메소드는 스프링 컨테이너를 초기화하고 빈을 생성하며, 컨텍스트를 사용 가능한 상태로 만드는 역할을 수행합니다.
'Back-end' 카테고리의 다른 글
TDD와 DDD (0) | 2024.07.29 |
---|---|
프록시와 Syncronized (0) | 2024.03.04 |