请尊重作者劳动成果,转载请标明原文链接:http://www.cnblogs.com/dylan-java/p/7455699.html
上一篇分析了SpringApplication的初始化,接下来看一下它的run方法
1 public ConfigurableApplicationContext run(String... args) { 2 StopWatch stopWatch = new StopWatch(); 3 stopWatch.start(); 4 ConfigurableApplicationContext context = null; 5 FailureAnalyzers analyzers = null; 6 configureHeadlessProperty(); 7 SpringApplicationRunListeners listeners = getRunListeners(args); 8 listeners.starting(); 9 try { 10 ApplicationArguments applicationArguments = new DefaultApplicationArguments( 11 args); 12 ConfigurableEnvironment environment = prepareEnvironment(listeners, 13 applicationArguments); 14 Banner printedBanner = printBanner(environment); 15 context = createApplicationContext(); 16 analyzers = new FailureAnalyzers(context); 17 prepareContext(context, environment, listeners, applicationArguments, 18 printedBanner); 19 refreshContext(context); 20 afterRefresh(context, applicationArguments); 21 listeners.finished(context, null); 22 stopWatch.stop(); 23 if (this.logStartupInfo) { 24 new StartupInfoLogger(this.mainApplicationClass) 25 .logStarted(getApplicationLog(), stopWatch); 26 } 27 return context; 28 } 29 catch (Throwable ex) { 30 handleRunFailure(context, listeners, analyzers, ex); 31 throw new IllegalStateException(ex); 32 } 33 }
首先创建一个StopWatch对象并调用它的start方法,该类是Spring提供的一个计时器类,与本篇要讨论的东西无关,所在在这里不对它进行分析
第6行调用了configureHeadlessProperty方法,该方法只有一行代码,就是设置系统属性java.awt.headless,这里设置为true,表示运行在服务器端,在没有显示器和鼠标键盘的模式下工作,模拟输入输出设备功能
第7行通过getRunListeners方法获取SpringApplicationRunListeners对象,这个对象是一个SpringBoot事件广播器的管理者,它可以包含多个SpringApplicationRunListener,SpringApplication类中使用它们来间接调用ApplicationListener
在classpath下的JAR文件中包含的/META/spring.factories文件里找到org.springframework.boot.SpringApplicationRunListener对应的属性,然后实例化并排序
在jar:file:/C:/Users/guiqingqing/.m2/repository/org/springframework/boot/spring-boot/1.5.6.RELEASE/spring-boot-1.5.6.RELEASE.jar!/META-INF/spring.factories文件找到如下配置
# Run Listeners org.springframework.boot.SpringApplicationRunListener=\ org.springframework.boot.context.event.EventPublishingRunListener
这里只有一个,就是EventPublishingRunListener,然后作为参数调用SpringApplicationRunListeners的构造函数,创建对象并返回,EventPublishingRunListener对象的实例被添加到SpringApplicationRunListeners对象的listeners属性中
看一下EventPublishingRunListener的构造函数
1 public EventPublishingRunListener(SpringApplication application, String[] args) { 2 this.application = application; 3 this.args = args; 4 this.initialMulticaster = new SimpleApplicationEventMulticaster(); 5 for (ApplicationListener<?> listener : application.getListeners()) { 6 this.initialMulticaster.addApplicationListener(listener); 7 } 8 }
内部会创建一个Spring广播器SimpleApplicationEventMulticaster对象,它实际上是一个事件广播器,EventPublishingRunListener实现了SpringApplicationRunListener接口,我们看一下SpringApplicationRunListener接口的定义
1 public interface SpringApplicationRunListener { 2 void starting(); 3 4 void environmentPrepared(ConfigurableEnvironment environment); 5 6 void contextPrepared(ConfigurableApplicationContext context); 7 8 void contextLoaded(ConfigurableApplicationContext context); 9 10 void finished(ConfigurableApplicationContext context, Throwable exception); 11 }
该接口规定了SpringBoot的生命周期,在各个生命周期广播相应的事件,调用实际的ApplicationListener的onApplicationEvent方法
回到SpringApplication的run方法
第8行调用listeners的starting方法
1 public void starting() { 2 for (SpringApplicationRunListener listener : this.listeners) { 3 listener.starting(); 4 } 5 }
该方法遍历所有的listeners,当然这里只有一个(EventPublishingRunListener),调用它的starting方法
1 @Override 2 @SuppressWarnings("deprecation") 3 public void starting() { 4 this.initialMulticaster.multicastEvent(new ApplicationStartedEvent(this.application, this.args)); 5 }
这里会使用广播器(SimpleApplicationEventMulticaster)去广播事件,创建一个ApplicationStartedEvent对象作为参数
1 @Override 2 public void multicastEvent(ApplicationEvent event) { 3 multicastEvent(event, resolveDefaultEventType(event)); 4 } 5 6 @Override 7 public void multicastEvent(final ApplicationEvent event, ResolvableType eventType) { 8 ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event)); 9 for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) { 10 Executor executor = getTaskExecutor(); 11 if (executor != null) { 12 executor.execute(new Runnable() { 13 @Override 14 public void run() { 15 invokeListener(listener, event); 16 } 17 }); 18 } 19 else { 20 invokeListener(listener, event); 21 } 22 } 23 }
可以看出multicastEvent方法会找到ApplicationListener的集合,然后依次调用invokeListener方法,而invokeListener方法内部则会调用ApplicationListener对象的onApplicationEvent方法
第10行创建一个DefaultApplicationArguments对象,它持有着args参数,就是main函数传进来的参数
第12行调用prepareEnvironment方法,该方法准备运行的环境,比如开发环境dev,测试环境test,还是生产环境prd,然后根据环境解析不同的配置文件
1 private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) { 2 // Create and configure the environment 3 ConfigurableEnvironment environment = getOrCreateEnvironment(); 4 configureEnvironment(environment, applicationArguments.getSourceArgs()); 5 listeners.environmentPrepared(environment); 6 if (!this.webEnvironment) { 7 environment = new EnvironmentConverter(getClassLoader()) 8 .convertToStandardEnvironmentIfNecessary(environment); 9 } 10 return environment; 11 }
getOrCreateEnvironment方法会检查之前设置的webEnvironment属性,如果是web程序,那么创建一个StandardServletEnvironment对象并返回,如果不是web程序,那么创建一个StandardEnvironment对象并返回
接下来是configureEnvironment方法,该方法会对上一步返回的environment做进一步的配置,调用configurePropertySources和configureProfiles,比如main函数传进来参数"--spring.profiles.active=dev",那么会在configurePropertySources方法里被添加到CommandLineArgs对象的optionArgs属性,不是以"--"开头的参数会被添加到nonOptionArgs属性。而configureProfiles方法这个时候就会获取到dev作为当前有效的profile并添加到environment的activeProfiles属性中,本例不传参数,所以基本上configureEnvironment方法什么都不做
然后调用listeners的environmentPrepared方法,发布一个ApplicationEnvironmentPreparedEvent事件,通过事件广播器,依次调用每个ApplicationListener对象的onApplicationEvent方法,这里我们重点分析ConfigFileApplicationListener的onApplicationEvent方法
1 @Override 2 public void onApplicationEvent(ApplicationEvent event) { 3 if (event instanceof ApplicationEnvironmentPreparedEvent) { 4 onApplicationEnvironmentPreparedEvent( 5 (ApplicationEnvironmentPreparedEvent) event); 6 } 7 if (event instanceof ApplicationPreparedEvent) { 8 onApplicationPreparedEvent(event); 9 } 10 }
1 private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) { 2 List<EnvironmentPostProcessor> postProcessors = loadPostProcessors(); 3 postProcessors.add(this); 4 AnnotationAwareOrderComparator.sort(postProcessors); 5 for (EnvironmentPostProcessor postProcessor : postProcessors) { 6 postProcessor.postProcessEnvironment(event.getEnvironment(), 7 event.getSpringApplication()); 8 } 9 }
首先loadPostProcessors方法会去classpath下的JAR文件中包含的/META/spring.factories文件里找到org.springframework.boot.env.EnvironmentPostProcessor对应的属性,然后实例化并排序,设置到postProcessors变量,在把当前对象(ConfigFileApplicationListener的实例)也添加到postProcessors变量中,然后对postProcessors排序,遍历postProcessors,依次调用它们的postProcessEnvironment方法。我们来看ConfigFileApplicationListener的postProcessEnvironment方法
1 @Override 2 public void postProcessEnvironment(ConfigurableEnvironment environment, 3 SpringApplication application) { 4 addPropertySources(environment, application.getResourceLoader()); 5 configureIgnoreBeanInfo(environment); 6 bindToSpringApplication(environment, application); 7 }
addPropertySources方法最终会调用到ConfigFileApplicationListener的内部类Loader的load方法,这个方法是解析我们配置的application.yml和application-dev.yml文件的实现所在
1 public void load() { 2 this.propertiesLoader = new PropertySourcesLoader(); 3 this.activatedProfiles = false; 4 this.profiles = Collections.asLifoQueue(new LinkedList<Profile>()); 5 this.processedProfiles = new LinkedList<Profile>(); 6 7 // Pre-existing active profiles set via Environment.setActiveProfiles() 8 // are additional profiles and config files are allowed to add more if 9 // they want to, so don't call addActiveProfiles() here. 10 Set<Profile> initialActiveProfiles = initializeActiveProfiles(); 11 this.profiles.addAll(getUnprocessedActiveProfiles(initialActiveProfiles)); 12 if (this.profiles.isEmpty()) { 13 for (String defaultProfileName : this.environment.getDefaultProfiles()) { 14 Profile defaultProfile = new Profile(defaultProfileName, true); 15 if (!this.profiles.contains(defaultProfile)) { 16 this.profiles.add(defaultProfile); 17 } 18 } 19 } 20 21 // The default profile for these purposes is represented as null. We add it 22 // last so that it is first out of the queue (active profiles will then 23 // override any settings in the defaults when the list is reversed later). 24 this.profiles.add(null); 25 26 while (!this.profiles.isEmpty()) { 27 Profile profile = this.profiles.poll(); 28 for (String location : getSearchLocations()) { 29 if (!location.endsWith("/")) { 30 // location is a filename already, so don't search for more 31 // filenames 32 load(location, null, profile); 33 } 34 else { 35 for (String name : getSearchNames()) { 36 load(location, name, profile); 37 } 38 } 39 } 40 this.processedProfiles.add(profile); 41 } 42 43 addConfigurationProperties(this.propertiesLoader.getPropertySources()); 44 }
25行之前基本上是一些变量的实例化,会往profiles集合中添加default和null,直接跳过,接下来从profiles集合中取出对应的值进行解析,首先取出null
第28行getSearchLocations方法,首先会检查当前环境是否配置了spring.config.location,如果没有,那么会使用默认的搜索路径classpath:/,classpath:/config/,file:./,file:./config/,这里调用了Collections.reverse方法进行倒序,也就是先查找file:./config/,在查找file:./,再是classpath:/config/,最后查找classpath:/。笔者的application.yml和application-dev.yml都是直接在src/main/resources目录下,所以对应的是在location为classpath:/时调用load(String location, String name, Profile profile)方法(在没有配置spring.config.name的情况下,name默认为application)
1 private void load(String location, String name, Profile profile) { 2 String group = "profile=" + (profile == null ? "" : profile); 3 if (!StringUtils.hasText(name)) { 4 // Try to load directly from the location 5 loadIntoGroup(group, location, profile); 6 } 7 else { 8 // Search for a file with the given name 9 for (String ext : this.propertiesLoader.getAllFileExtensions()) { 10 if (profile != null) { 11 // Try the profile-specific file 12 loadIntoGroup(group, location + name + "-" + profile + "." + ext, 13 null); 14 for (Profile processedProfile : this.processedProfiles) { 15 if (processedProfile != null) { 16 loadIntoGroup(group, location + name + "-" 17 + processedProfile + "." + ext, profile); 18 } 19 } 20 // Sometimes people put "spring.profiles: dev" in 21 // application-dev.yml (gh-340). Arguably we should try and error 22 // out on that, but we can be kind and load it anyway. 23 loadIntoGroup(group, location + name + "-" + profile + "." + ext, 24 profile); 25 } 26 // Also try the profile-specific section (if any) of the normal file 27 loadIntoGroup(group, location + name + "." + ext, profile); 28 } 29 } 30 }
调用this.propertiesLoader.getAllFileExtensions()方法获取配置文件后缀名,分别是在PropertiesPropertySourceLoader类和YamlPropertySourceLoader类中getFileExtensions方法定义的
1 public class PropertiesPropertySourceLoader implements PropertySourceLoader { 2 @Override 3 public String[] getFileExtensions() { 4 return new String[] { "properties", "xml" }; 5 } 6 }
1 public class YamlPropertySourceLoader implements PropertySourceLoader { 2 @Override 3 public String[] getFileExtensions() { 4 return new String[] { "yml", "yaml" }; 5 } 6 }
第23行调用的loadIntoGroup方法里又调用doLoadIntoGroup方法,这个方法会检查配置文件是否存在,比如file:./config/下不存在,或是classpath:/下的application.properties不存在,最后发现classpath:/下的application.yml存在,然后对其进行解析
1 public PropertySource<?> load(Resource resource, String group, String name, 2 String profile) throws IOException { 3 if (isFile(resource)) { 4 String sourceName = generatePropertySourceName(name, profile); 5 for (PropertySourceLoader loader : this.loaders) { 6 if (canLoadFileExtension(loader, resource)) { 7 PropertySource<?> specific = loader.load(sourceName, resource, 8 profile); 9 addPropertySource(group, specific, profile); 10 return specific; 11 } 12 } 13 } 14 return null; 15 }
这里有2个loader,PropertiesPropertySourceLoader和YamlPropertySourceLoader,先检查是否该由此loader去解析,检查的规则就是看文件后缀名是否在getFileExtensions方法返回的数组中存在,很显然,这里应该由YamlPropertySourceLoader去解析,笔者的application.yml很简单,内容如下
spring: profiles: active: dev
解析完成之后,调用了一个handleProfileProperties方法,这个方法里又会调用maybeActivateProfiles方法,此方法的addProfiles方法会把解析到的dev添加到profiles中去,removeUnprocessedDefaultProfiles方法会删除之前添加的default(此时只剩下dev),如果配置了include,也会被解析
然后再次遍历file:./config/,file:./,classpath:/config/,classpath:/,这次是查找到application-dev.yml并解析,过程就不重复分析了
回到SpringApplication的run方法
第14行调用printBanner方法,打印SpringBoot的LOGO
1 private Banner printBanner(ConfigurableEnvironment environment) { 2 if (this.bannerMode == Banner.Mode.OFF) { 3 return null; 4 } 5 ResourceLoader resourceLoader = this.resourceLoader != null ? this.resourceLoader 6 : new DefaultResourceLoader(getClassLoader()); 7 SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter( 8 resourceLoader, this.banner); 9 if (this.bannerMode == Mode.LOG) { 10 return bannerPrinter.print(environment, this.mainApplicationClass, logger); 11 } 12 return bannerPrinter.print(environment, this.mainApplicationClass, System.out); 13 }
方法内部会创建一个SpringApplicationBannerPrinter的对象,并调用print方法
1 public Banner print(Environment environment, Class<?> sourceClass, PrintStream out) { 2 Banner banner = getBanner(environment, this.fallbackBanner); 3 banner.printBanner(environment, sourceClass, out); 4 return new PrintedBanner(banner, sourceClass); 5 }
通过getBanner方法获取Banner对象
1 private Banner getBanner(Environment environment, Banner definedBanner) { 2 Banners banners = new Banners(); 3 banners.addIfNotNull(getImageBanner(environment)); 4 banners.addIfNotNull(getTextBanner(environment)); 5 if (banners.hasAtLeastOneBanner()) { 6 return banners; 7 } 8 if (this.fallbackBanner != null) { 9 return this.fallbackBanner; 10 } 11 return DEFAULT_BANNER; 12 }
第一步,调用Banners的无参构造创建一个Banners的对象,然后我们先看一下SpringApplicationBannerPrinter类里定义的几个静态属性
1 static final String BANNER_LOCATION_PROPERTY = "banner.location"; 2 3 static final String BANNER_IMAGE_LOCATION_PROPERTY = "banner.image.location"; 4 5 static final String DEFAULT_BANNER_LOCATION = "banner.txt"; 6 7 static final String[] IMAGE_EXTENSION = { "gif", "jpg", "png" };
先看是否配置了系统属性banner.image.location,没有配置则在classpath中查找banner.gif,banner.jpg,banner.png,如果找到,则创建一个ImageBanner对象并添加到Banners对象的banners属性中,该属性是一个List类型,接下来看是否配置了banner.location属性,如果没有,则用默认的banner.txt,然后在classpath中查找banner.txt,如果找到,则创建一个ResourceBanner对象并添加到Banners对象的banners属性中,最后,如果Banners对象的banners不为空,也就是至少找到了banner.gif,banner.jpg,banner.png,banner.txt其中的一个,那么返回该Banners对象,否则返回默认的SpringBootBanner对象
接下来根据返回的不同Banners类型的对象调用具体的printBanner实现方法,所以如果要想打印自定义的LOGO,要么你在classpath下添加图片,要么添加banner.txt的文本文件,默认的SpringBootBanner会打印如下图
回到SpringApplication的run方法
第15行调用createApplicationContext方法,该方法创建SpringBoot的上下文
1 protected ConfigurableApplicationContext createApplicationContext() { 2 Class<?> contextClass = this.applicationContextClass; 3 if (contextClass == null) { 4 try { 5 contextClass = Class.forName(this.webEnvironment 6 ? DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS); 7 } 8 catch (ClassNotFoundException ex) { 9 throw new IllegalStateException( 10 "Unable create a default ApplicationContext, " 11 + "please specify an ApplicationContextClass", 12 ex); 13 } 14 } 15 return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass); 16 }
通过判断当前是否是web环境决定创建什么类,如果是web程序,那么创建org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext的实例,否则创建org.springframework.context.annotation.AnnotationConfigApplicationContext的实例,这些都是定义在SpringApplication类的静态属性中
第16行创建FailureAnalyzers的对象,FailureAnalyzers的构造函数里调用了loadFailureAnalyzers方法,还是老规矩,在classpath下的JAR文件中包含的/META/spring.factories文件中找到org.springframework.boot.diagnostics.FailureAnalyzer对应的属性,实例化并排序,赋值给FailureAnalyzers对象的analyzers属性,主要是用来处理启动时发生一些异常时的一些分析
第17行调用prepareContext方法,准备上下文
1 private void prepareContext(ConfigurableApplicationContext context, 2 ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, 3 ApplicationArguments applicationArguments, Banner printedBanner) { 4 context.setEnvironment(environment); 5 postProcessApplicationContext(context); 6 applyInitializers(context); 7 listeners.contextPrepared(context); 8 if (this.logStartupInfo) { 9 logStartupInfo(context.getParent() == null); 10 logStartupProfileInfo(context); 11 } 12 13 // Add boot specific singleton beans 14 context.getBeanFactory().registerSingleton("springApplicationArguments", 15 applicationArguments); 16 if (printedBanner != null) { 17 context.getBeanFactory().registerSingleton("springBootBanner", printedBanner); 18 } 19 20 // Load the sources 21 Set<Object> sources = getSources(); 22 Assert.notEmpty(sources, "Sources must not be empty"); 23 load(context, sources.toArray(new Object[sources.size()])); 24 listeners.contextLoaded(context); 25 }
先给上下文设置环境,然后调用postProcessApplicationContext方法设置上下文的beanNameGenerator和resourceLoader(如果SpringApplication有的话)
接下来调用applyInitializers方法,拿到之前实例化SpringApplication对象的时候设置的ApplicationContextInitializer,调用它们的initialize方法,对上下文做初始化
介绍几个ApplicationContextInitializer,其他的不过多介绍,有兴趣的可以自己DEBUG
DelegatingApplicationContextInitializer: 从环境中(配置的context.initializer.classes)取出所有的ApplicationContextInitializer并执行
ContextIdApplicationContextInitializer: 设置上下文的id(name + ":" + profiles + ":" + index),笔者调试的id为(application:dev:8080)
下面一行调用listeners的contextPrepared方法,跟之前调用listeners的starting方法一样,只是EventPublishingRunListener的contextPrepared方法是个空实现
然后打印启动日志
2017-09-02 22:08:07.196 [INFO ][main]:com.dylan.java.springboot.template.Application[logStarting:48] - Starting Application on Dylan-PC with PID 5764 (F:\Dylan\workspace\template\target\classes started by Dylan in F:\Dylan\workspace\template) 2017-09-02 22:08:12.388 [INFO ][main]:com.dylan.java.springboot.template.Application[logStartupProfileInfo:597] - The following profiles are active: dev
打印完日志往上下文的beanFactory中注册一个singleton的bean,bean的名字是springApplicationArguments,bean的实例是之前实例化的ApplicationArguments对象
如果之前获取的printedBanner不为空,那么往上下文的beanFactory中注册一个singleton的bean,bean的名字是springBootBanner,bean的实例就是这个printedBanner
prepareContext方法的倒数第2行调用load方法注册启动类的bean定义,也就是调用SpringApplication.run(Application.class, args);的类,SpringApplication的load方法内会创建BeanDefinitionLoader的对象,并调用它的load()方法
1 public int load() { 2 int count = 0; 3 for (Object source : this.sources) { 4 count += load(source); 5 } 6 return count; 7 }
对所有的source(这里只有一个: class com.dylan.java.springboot.template.Application)都执行一次load(Object source)方法,这个方法又会调用load(Class<?> source)方法
1 private int load(Class<?> source) { 2 if (isGroovyPresent()) { 3 // Any GroovyLoaders added in beans{} DSL can contribute beans here 4 if (GroovyBeanDefinitionSource.class.isAssignableFrom(source)) { 5 GroovyBeanDefinitionSource loader = BeanUtils.instantiateClass(source, 6 GroovyBeanDefinitionSource.class); 7 load(loader); 8 } 9 } 10 if (isComponent(source)) { 11 this.annotatedReader.register(source); 12 return 1; 13 } 14 return 0; 15 }
由于@SpringBootApplication注解继承了@SpringBootConfiguration注解,@SpringBootConfiguration注解继承了@Configuration注解,@Configuration注解又继承了@Component注解,所以上面代码的第10行返回true,于是执行第11行和第12行代码
1 class BeanDefinitionLoader { 2 private final Object[] sources; 3 4 private final AnnotatedBeanDefinitionReader annotatedReader; 5 6 private final XmlBeanDefinitionReader xmlReader; 7 8 private BeanDefinitionReader groovyReader; 9 10 ... 11 }
可以看出BeanDefinitionLoader中有多个加载BeanDefinition的Reader类,这里针对@SpringBootApplication注解使用了annotatedReader,调用register方法,因为启动类没有@Conditional注解,所以不能跳过注册的步骤
那么就老老实实的注册该BeanDefinition吧,没有设置bean是singleton还是prototype,那么默认使用singleton,而bean的名字,则默认是把类名的首字母变小写,也就是application,然后检查是否有@Lazy、@Primary、@DependsOn注解并设置AnnotatedBeanDefinition的属性,如果是AbstractBeanDefinition,还要检查是否有@Role、@Description注解并设置其属性,最后通过BeanDefinitionReaderUtils类的registerBeanDefinition方法注册BeanDefinition
最后调用listeners的contextLoaded方法,说明上下文已经加载,该方法先找到所有的ApplicationListener,遍历这些listener,如果该listener继承了ApplicationContextAware类,那么在这一步会调用它的setApplicationContext方法,设置context
遍历完ApplicationListener之后,创建ApplicationPreparedEvent事件对象,并广播出去,也就是调用所有ApplicationListener的onApplicationEvent方法
最后再回到SpringApplication的run方法,第19行调用refreshContext方法,该方法内容较多,我们放下一篇再分析