@SpringBootApplication
public class HeeverseTicketApplication {
public static void main(String[] args) {
SpringApplication.run(HeeverseTicketApplication.class, args);
}
}
// SpringApplication 생성
/**
* Static helper that can be used to run a {@link SpringApplication} from the
* specified sources using default settings and user supplied arguments.
* @param primarySources the primary sources to load
* @param args the application arguments (usually passed from a Java main method)
* @return the running {@link ApplicationContext}
*/
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
// SpringApplication.run() 내부 코드
/**
* Run the Spring application, creating and refreshing a new
* {@link ApplicationContext}.
* @param args the application arguments (usually passed from a Java main method)
* @return a running {@link ApplicationContext}
*/
public ConfigurableApplicationContext run(String... args) {
long startTime = System.nanoTime();
// DefaultBootstrapContext 생성
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ConfigurableApplicationContext context = null;
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
Banner printedBanner = printBanner(environment);
// 어플리케이션 컨텍스트 생성
context = createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
// 어플리케이션 컨텍스트 준비
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
// 어플리케이션 컨텍스트 refresh
refreshContext(context);
// 어플리케이션 컨텍스트 refresh 후처리
afterRefresh(context, applicationArguments);
Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
}
listeners.started(context, timeTakenToStartup);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
if (ex instanceof AbandonedRunException) {
throw ex;
}
handleRunFailure(context, ex, listeners);
throw new IllegalStateException(ex);
}
try {
if (context.isRunning()) {
Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
listeners.ready(context, timeTakenToReady);
}
}
catch (Throwable ex) {
if (ex instanceof AbandonedRunException) {
throw ex;
}
handleRunFailure(context, ex, null);
throw new IllegalStateException(ex);
}
return context;
}
// 어플리케이션 컨텍스트 생성
context = createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
/**
* Strategy method used to create the {@link ApplicationContext}. By default this
* method will respect any explicitly set application context class or factory before
* falling back to a suitable default.
* @return the application context (not yet refreshed)
* @see #setApplicationContextFactory(ApplicationContextFactory)
*/
protected ConfigurableApplicationContext createApplicationContext() {
return this.applicationContextFactory.create(this.webApplicationType);
}
// 어플리케이션 컨텍스트 준비
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
// 어플리케이션 컨텍스트 refresh
refreshContext(context);
어플리케이션 타입에 따라 refresh() 동작 내용이 다르므로 템플릿 메서드 패턴이다.
// AbstractApplicationContext
@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");
// 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();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
contextRefresh.end();
}
}
}
// 어플리케이션 컨텍스트 refresh 후처리
afterRefresh(context, applicationArguments);
@SpringBootConfiguration
, @EnableAutoConfiguration
and @ComponentScan
가지의 역할을 수행하는 애노테이션
@SpringBootConfiguration
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
**@Configuration**
@Indexed
public @interface SpringBootConfiguration {
@Configuration
의 상위 어노테이션이지만 역할은 똑같다.@EnableAutoConfiguration
보통의 @Enable*
어노테이션을 이용하는 클래스는 @Import
를 메타어노테이션으로 사용한다.
자동 설정 기능을 활성화하는 어노테이션이다.
특정 작업들을 위한 베이스 패키지
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
AutoConfigurationImportSelector.selectImports()
를 실행시켜서 반환한 Configuration 클래스만 로딩을 하도록 한다.
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
자동 설정이 가능한 목록은 spring-boot-autoconfigure.jar의 META-INF 디렉토리의 spring.factories에서 확인가능
Configuration 클래스들은 조건을 충족할 때만 로딩되게 설정할 수 있다.
// 예시
@AutoConfiguration
@ConditionalOnClass(ObjectMapper.class)
public class JacksonAutoConfiguration {
@ConditionalOnClass
: 해당 클래스가 클래스 패스에 존재하는 경우@ConditionlOnBean
: 해당 클래스나 이름이 빈 팩토리에 포함되어 있는 경우@ConditionalOnMissingBean
: 해당 빈이 등록되어있지 않은 경우@ComponentScan
루트 패키지 이하의 클래스 중 @Component 어노테이션이 붙은 클래스들을 검색
@Component의 종류
스프링은 @Component 이 선언된 포함한 클래스들의 인스턴스를 자동으로 생성