Start the principle -SpringApplication SpringBoot analysis

First posted a simple little demo SpringBoot

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

}

As above, this simple little program can be seen starting Springboot program mainly through SpringApplication.run (), then we have to analyze how Springboot by SpringApplication.run () method to start.

Constructor

/**
 * 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();
}

Next we look each method

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

Determining whether there is a static method by the classpath corresponding class specific return type

  • WebApplicationType.REACTIVE reactive Web application (whether or not there is a determination condition org.springframework.web.reactive.DispatcherHandler)
  • WebApplicationType.SERVLET the servlet Web application (determined whether a condition exists javax.servlet.Servlet, org.springframework.web.context.ConfigurableWebApplicationContext)
  • WebApplicationType.NONE simple java application

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

Main () method is implemented by SpringFactoriesLoader.loadFactoryNames, its source code can be traced to identify critical points in 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);
    }
}

As can be seen from the above method springboot primarily by "META-INF / spring.factories" configuration agreed default implementation. ApplicationContextInitializer ApplicationListener respectively and a corresponding configuration therein.

SpringApplication from the constructor can be seen mainly done mainly on the number of initialization and ApplicationContextInitializer ApplicationListener interface instantiated.

SpringApplication.run()

After analyzing the constructor we turn next to the most important SpringApplication.run () method.

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

Separate analysis of each specific method

getRunListeners ()

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

As can be seen from the main source it is also called in the constructor SpringApplication have analyzed getSpringFactoriesInstances () method to get from the META-INF / spring.factories in SpringApplicationRunListener interfaces and implementation class to instantiate. It can be seen through a configuration file specific to org.springframework.boot.context.event.EventPublishingRunListener . Analyze 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);
    }
}

From EventPublishingRunListener constructor can be seen by the main () application.getListeners acquired ApplicationListener type listner then added to hereinafter by this.initialMulticaster.addApplicationListener (listener) SimpleApplicationEventMulticaster in.

listeners.starting();

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

Then when starting EventPublishingRunListener method is called, you will find support ApplicationStartingEvent event listener calls its onApplicationEvent event method. Mainly for the listener:
getApplicationListeners break Screenshot

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

The main preparatory work for the environment; listeners.environmentPrepared (environment); main role is to release the code ApplicationEnvironmentPreparedEvent event, after performing onApplicationEvent method in which a more important listener ConfigFileApplicationListener loaded into the corresponding properties according to spring.profile.active or yml profile Environment.

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

The main attribute is read and spring.beaninfo.ignore environment parameter set to true.

banner print ()

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

The main chart is printed 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);
}

Examples of types depending on the respective application ConfigurableApplicationContext, this example returns 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);
}

Look under load (ApplicationContext context, Object [] sources) Method

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

Re-look back 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;
}

Here the main source of the class are provided determining whether annotated @Component, if it is injected into the beanFactory. Use this example the @SpringApplication notes, the final will be processed AnnotatedBeanDefinitionReader.

refreshContext

Refresh the context of work to do, too many codes, plans to open a separate article interpretation.

callRunners()

Call run CommandLineRunner interface class () method

summary

Throughout Springboot start source code can be seen more crucial still springboot will load the corresponding implementation class from spring.factories configuration, and can be seen from springboot official document if there is a demand we can also add the corresponding configuration spring.factories years, so as to achieve the respective purposes.

Released three original articles · won praise 1 · views 1416

Guess you like

Origin blog.csdn.net/b95152394/article/details/104673064