开发工具:Intellij IDEA 2017(盗)版
java版本:1.8.0_151
spring的github地址:spring-framework
准备:git clone或直接下载github上的spring源码,导入idea中,在项目路径下执行gradle build (如果本机没有gradle环境,或者版本差很多,就用gradlew代替),会build很久,可以事先将阿里的maven仓库地址加到repositories中,像这样:
repositories {
maven {
url "http://maven.aliyun.com/nexus/content/groups/public/"
}
maven { url "https://repo.spring.io/plugins-release" }
}
会用到的缩写:
/**
* @author pk
* @date 2018/02/22
*/
@SpringBootApplication
public class SpringBootTwoStudyApplication {
public static void main(String[] args) {
SpringApplication springApplication = new SpringApplication(SpringBootTwoStudyApplication.class);
//可以在run之前,对AC进行一些自定义的配置,添加点ApplicationListener,ApplicationContextInitializer啥的
springApplication.run(args);
}
}
代码如下:
public SpringApplication(ResourceLoader resourceLoader, Class>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));//1
this.webApplicationType = deduceWebApplicationType();//2
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));//3
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));//4
this.mainApplicationClass = deduceMainApplicationClass();//5
}
这个启动类就是在初始化SpringApplication时候的参数,可以有多个
根据当前classpath下存在的类来判断的:
代码如下:
private Collection getSpringFactoriesInstances(Class type,
Class>[] parameterTypes, Object... args) {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();//3.1
Set names = new LinkedHashSet<>(
SpringFactoriesLoader.loadFactoryNames(type, classLoader));//3.2
List instances = createSpringFactoriesInstances(type, parameterTypes,
classLoader, args, names);//3.3
AnnotationAwareOrderComparator.sort(instances);//3.4
return instances;
}
TODO(ClassLoader的相关解释)
会得到如些几个类(实际是全称类名):
根据Ordered接口/@PriorityOrder注解/@Order注解去排
其他地方的排序基本也是按照这个规则来排的
跟设置ACI的方法一样,也是读取spring.factories文件中的对应key项所对应的所有类名,然后实例化、排序.
得到如下几个ApplicationListener:
就是找到main方法所在的类.是唯一的,跟上面的primarySources不是一回事
代码如下:
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();//1
ConfigurableApplicationContext context = null;
Collection exceptionReporters = new ArrayList<>();
configureHeadlessProperty();//2
SpringApplicationRunListeners listeners = getRunListeners(args);//3
listeners.starting();//4
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);//5
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);//6
configureIgnoreBeanInfo(environment);//7
Banner printedBanner = printBanner(environment);//8
context = createApplicationContext();//9
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);//10
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);//11
refreshContext(context);//12
afterRefresh(context, applicationArguments);//13
listeners.finished(context, null);//14
stopWatch.stop();
//15
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
callRunners(context, applicationArguments);//16
return context;
}
catch (Throwable ex) {
handleRunFailure(context, listeners, exceptionReporters, ex);
throw new IllegalStateException(ex);
}
}
这个headless是一种模式,是指缺少显示屏、键盘或者鼠标时的系统配置
我找了spring和spring boot的源码,只有ImageBanner里面用到了
StringApplicationListeners就是对对个SpringApplicationRunListener的包装
现在看只有一个具体实现类
虽然这个类叫做listener,但其实起的作用是传播事件,更像是一个ApplicationEventMulticaster。
spring boot启动过程中的事件,主要由这个类来传播
关于spring事件发布机制,请看spring事件机制
响应的AL
ApplicationArguments的接口定义:
public interface ApplicationArguments {
String[] getSourceArgs();
//--开头的参数
Set getOptionNames();
boolean containsOption(String name);
//--开头的参数是允许重复定义的,不会覆盖
List getOptionValues(String name);
List getNonOptionArgs();
}
这个类的作用就是解析所有的启动参数的
代码如下:
private ConfigurableEnvironment prepareEnvironment(
SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
ConfigurableEnvironment environment = getOrCreateEnvironment();//6.1
configureEnvironment(environment, applicationArguments.getSourceArgs());//6.2
listeners.environmentPrepared(environment);//6.3
bindToSpringApplication(environment);//6.4
if (this.webApplicationType == WebApplicationType.NONE) {
environment = new EnvironmentConverter(getClassLoader())
.convertToStandardEnvironmentIfNecessary(environment);
}
ConfigurationPropertySources.attach(environment);//6.5
return environment;
}
关于spring中Environment/PropertySource的介绍:
根据webApplicationType:
初始化的时候会在构造函数中调用customizePropertySources()方法,其结果是会在environment的propertySources属性的propertySourcesList列表中加入以下PropertySource:
这是一个很重要的事件,响应的listener有很多:
todo…
NONE -> AnnotationConfigAC
按照继承结构: