SpringBoot分析之启动原理-SpringApplication

首先贴出一个简单的SpringBoot小demo

package com.baojiong.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

}

正如上面这个简单的小程序可以看出Springboot程序的启动主要是通过SpringApplication.run(),那么我们就来分析一下Springboot是如何通过SpringApplication.run()方法启动的。

构造函数

/**
 * Create a new {@link SpringApplication} instance. The application context will load
 * beans from the specified primary sources (see {@link SpringApplication class-level}
 * documentation for details. The instance can be customized before calling
 * {@link #run(String...)}.
 * @param resourceLoader the resource loader to use
 * @param primarySources the primary bean sources
 * @see #run(Class, String[])
 * @see #setSources(Set)
 */
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    this.resourceLoader = resourceLoader;
    //断言启动主类,通过方法中可变参数可以看出主类可以为多个
    Assert.notNull(primarySources, "PrimarySources must not be null");
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    //判断是否为web环境
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    //加载ApplicationContextInitializer接口类
    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    //加载ApplicationListener类
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    //通过是否包含main方法查找启动类
    this.mainApplicationClass = deduceMainApplicationClass();
}

接下来我们细看各个方法

deduceFromClasspath()

static WebApplicationType deduceFromClasspath() {
    if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
            && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
        return WebApplicationType.REACTIVE;
    }
    for (String className : SERVLET_INDICATOR_CLASSES) {
        if (!ClassUtils.isPresent(className, null)) {
            return WebApplicationType.NONE;
        }
    }
    return WebApplicationType.SERVLET;
}

静态方法通过判断classpath中是否存在相应的类返回具体类型

  • WebApplicationType.REACTIVE reactive web应用(判断条件为是否存在org.springframework.web.reactive.DispatcherHandler)
  • WebApplicationType.SERVLET servlet web应用(判断条件为是否存在javax.servlet.Servlet,org.springframework.web.context.ConfigurableWebApplicationContext)
  • WebApplicationType.NONE 单纯的java应用

getSpringFactoriesInstances()

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
    ClassLoader classLoader = getClassLoader();
    // Use names and ensure unique to protect against duplicates
    Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
    //通过反射的方式进行实例化
    List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
    AnnotationAwareOrderComparator.sort(instances);
    return instances;
}

主要是通过SpringFactoriesLoader.loadFactoryNames()方法实现,追溯其源码可发现关键点在SpringFactoriesLoader.loadSpringFactories()

public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
    String factoryTypeName = factoryType.getName();
    return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}

SpringFactoriesLoader.loadSpringFactories()

private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
    MultiValueMap<String, String> result = cache.get(classLoader);
    if (result != null) {
        return result;
    }

    try {
        //搜索classpath下"META-INF/spring.factories"文件
        Enumeration<URL> urls = (classLoader != null ?
                classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
                ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
        result = new LinkedMultiValueMap<>();
        //文件中配置的类作分类添加至集合中
        while (urls.hasMoreElements()) {
            URL url = urls.nextElement();
            UrlResource resource = new UrlResource(url);
            Properties properties = PropertiesLoaderUtils.loadProperties(resource);
            for (Map.Entry<?, ?> entry : properties.entrySet()) {
                String factoryTypeName = ((String) entry.getKey()).trim();
                for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
                    result.add(factoryTypeName, factoryImplementationName.trim());
                }
            }
        }
        cache.put(classLoader, result);
        return result;
    }
    catch (IOException ex) {
        throw new IllegalArgumentException("Unable to load factories from location [" +
                FACTORIES_RESOURCE_LOCATION + "]", ex);
    }
}

从上述方法可以看出springboot主要通过"META-INF/spring.factories"配置约定默认的实现。ApplicationContextInitializer和ApplicationListener也分别在其中有相应的配置。

从SpringApplication的构造函数可以看出主要是完成一些初始化操作主要是对ApplicationContextInitializer和ApplicationListener接口实现进行实例化。

SpringApplication.run()

分析完构造函数我们接下来看看最重要的SpringApplication.run()方法。

public ConfigurableApplicationContext run(String... args) {
    //启动计时
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    ConfigurableApplicationContext context = null;
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    //配置headless参数
    configureHeadlessProperty();
    //获取SpringApplicationRunListener接口并实例化
    SpringApplicationRunListeners listeners = getRunListeners(args);
    调用starting()
    listeners.starting();
    try {
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
        configureIgnoreBeanInfo(environment);
        Banner printedBanner = printBanner(environment);
        context = createApplicationContext();
        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);
        }
        // 发布ApplicationStartedEvent事件
        listeners.started(context);
        callRunners(context, applicationArguments);
    }
    catch (Throwable ex) {
        handleRunFailure(context, ex, exceptionReporters, listeners);
        throw new IllegalStateException(ex);
    }

    try {
    	// 发布ApplicationReadyEvent事件
        listeners.running(context);
    }
    catch (Throwable ex) {
        handleRunFailure(context, ex, exceptionReporters, null);
        throw new IllegalStateException(ex);
    }
    return context;
}

分开分析各个具体的方法

getRunListeners()

private SpringApplicationRunListeners getRunListeners(String[] args) {
    Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
    return new SpringApplicationRunListeners(logger,
            getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}

从源码可以看出主要也是调用了在SpringApplication构造函数中已分析过的getSpringFactoriesInstances()方法从META-INF/spring.factories中获取SpringApplicationRunListener接口实现类并实例化。通过配置文件可以看到具体为org.springframework.boot.context.event.EventPublishingRunListener。分析一下EventPublishingRunListener

public EventPublishingRunListener(SpringApplication application, String[] args) {
    this.application = application;
    this.args = args;
    this.initialMulticaster = new SimpleApplicationEventMulticaster();
    for (ApplicationListener<?> listener : application.getListeners()) {
        this.initialMulticaster.addApplicationListener(listener);
    }
}

EventPublishingRunListener构造函数可以看到主要通过application.getListeners() 获取ApplicationListener类型的listner然后通过下文中this.initialMulticaster.addApplicationListener(listener)添加至SimpleApplicationEventMulticaster中。

listeners.starting();

@Override
public void starting() {
    this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
}

然后当EventPublishingRunListener的starting方法被调用时,会找到支持ApplicationStartingEvent事件的listener调用其onApplicationEvent事件方法。主要为以下listener:
SpringBoot分析之启动原理-SpringApplication

prepareEnvironment()

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
        ApplicationArguments applicationArguments) {
    // Create and configure the environment
    // 根据webApplicationType获取指定类型ConfigurableEnvironment(本次例子获取的为StandardServletEnvironment)
    ConfigurableEnvironment environment = getOrCreateEnvironment();
    // 解析args参数和读取spring.profile.active
    configureEnvironment(environment, applicationArguments.getSourceArgs());
    ConfigurationPropertySources.attach(environment);
    // 执行ApplicationEnvironmentPreparedEvent事件
    listeners.environmentPrepared(environment);
    bindToSpringApplication(environment);
    if (!this.isCustomEnvironment) {
        environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
                deduceEnvironmentClass());
    }
    ConfigurationPropertySources.attach(environment);
    return environment;
}

主要为环境准备工作;listeners.environmentPrepared(environment);代码主要作用为发布ApplicationEnvironmentPreparedEvent事件,其中一个比较重要的监听器ConfigFileApplicationListener执行onApplicationEvent方法后会根据spring.profile.active将相应的properties或者yml配置文件加载到环境中。

configureIgnoreBeanInfo()

private void configureIgnoreBeanInfo(ConfigurableEnvironment environment) {      
    if(System.getProperty(CachedIntrospectionResults.IGNORE_BEANINFO_PROPERTY_NAME) == null) {            
        Boolean ignore = environment.getProperty("spring.beaninfo.ignore", Boolean.class, Boolean.TRUE);            
        System.setProperty(CachedIntrospectionResults.IGNORE_BEANINFO_PROPERTY_NAME, ignore.toString());      
    }
}

主要为读取spring.beaninfo.ignore属性并将环境参数设置为true。

printBanner()

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);
}

主要为打印banner图。

createApplicationContext()

protected ConfigurableApplicationContext createApplicationContext() {
    Class<?> contextClass = this.applicationContextClass;
    if (contextClass == null) {
        try {
            switch (this.webApplicationType) {
            case SERVLET:
                contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
                break;
            case REACTIVE:
                contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
                break;
            default:
                contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
            }
        }
        catch (ClassNotFoundException ex) {
            throw new IllegalStateException(
                    "Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex);
        }
    }
    return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}

根据应用类型实例相应的ConfigurableApplicationContext,本次例子返回的是org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext

prepareContext()

private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
        SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
    context.setEnvironment(environment);
    postProcessApplicationContext(context);
    // ApplicationContextInitializer接口实现类执行initialize()方法
    applyInitializers(context);
    // 发布ApplicationContextInitializedEvent事件
    listeners.contextPrepared(context);
    if (this.logStartupInfo) {
        logStartupInfo(context.getParent() == null);
        logStartupProfileInfo(context);
    }
    // Add boot specific singleton beans
    ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
    beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
    if (printedBanner != null) {
        beanFactory.registerSingleton("springBootBanner", printedBanner);
    }
    if (beanFactory instanceof DefaultListableBeanFactory) {
        ((DefaultListableBeanFactory) beanFactory)
                .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
    }
    if (this.lazyInitialization) {
        context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
    }
    // Load the sources
    Set<Object> sources = getAllSources();
    Assert.notEmpty(sources, "Sources must not be empty");
    // 加载beans
    load(context, sources.toArray(new Object[0]));
    // 发布ApplicationPreparedEvent事件
    listeners.contextLoaded(context);
}

细看下load(ApplicationContext context, Object[] sources)方法

load(ApplicationContext context, Object[] sources)

/**
 * Load beans into the application context.
 * @param context the context to load beans into
 * @param sources the sources to load
 */
protected void load(ApplicationContext context, Object[] sources) {
	if (logger.isDebugEnabled()) {
		logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
	}
	BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
	if (this.beanNameGenerator != null) {
		loader.setBeanNameGenerator(this.beanNameGenerator);
	}
	if (this.resourceLoader != null) {
		loader.setResourceLoader(this.resourceLoader);
	}
	if (this.environment != null) {
		loader.setEnvironment(this.environment);
	}
	loader.load();
}
createBeanDefinitionLoader()
/**
 * Factory method used to create the {@link BeanDefinitionLoader}.
 * @param registry the bean definition registry
 * @param sources the sources to load
 * @return the {@link BeanDefinitionLoader} that will be used to load beans
 */
protected BeanDefinitionLoader createBeanDefinitionLoader(BeanDefinitionRegistry registry, Object[] sources) {
	return new BeanDefinitionLoader(registry, sources);
}

// BeanDefinitionLoader构造函数
/**
 * Create a new {@link BeanDefinitionLoader} that will load beans into the specified
 * {@link BeanDefinitionRegistry}.
 * @param registry the bean definition registry that will contain the loaded beans
 * @param sources the bean sources
 */
BeanDefinitionLoader(BeanDefinitionRegistry registry, Object... sources) {
	Assert.notNull(registry, "Registry must not be null");
	Assert.notEmpty(sources, "Sources must not be empty");
	this.sources = sources;
    // 注解解析类
	this.annotatedReader = new AnnotatedBeanDefinitionReader(registry);
    // xml解析类
	this.xmlReader = new XmlBeanDefinitionReader(registry);
	if (isGroovyPresent()) {
		this.groovyReader = new GroovyBeanDefinitionReader(registry);
	}
    // 屏蔽指定类
	this.scanner = new ClassPathBeanDefinitionScanner(registry);
	this.scanner.addExcludeFilter(new ClassExcludeFilter(sources));
}

重新看回loader.load()

/**
 * Load the sources into the reader.
 * @return the number of loaded beans
 */
int load() {
	int count = 0;
	for (Object source : this.sources) {
		count += load(source);
	}
	return count;
}

private int load(Object source) {
	Assert.notNull(source, "Source must not be null");
	if (source instanceof Class<?>) {
		return load((Class<?>) source);
	}
	if (source instanceof Resource) {
		return load((Resource) source);
	}
	if (source instanceof Package) {
		return load((Package) source);
	}
	if (source instanceof CharSequence) {
		return load((CharSequence) source);
	}
	throw new IllegalArgumentException("Invalid source type " + source.getClass());
}

private int load(Class<?> source) {
	if (isGroovyPresent() && GroovyBeanDefinitionSource.class.isAssignableFrom(source)) {
		// Any GroovyLoaders added in beans{} DSL can contribute beans here
		GroovyBeanDefinitionSource loader = BeanUtils.instantiateClass(source, GroovyBeanDefinitionSource.class);
		load(loader);
	}
	if (isComponent(source)) {
		this.annotatedReader.register(source);
		return 1;
	}
	return 0;
}

此处主要对设置的source类判断是否被@Component注解,如果是就注入到beanFactory中。本次例子中使用了@SpringApplication注解,所以最终会被AnnotatedBeanDefinitionReader处理。

refreshContext

做上下文刷新工作,代码过多,计划单独开出一篇文章解读。

callRunners()

调用CommandLineRunner接口实现类的run()方法

小结

纵观整个Springboot启动源码可以看出比较关键的还是springboot会从spring.factories配置中加载相应实现类,同时从springboot官方文档中可以看出如果我们有需求也可以在spring.factories里增加相应配置,从而达到相应的目的。

上一篇:IDEA cannot download sources


下一篇:安装 .NET Framework 3.5 Error 0x800f0950 in Windows 10