Iniciar el principio de análisis -SpringApplication SpringBoot

Por primera vez un poco simple demostración 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);
    }

}

Como el anterior, este pequeño y sencillo programa puede ser visto a partir del programa Springboot principalmente a través de SpringApplication.run (), entonces tenemos que analizar cómo método Springboot por SpringApplication.run () para iniciar.

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

A continuación analizamos cada método

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

La determinación de si hay un método estático por la ruta de clase correspondiente tipo de retorno específico clase

  • WebApplicationType.REACTIVE aplicación Web reactiva (si existe o no es una org.springframework.web.reactive.DispatcherHandler condición de determinación)
  • WebApplicationType.SERVLET la aplicación web servlet (determina si existe una condición javax.servlet.Servlet, org.springframework.web.context.ConfigurableWebApplicationContext)
  • WebApplicationType.NONE sencilla aplicación 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;
}

método main () está implementado por SpringFactoriesLoader.loadFactoryNames, su código fuente puede ser rastreado para identificar puntos críticos en 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);
    }
}

Como puede verse en la springboot método anterior principalmente por "META-INF / spring.factories" configuración por defecto de acuerdo aplicación. ApplicationContextInitializer ApplicationListener respectivamente y una correspondiente configuración en el mismo.

SpringApplication desde el constructor puede ser visto principalmente realiza principalmente en el número de inicialización y la interfaz ApplicationContextInitializer ApplicationListener instancia.

SpringApplication.run ()

Después de analizar el constructor nos dirigimos al lado del método más importante 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;
}

El análisis separado de cada método específico

getRunListeners ()

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

Como puede verse en la fuente principal también se le llama en el SpringApplication constructor han analizado método getSpringFactoriesInstances () para llegar desde el META-INF / spring.factories en SpringApplicationRunListener interfaces y clase de implementación para crear una instancia. Se puede observar a través de un determinado archivo de configuración para org.springframework.boot.context.event.EventPublishingRunListener . analizar 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);
    }
}

De EventPublishingRunListener constructor puede ser visto por los principales application.getListeners () adquirido ApplicationListener tipo listner después se añadió a continuación a this.initialMulticaster.addApplicationListener (oyente) SimpleApplicationEventMulticaster en.

listeners.starting ();

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

Luego, cuando se llama método de arranque EventPublishingRunListener, se encuentra detector de eventos de apoyo ApplicationStartingEvent llama a su método de evento onApplicationEvent. Principalmente para el oyente:
getApplicationListeners rompen Captura de pantalla

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

El principal trabajo preparatorio para el medio ambiente; listeners.environmentPrepared (medio ambiente); papel principal es para liberar el evento código ApplicationEnvironmentPreparedEvent, después de realizar método onApplicationEvent en el que un oyente más importante ConfigFileApplicationListener carga en las propiedades correspondientes de acuerdo con spring.profile.active o perfil yml medio ambiente.

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

El atributo principal es leído y parámetro de entorno spring.beaninfo.ignore establece en true.

impresión de banner ()

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

El gráfico principal se imprime bandera.

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

Ejemplos de tipos dependiendo de la aplicación respectiva ConfigurableApplicationContext, este ejemplo se devuelve 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 bajo carga (contexto ApplicationContext, Object [] fuentes) Método

carga (contexto ApplicationContext, Object [] fuentes)

/**
 * 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 posterior re-mirada ()

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

Aquí la fuente principal de la clase se proporcionan determinar si anotado @Component, si se inyecta en el BeanFactory. Utilizar este ejemplo las notas @SpringApplication, la final serán procesados ​​AnnotatedBeanDefinitionReader.

refreshContext

Refrescar el contexto de trabajo por hacer, demasiados códigos, planes de abrir una interpretación artículo separado.

callRunners ()

Llamada correr clase de interfaz CommandLineRunner método ()

resumen

A lo largo de código fuente inicio Springboot puede verse más importante springboot todavía se carga la clase de implementación de la configuración correspondiente spring.factories, y se puede ver a partir de documentos oficiales springboot si existe una demanda también podemos añadir las correspondientes spring.factories configuración años, a fin de lograr los propósitos respectivos.

Publicado tres artículos originales · ganado elogios 1 · vistas 1416

Supongo que te gusta

Origin blog.csdn.net/b95152394/article/details/104673064
Recomendado
Clasificación