此篇文章记录自己学习,加深自己的理解,以及设计模式的运用。也请各位可以给出理解不足之处
准备步骤
1,搭建springboot例子
根据springboot官方网站,搭建简单的springboot工程,gs-spring-boot。
2,主类代码
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Bean
public CommandLineRunner commandLineRunner(ApplicationContext ctx) {
return args -> {
System.out.println("Let's inspect the beans provided by Spring Boot:");
String[] beanNames = ctx.getBeanDefinitionNames();
Arrays.sort(beanNames);
for (String beanName : beanNames) {
System.out.println(beanName);
}
};
}
}
源码分析
以上操作结束后,开始对SpringApplication.run方法的分析,进入方法后,我们看到如下源码:
public static ConfigurableApplicationContext run(Class<?> primarySource,
String... args) {
return run(new Class<?>[] { primarySource }, args);
}
public static ConfigurableApplicationContext run(Class<?>[] primarySources,
String[] args) {
return new SpringApplication(primarySources).run(args);
}
在第二个方法中,加载了两部分内容:
1,SpringApplication的构造方法。
2,SpringApplication的run方法。
1,构造方法
在SpringApplication的构造方法的方法中。
public SpringApplication(Class<?>... primarySources) {
this(null, primarySources);
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
// 此处传入的null
this.resourceLoader = resourceLoader;
// 判断加载的自定义的启动类的class
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 默认是SERVLET的web应用程序
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 初始化的工厂,由工厂来加载spring的容器
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
// 初始化的工厂,由工厂来加载spring的监听
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
ResourceLoader 使用策略模式加载资源的顶层接口。
构造方法中,主要定义了资源类文件,将springboot启动类存入Set中(有顺序要求)。获取用到的Application的启动类别是REACTIVE
还是SERVLET
根据ApplicationContextInitializer.class
和ApplicationListener.class
从META-INF/spring.factories
文件中获取对应的类并实例化,使用setInitializers
和setListeners
分别存储。其中都是有序的集合。
getSpringFactoriesInstances
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
return getSpringFactoriesInstances(type, new Class<?>[] {});
}
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, Object... args) {
// 此方法源码作者对class文件加载模型的有深入理解,非常符合类的加载机制,getClassLoader该方法非常经典
ClassLoader classLoader = getClassLoader();
// Use names and ensure unique to protect against duplicates
// 加载springboot启动需要加载的配置类,加入到cache中,配置类路径为META-INF/spring.factories
Set<String> names = new LinkedHashSet<>(
SpringFactoriesLoader.loadFactoryNames(type, classLoader));
// 通过ApplicationContextInitializer和ApplicationListener的构造信息,分别取的信息和访问,通过该构造加载方法实例化配置类信息
List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
classLoader, args, names);
// 对实例化的类进行排序
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
其实从META-INF/spring.factories
中读取类的时候是有序的,在实例化时变成无序,因此需要重新排序。AnnotationAwareOrderComparator
由父级实现了Comparator
接口。
由此可见构造方法中只是准备了启动要准备的资源文件。
由此可以看出,根据ApplicationContextInitializer.class取出的类并实例化的类有:
org.springframework.context.ApplicationContextInitializer=
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
根据ApplicationListener.class取出的类并实例化的类有:
org.springframework.context.ApplicationListener=
org.springframework.boot.ClearCachesApplicationListener,
org.springframework.boot.builder.ParentContextCloserApplicationListener,
org.springframework.boot.context.FileEncodingApplicationListener,
org.springframework.boot.context.config.AnsiOutputApplicationListener,
org.springframework.boot.context.config.ConfigFileApplicationListener,
org.springframework.boot.context.config.DelegatingApplicationListener,
org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,
org.springframework.boot.context.logging.LoggingApplicationListener,
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
2run方法
run方式中的源码主要如下:
// main方法中参数处理
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
// 此处的listeners,是从META-INF\spring.factories取出并实例化的
// org.springframework.boot.context.event.EventPublishingRunListener
// 在构造方法中我们的spring默认的web应用是servlet,此处实例化了
// StandardServletEnvironment对象
// 并加载了web应用的一些配置,并启用了Executor
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
// 设置spring.beaninfo.ignore
configureIgnoreBeanInfo(environment);
// 在日志控制台打印的很酷的spring的字样
Banner printedBanner = printBanner(environment);
// 实例化
// org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext
// 加载注解类
context = createApplicationContext();
// 实例化异常报告
// org.springframework.boot.diagnostics.FailureAnalyzers
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
// 单独分析,前期加载容器
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
// 单独分析,加载容器中的配置
refreshContext(context);
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
// 开始监听
listeners.started(context);
callRunners(context, applicationArguments);
prepareContext方法
private void prepareContext(ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
// 将ConfigurableEnvironment放入上下文容器
context.setEnvironment(environment);
// 根据前面的处理加载一些配置信息,此处我的理解是作者对以上加载类做的后续补充
postProcessApplicationContext(context);
// 单例加载上下文容器
applyInitializers(context);
// 将容器内容加载到监听中,并启用了Executor
listeners.contextPrepared(context);
// 启动日志
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
// 获取bean工厂
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
// 由bean工厂来注册main的参数
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
// 由bean工厂来注册很酷的启动时打印的spring的字样,该字样可以配置,由使用者配置
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
// Load the sources
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[0]));
// 加入到监听并启用了Executor,此处方法和contextPrepared功能一样
listeners.contextLoaded(context);
}
refreshContext方法,此处方法比较复杂
private void refreshContext(ConfigurableApplicationContext context) {
refresh(context);
if (this.registerShutdownHook) {
try {
// 注册一个新的虚拟机关机挂钩。
context.registerShutdownHook();
}
catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
}
protected void refresh(ApplicationContext applicationContext) {
Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
((AbstractApplicationContext) applicationContext).refresh();
}
此处的((AbstractApplicationContext) applicationContext).refresh();
主要分析
org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext
@Override
public final void refresh() throws BeansException, IllegalStateException {
try {
super.refresh();
}
catch (RuntimeException ex) {
stopAndReleaseWebServer();
throw ex;
}
}
该类的父类中的refresh方法,AbstractApplicationContext
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 准备此上下文以进行刷新
// 注解的前期准备并加载到监听中,主要分析
// AnnotationConfigServletWebServerApplicationContext
prepareRefresh();
// 告诉子类刷新内部bean工厂
// 此方法是抽象方法,是刷新子类中的工厂bean,即ServletWebServerApplicationContext中的工厂类
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 准备bean工厂以在此上下文中使用,加载较多的类
prepareBeanFactory(beanFactory);
try {
// 允许在上下文子类中对bean工厂进行后处理,抽象方法,查看ServletWebServerApplicationContext中的方法,主要也是加载了一些web应该范围如request和response等
postProcessBeanFactory(beanFactory);
// 在上下文中调用注册为bean的工厂处理器,加载注解类
invokeBeanFactoryPostProcessors(beanFactory);
// 注册拦截bean创建的bean处理器,拦截器的处理
registerBeanPostProcessors(beanFactory);
// 初始化此上下文的消息源
initMessageSource();
// 初始化此上下文的事件多播器
initApplicationEventMulticaster();
// 在特定的上下文子类中初始化其他特殊bean
// 抽象方法,查看即ServletWebServerApplicationContext中的onRefresh方法
onRefresh();
// 检查监听器bean并注册
registerListeners();
// 实例化所有剩余(非延迟初始化)单例.
finishBeanFactoryInitialization(beanFactory);
// 发布相应的事件
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();
}
}
}
注解SpringBootApplication
在整个启动的过程中,SpringBootApplication注解是怎么回事?在上述中的哪一步加载到上下文容器中?
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication
SpringBootApplication使用了EnableAutoConfiguration注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguratio
EnableAutoConfiguratio导入了AutoConfigurationImportSelector类
public class AutoConfigurationImportSelector
implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware,
BeanFactoryAware, EnvironmentAware, Ordered
在上下文容器进行加载时,由ConfigurationClassPostProcessor来处理如下自定义的类,包括其注解类。
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
小结
简单过了一遍源码,分析了SpringApplication.run的启动,以及加载类的一些主要方法。
可以看到Spring 应用上下文生命周期控制,注解驱动bean;Spring事件/监听机制加载初始化组件。有各自职责的工厂负责初始化不同功能的类;又定义了不同的监听对象服务不同类别加载过程中出现的事件。