现在做什么?(思路:按着控制台打印的日志,找一下springboot启动过程中都做了哪些工作)
回:下午心血来潮,想看看springboot启动流程是什么?就在resources目录下新建了logback.xml,日志级别设置成DEBUG模式,启动项目,控制台打印出日志如下:
为了找到标红的那行日志,一步一步找,不太好找,先声明一下我的springboot版本:1.5.7.RELEASE,关于@SpringBootApplication注解,启动过程中都做了哪些工作,如何进行自动配置的?先暂时不讨论
1、首先是启动类
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
2、执行run方法
public static ConfigurableApplicationContext run(Object source, String... args) {
return run(new Object[] { source }, args);
}
3、接着执行下面这个方法
public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
return new SpringApplication(sources).run(args);
}
4、创建SpringApplication对象,执行初始化方法
public SpringApplication(Object... sources) {
initialize(sources);
}
5、初始化方法,
@SuppressWarnings({ "unchecked", "rawtypes" })
private void initialize(Object[] sources) {
if (sources != null && sources.length > 0) {
this.sources.addAll(Arrays.asList(sources));
}
//判断是否为web环境
this.webEnvironment = deduceWebEnvironment();
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
6、步骤4-5是创建对象做的初始化工作,接着看步骤3的run方法,注释里说的很清楚-运行一个spring应用,创建并刷新ApplicationContext
/**
* 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) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
FailureAnalyzers analyzers = null;
//设置Headless模式,Headless模式是系统的一种配置模式。在系统可能缺少显示设备、键盘或鼠标这些外设的情况下可以使用该模式。
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
analyzers = new FailureAnalyzers(context);
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
listeners.finished(context, null);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
return context;
}
catch (Throwable ex) {
handleRunFailure(context, listeners, analyzers, ex);
throw new IllegalStateException(ex);
}
}
这代码里面看这一行,Banner printedBanner = printBanner(environment);
private Banner printBanner(ConfigurableEnvironment environment) {
if (this.bannerMode == Banner.Mode.OFF) {
return null;
}
ResourceLoader resourceLoader = this.resourceLoader != null ? this.resourceLoader
: new DefaultResourceLoader(getClassLoader());
SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(
resourceLoader, this.banner);
if (this.bannerMode == Mode.LOG) {
return bannerPrinter.print(environment, this.mainApplicationClass, logger);
}
return bannerPrinter.print(environment, this.mainApplicationClass, System.out);
}
再看第9行,
if (this.bannerMode == Mode.LOG) {
return bannerPrinter.print(environment, this.mainApplicationClass, logger);
}
进入bannerPrinter.print方法,
public Banner print(Environment environment, Class<?> sourceClass, Log logger) {
Banner banner = getBanner(environment, this.fallbackBanner);
try {
logger.info(createStringFromBanner(banner, environment, sourceClass));
}
catch (UnsupportedEncodingException ex) {
logger.warn("Failed to create String for banner", ex);
}
return new PrintedBanner(banner, sourceClass);
}
进入第2行,getBanner方法
private Banner getBanner(Environment environment, Banner definedBanner) {
Banners banners = new Banners();
banners.addIfNotNull(getImageBanner(environment));
banners.addIfNotNull(getTextBanner(environment));
if (banners.hasAtLeastOneBanner()) {
return banners;
}
if (this.fallbackBanner != null) {
return this.fallbackBanner;
}
return DEFAULT_BANNER;
}
接着进入第3行,getImageBanner方法,此时BANNER_IMAGE_LOCATION_PROPERTY="banner.image.location"
private Banner getImageBanner(Environment environment) {
String location = environment.getProperty(BANNER_IMAGE_LOCATION_PROPERTY);
if (StringUtils.hasLength(location)) {
Resource resource = this.resourceLoader.getResource(location);
return (resource.exists() ? new ImageBanner(resource) : null);
}
for (String ext : IMAGE_EXTENSION) {
Resource resource = this.resourceLoader.getResource("banner." + ext);
if (resource.exists()) {
return new ImageBanner(resource);
}
}
return null;
}
进入第2行,environment.getProperty
@Override
public String getProperty(String key) {
return getProperty(key, String.class, true);
}
再第3行跟进,
找到这行代码挺费劲的,同理,也找到了这行,static final String BANNER_LOCATION_PROPERTY = "banner.location";
7、继续看步骤6里面的这行代码
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
跟进
private ConfigurableEnvironment prepareEnvironment(
SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// Create and configure the environment
ConfigurableEnvironment environment = getOrCreateEnvironment();
configureEnvironment(environment, applicationArguments.getSourceArgs());
listeners.environmentPrepared(environment);
if (!this.webEnvironment) {
environment = new EnvironmentConverter(getClassLoader())
.convertToStandardEnvironmentIfNecessary(environment);
}
return environment;
}
看这一行,ConfigurableEnvironment environment = getOrCreateEnvironment();
再跟进
private ConfigurableEnvironment getOrCreateEnvironment() {
if (this.environment != null) {
return this.environment;
}
if (this.webEnvironment) {
return new StandardServletEnvironment();
}
return new StandardEnvironment();
}
看第二个if判断语句,是否满足web环境,如果是,则创建StandardServletEnvironment对象
再进入StandardServletEnvironment类,看customizePropertySources方法
@Override
protected void customizePropertySources(MutablePropertySources propertySources) {
propertySources.addLast(new StubPropertySource(SERVLET_CONFIG_PROPERTY_SOURCE_NAME));
propertySources.addLast(new StubPropertySource(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME));
if (JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()) {
propertySources.addLast(new JndiPropertySource(JNDI_PROPERTY_SOURCE_NAME));
}
super.customizePropertySources(propertySources);
}
这个方法,什么时候执行的?是在创建对象的时候就去执行了,因为在父类AbstractEnvironment,是这样做的
public AbstractEnvironment() {
customizePropertySources(this.propertySources);
if (logger.isDebugEnabled()) {
logger.debug("Initialized " + getClass().getSimpleName() + " with PropertySources " + this.propertySources);
}
}
再看StandardServletEnvironment类的customizePropertySources方法中的第一、二行,SERVLET_CONFIG_PROPERTY_SOURCE_NAME="servletConfigInitParams"
SERVLET_CONTEXT_PROPERTY_SOURCE_NAME="servletContextInitParams"
propertySources.addLast(new StubPropertySource(SERVLET_CONFIG_PROPERTY_SOURCE_NAME));
propertySources.addLast(new StubPropertySource(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME));
跟进,addLast方法
public void addLast(PropertySource<?> propertySource) {
if (logger.isDebugEnabled()) {
logger.debug("Adding PropertySource '" + propertySource.getName() + "' with lowest search precedence");
}
removeIfPresent(propertySource);
this.propertySourceList.add(propertySource);
}
看这第二行代码,就知道本文章头部控制台打印出的日志是这样打出来的,
DEBUG- o.s.web.context.support.StandardServletEnvironment - Adding PropertySource 'servletConfigInitParams' with lowest search precedence
DEBUG- o.s.web.context.support.StandardServletEnvironment - Adding PropertySource 'servletContextInitParams' with lowest search precedence
因为StandardServletEnvironment类,方法末尾调用了父类的customizePropertySources方法
super.customizePropertySources(propertySources);
故,控制台接着打印
DEBUG- o.s.web.context.support.StandardServletEnvironment - Adding PropertySource 'systemProperties' with lowest search precedence
DEBUG- o.s.web.context.support.StandardServletEnvironment - Adding PropertySource 'systemEnvironment' with lowest search precedence