启动配置原理
重要的事件回调机制:
ApplicationContextInitializer
SpringApplicationRunListener
ApplicationRunner
CommandLineRunner
前两者需要配置在META-INF/spring.factories中,而后两者只需要放在ioc容器中。
启动流程
1、创建SpringApplication对象:
SpringApplication#run
public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
return (new SpringApplication(sources)).run(args);
}
首先根据主类创建SpringApplication对象。
public SpringApplication(Object... sources) {
//初始化对象
initialize(sources);
}
2、进行初始化工作:
SpringApplication#initialize
private void initialize(Object[] sources) {
if (sources != null && sources.length > 0) {
//保存主配置类
this.sources.addAll(Arrays.asList(sources));
}
//判断是否是web应用,源码#2.1
this.webEnvironment = deduceWebEnvironment();
//从类路径下找到META-INF/spring.factories配置的所有ApplicationContextInitializer,然后保存起来,源码#2.2
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
//从类路径下找到META-INF/spring.factories配置的所有ApplicationListener,同上
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//从多个主配置类中找到有main方法的主类,源码#2.3
this.mainApplicationClass = deduceMainApplicationClass();
}
#2.1:SpringApplication#deduceWebEnvironment
private static final String[] WEB_ENVIRONMENT_CLASSES = new String[]{"javax.servlet.Servlet", "org.springframework.web.context.ConfigurableWebApplicationContext"};
private boolean deduceWebEnvironment() {
for (String className : WEB_ENVIRONMENT_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return false;
}
}
return true;
}
从WEB_ENVIRONMENT_CLASSES集合中遍历元素判断当前环境中是否有web应用的两个重要的依赖。
#2.2:SpringApplication#getSpringFactoriesInstances
从类路径下的所有jar包里的META-INF/spring.factories文件中获取所有type类型的类。
private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type) {
return getSpringFactoriesInstances(type, new Class<?>[] {});
}
private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
//获取ClassLoader
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
//使用ClassLoader根据type来获取所有的value值并返回name组成的集合,源码#2.2.1
Set<String> names = new LinkedHashSet<String>(
SpringFactoriesLoader.loadFactoryNames(type, classLoader));
//根据name值利用反射机制得到name所在的类实例,源码#2.2.2
List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
#2.2.1:SpringFactoriesLoader#loadFactoryNames
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
try {
//从类路径下所有jar包中查找META-INF/spring.factories文件路径
Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
List<String> result = new ArrayList<String>();
while(urls.hasMoreElements()) {
URL url = (URL)urls.nextElement();
//依次遍历所有的文件路径,从文件中获取key为classname对应value,并以逗号分隔加入到集合中
Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
String factoryClassNames = properties.getProperty(factoryClassName);
result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
}
//返回类型集合
return result;
} catch (IOException var8) {
throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() +
"] factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
#2.2.2:SpringApplication#createSpringFactoriesInstances
private <T> List<T> createSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args,
Set<String> names) {
List<T> instances = new ArrayList<T>(names.size());
for (String name : names) {
try {
Class<?> instanceClass = ClassUtils.forName(name, classLoader);
Assert.isAssignable(type, instanceClass);
Constructor<?> constructor = instanceClass
.getDeclaredConstructor(parameterTypes);
//创建实例
T instance = (T) BeanUtils.instantiateClass(constructor, args);
instances.add(instance);
}
catch (Throwable ex) {
throw new IllegalArgumentException(
"Cannot instantiate " + type + " : " + name, ex);
}
}
//返回实例
return instances;
}
#2.3:SpringApplication#deduceMainApplicationClass
private Class<?> deduceMainApplicationClass() {
try {
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
for (StackTraceElement stackTraceElement : stackTrace) {
//获取main方法的主类
if ("main".equals(stackTraceElement.getMethodName())) {
return Class.forName(stackTraceElement.getClassName());
}
}
}
catch (ClassNotFoundException ex) {
// Swallow and continue
}
return null;
}
以上代码完成SpringApplication对象创建。
所有的initializer集合,如图所示:
所有的listener集合,如图所示:
以上两个示意图中的集合数据要看具体依赖 ,依赖其他模块,可能就不一样了。
3、运行run方法
SpringApplication#runprivate ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
FailureAnalyzers analyzers = null;
configureHeadlessProperty();
//获取SpringApplicationRunListeners,从类路径下找到META-INF/spring.factories配置的所有SpringApplicationRunListener,源码#3.1
SpringApplicationRunListeners listeners = getRunListeners(args);
//回调所有的SpringApplicationRunListener的starting方法,源码#3.2
listeners.starting();
try {
//封装命令行参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
//准备环境:首先创建环境对象,创建完成后回调每个SpringApplicationRunListener对象的environmentPrepared方法,源码#3.3
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
//打印Banner,源码#3.4
Banner printedBanner = printBanner(environment);
//创建ApplicationContext,决定创建web的ioc容器还是普通的ioc容器,具体原理看原来文章(此处略)
context = createApplicationContext();
analyzers = new FailureAnalyzers(context);
//准备上下文,将environment保存到ioc容器中,源码#3.5
//prepareContext方法内部:
//首先applyInitializers(context);用来回调之前保存的所有的ApplicationContextInitializer的initialize方法
//然后listeners.contextPrepared(context);用来回调所有的SpringApplicationRunListener的contextPrepared方法
//最后listeners.contextLoaded(context);再回调所有的SpringApplicationRunListener的contextLoaded方法
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
//刷新ioc容器:首先创建ioc容器并初始化,如果是web应用还会创建嵌入式的Servlet容器,具体原理看原来文章(此处略)
refreshContext(context);
//从ioc容器中获取所有的ApplicationRunner和CommandLineRunner类,源码#3.6
//并先回调ApplicationRunner的run方法,再回调CommandLineRunner的run方法
afterRefresh(context, applicationArguments);
//执行所有的SpringApplicationRunListener的finished方法,源码#3.7
listeners.finished(context, null);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
//整个SpringBoot应用启动完成后返回ioc容器
return context;
}
catch (Throwable ex) {
handleRunFailure(context, listeners, analyzers, ex);
throw new IllegalStateException(ex);
}
}
#3.1:SpringApplication#getRunListeners
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
SpringApplicationRunListener.class, types, this, args));
}
从类路径下找到所有META-INF/spring.factories文件中配置的所有SpringApplicationRunListener类,并保存到SpringApplicationRunListeners对象的listeners对象集合中
#3.2:SpringApplicationRunListeners#starting
public void starting() {
for (SpringApplicationRunListener listener : this.listeners) {
listener.starting();
}
}
调用所有SpringApplicationRunListener对象的starting方法。
#3.3:SpringApplication#prepareEnvironment
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {
//获取或者创建环境,源码#3.3.1
ConfigurableEnvironment environment = getOrCreateEnvironment();
//配置环境对象,比如当前的profiles,源码#3.3.2
configureEnvironment(environment, applicationArguments.getSourceArgs());
//调用每个SpringApplicationRunListener对象的environmentPrepared方法,源码#3.3.3
listeners.environmentPrepared(environment);
if (!this.webEnvironment) {
environment = new EnvironmentConverter(getClassLoader())
.convertToStandardEnvironmentIfNecessary(environment);
}
//返回环境对象
return environment;
}
#3.3.1:SpringApplication#getOrCreateEnvironment
private ConfigurableEnvironment getOrCreateEnvironment() {
if (this.environment != null) {
return this.environment;
}
if (this.webEnvironment) {
return new StandardServletEnvironment();
}
return new StandardEnvironment();
}
在上面第2章节中deduceWebEnvironment()方法返回是否是web环境标识,如果是web环境,webEnvironment则为true,否则为false。在此getOrCreateEnvironment()方法中根据webEnvironment的值创建环境对象,如果webEnvironment为true,则创建StandardServletEnvironment类型的环境对象,否则创建StandardEnvironment类型的环境对象。
#3.3.2:SpringApplication#configureEnvironment
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
this.configurePropertySources(environment, args);
//配置当前环境的profiles,默认为default
this.configureProfiles(environment, args);
}
#3.3.3:SpringApplicationRunListeners#environmentPrepared
public void environmentPrepared(ConfigurableEnvironment environment) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.environmentPrepared(environment);
}
}
调用所有SpringApplicationRunListener对象的environmentPrepared方法。
#3.4:SpringApplication#printBanner
private Banner printBanner(ConfigurableEnvironment environment) {
//判断当前bannerMode是否为OFF
if (this.bannerMode == Banner.Mode.OFF) {
return null;
} else {
ResourceLoader resourceLoader = (this.resourceLoader != null ? this.resourceLoader
: new DefaultResourceLoader(getClassLoader()));
//获取BannerPrinter打印对象
SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(
resourceLoader, this.banner);
//根据BannerMode的值,来打印Banner,源码#3.4.1
if (this.bannerMode == Mode.LOG) {
return bannerPrinter.print(environment, this.mainApplicationClass, logger);
}
return bannerPrinter.print(environment, this.mainApplicationClass, System.out);
}
}
#3.4.1:SpringApplicationBannerPrinter#print
public Banner print(Environment environment, Class<?> sourceClass, PrintStream out) {
//获取Banner对象
Banner banner = getBanner(environment, this.fallbackBanner);
//依次调用Banners集合中的Banner对象的printBanner来开始打印Banner,源码#3.4.1.1
banner.printBanner(environment, sourceClass, out);
return new PrintedBanner(banner, sourceClass);
}
private Banner getBanner(Environment environment, Banner definedBanner) {
Banners banners = new Banners();
//从image图片格式中获取banner对象,如果有则保存到banners集合中
banners.addIfNotNull(getImageBanner(environment));
//从text文本格式中获取banner对象,如果有则保存到banners集合中
banners.addIfNotNull(getTextBanner(environment));
//banners集合个数只有0,1,2三种情况,然后判断banners集合是否为空,如果不为空,则返回之前获取到的Banners集合对象
if (banners.hasAtLeastOneBanner()) {
return banners;
}
if (this.fallbackBanner != null) {
return this.fallbackBanner;
}
return DEFAULT_BANNER;
}
static final String BANNER_LOCATION_PROPERTY = "banner.location";
static final String DEFAULT_BANNER_LOCATION = "banner.txt";
private Banner getTextBanner(Environment environment) {
//首先从环境对象中获取banner.location的值,如果存在直接返回该值,否则使用默认的banner.txt文件作为location的值
String location = environment.getProperty(BANNER_LOCATION_PROPERTY,
DEFAULT_BANNER_LOCATION);
//根据location的值获取Resource对象
Resource resource = this.resourceLoader.getResource(location);
//判断Resource是否存在,如果存在返回ResourceBanner,否则返回null
if (resource.exists()) {
return new ResourceBanner(resource);
}
return null;
}
static final String[] IMAGE_EXTENSION = new String[]{"gif", "jpg", "png"};
static final String BANNER_IMAGE_LOCATION_PROPERTY = "banner.image.location";
private Banner getImageBanner(Environment environment) {
//首先从环境对象中获取banner.image.location的值,如果存在直接返回该值
String location = environment.getProperty(BANNER_IMAGE_LOCATION_PROPERTY);
//判断是否有值
if (StringUtils.hasLength(location)) {
//如果banner.image.location有值,则根据该值返回Resource对象,并判断是否存在,如果存在则返回ImageBanner,否则返回null
Resource resource = this.resourceLoader.getResource(location);
return (resource.exists() ? new ImageBanner(resource) : null);
}
//如果banner.image.location没有值或者未设置,则依次判断banner.gif,banner.jpg,banner.png三种文件是否存在,如果某个文件存在,则直接返回该文件的ImageBanner对象,否则返回null
for (String ext : IMAGE_EXTENSION) {
Resource resource = this.resourceLoader.getResource("banner." + ext);
if (resource.exists()) {
return new ImageBanner(resource);
}
}
return null;
}
#3.4.1.1:SpringApplicationBannerPrinter#printBanner
@Override
public void printBanner(Environment environment, Class<?> sourceClass,
PrintStream out) {
for (Banner banner : this.banners) {
//依次调用每个banner对象的pringBanner方法
banner.printBanner(environment, sourceClass, out);
}
}
ResourceBanner#printBanner
@Override
public void printBanner(Environment environment, Class<?> sourceClass,
PrintStream out) {
try {
String banner = StreamUtils.copyToString(this.resource.getInputStream(),
environment.getProperty("banner.charset", Charset.class,
Charset.forName("UTF-8")));
for (PropertyResolver resolver : getPropertyResolvers(environment,
sourceClass)) {
banner = resolver.resolvePlaceholders(banner);
}
out.println(banner);
}
catch (Exception ex) {
//other code...
}
}
ImageBanner#printBanner
@Override
public void printBanner(Environment environment, Class<?> sourceClass,
PrintStream out) {
String headless = System.getProperty("java.awt.headless");
try {
System.setProperty("java.awt.headless", "true");
printBanner(environment, out);
}
//catch...finally...
}
#3.5:SpringApplication#prepareContext
private void prepareContext(ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
postProcessApplicationContext(context);
//回调所有的initializer对象的initialize方法,源码#3.5.1
applyInitializers(context);
//回调所有的listener对象的contextPrepared方法,源码#3.5.2
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
//other code...
//回调所有的listener对象的contextLoaded方法,源码#3.5.3
listeners.contextLoaded(context);
}
#3.5.1:SpringApplication#applyInitializers
protected void applyInitializers(ConfigurableApplicationContext context) {
//依次遍历所有的ApplicationContextInitializer对象,调用其initialize方法
for (ApplicationContextInitializer initializer : getInitializers()) {
Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(
initializer.getClass(), ApplicationContextInitializer.class);
Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
initializer.initialize(context);
}
}
#3.5.2:SpringApplicationRunListeners#contextPrepared
public void contextPrepared(ConfigurableApplicationContext context) {
//依次遍历所有的SpringApplicationRunListener对象,调用其contextPrepared方法
for (SpringApplicationRunListener listener : this.listeners) {
listener.contextPrepared(context);
}
}
#3.5.3 : SpringApplicationRunListeners#contextLoaded
public void contextPrepared(ConfigurableApplicationContext context) {
//依次遍历所有的SpringApplicationRunListener对象,调用其contextPrepared方法
for (SpringApplicationRunListener listener : this.listeners) {
listener.contextPrepared(context);
}
}
#3.6:SpringApplication#afterRefresh
protected void afterRefresh(ConfigurableApplicationContext context,
ApplicationArguments args) {
//调用所有的xxxRunner的run方法
callRunners(context, args);
}
private void callRunners(ApplicationContext context, ApplicationArguments args) {
List<Object> runners = new ArrayList<Object>();
//从ioc容器中获取所有的ApplicationRunner和CommandLineRunner类
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
AnnotationAwareOrderComparator.sort(runners);
//先回调所有ApplicationRunner对象的run方法,在回调所有CommandLineRunner对象的run方法
for (Object runner : new LinkedHashSet<Object>(runners)) {
if (runner instanceof ApplicationRunner) {
callRunner((ApplicationRunner) runner, args);
}
if (runner instanceof CommandLineRunner) {
callRunner((CommandLineRunner) runner, args);
}
}
}
#3.7:SpringApplicationRunListeners#finished
public void finished(ConfigurableApplicationContext context, Throwable exception) {
//依次遍历所有的SpringApplicationRunListener对象,调用其finished方法
for (SpringApplicationRunListener listener : this.listeners) {
callFinishedListener(listener, context, exception);
}
}
private void callFinishedListener(SpringApplicationRunListener listener,
ConfigurableApplicationContext context, Throwable exception) {
try {
listener.finished(context, exception);
}
catch (Throwable ex) {
//other code...
}
}
这样整个SpringBoot应用启动就完成并返回ioc容器。
测试用例
分别编写四个事件回调类的子类:
ApplicationContextInitializer子类:HelloApplicationContextInitializer
public class HelloApplicationContextInitializer implements ApplicationContextInitializer {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
System.out.println("HelloApplicationContextInitializer...initialize...");
}
}
SpringApplicationRunListener子类:HelloSpringApplicationRunListener
public class HelloSpringApplicationRunListener implements SpringApplicationRunListener {
//SpringApplicationRunListener子类必须有一个有参构造函数,参数分别是SpringApplication和String[]
public HelloSpringApplicationRunListener(SpringApplication springApplication, String[] args){
System.out.println("HelloSpringApplicationRunListener...constructor...");
}
@Override
public void starting() {
System.out.println("HelloSpringApplicationRunListener...starting...");
}
@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
System.out.println("HelloSpringApplicationRunListener...environmentPrepared...");
}
@Override
public void contextPrepared(ConfigurableApplicationContext context) {
System.out.println("HelloSpringApplicationRunListener...contextPrepared...");
}
@Override
public void contextLoaded(ConfigurableApplicationContext context) {
System.out.println("HelloSpringApplicationRunListener...contextLoaded...");
}
@Override
public void finished(ConfigurableApplicationContext context, Throwable exception) {
System.out.println("HelloSpringApplicationRunListener...finished...");
}
}
ApplicationRunner子类:HelloApplicationRunner
@Component
public class HelloApplicationRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("HelloApplicationRunner...run...");
}
}
CommandLineRunner子类:HelloCommandLineRunner
@Component
public class HelloCommandLineRunner implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
System.out.println("HelloCommandLineRunner...run...");
}
}
在resources目录下新建META-INF文件夹,并新建一个文件,文件名为spring.factories,其内容为:
org.springframework.context.ApplicationContextInitializer=\
org.com.cya.spring.boot.springboot07starter.initilizer.HelloApplicationContextInitializer
org.springframework.boot.SpringApplicationRunListener=\
org.com.cya.spring.boot.springboot07starter.listener.HelloSpringApplicationRunListener
启动服务器之后,查看控制台输出的信息(忽略Spring Boot打印信息):
HelloSpringApplicationRunListener...constructor...
HelloSpringApplicationRunListener...starting...
HelloSpringApplicationRunListener...environmentPrepared...
HelloApplicationContextInitializer...initialize...
HelloSpringApplicationRunListener...contextPrepared...
HelloSpringApplicationRunListener...contextLoaded...
HelloApplicationRunner...run...
HelloCommandLineRunner...run...
HelloSpringApplicationRunListener...finished...
其结果跟源码分析过程一致。
====================打个广告,欢迎关注====================
QQ: | 412425870 |
微信公众号:Cay课堂 | |
csdn博客: | http://blog.csdn.net/caychen |
码云: | https://gitee.com/caychen/ |
github: | https://github.com/caychen |
点击群号或者扫描二维码即可加入QQ群: | |
|