抱歉,你查看的文章不存在

SpringBoot的启动引导原理&IOC容器的启动原理(1)

源码解析系列笔记,建议配合哔哩哔哩群直播录像食用更佳(狗头)

本篇笔记对应录像:SpringBoot-启动引导原理&IOC启动原理(1)(右键新标签页打开 ヽ( ̄▽ ̄)ノ )

1. SpringBoot的启动引导

//@SpringBootApplication是启动引导的核心
@SpringBootApplication
public class DemoApplication {
    
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
    
}

@SpringBootApplication注解的声明:

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication

1.1 @SpringBootConfiguration

@Configuration
public @interface SpringBootConfiguration

说明这是一个配置类

1.2 @EnableAutoConfiguration *

@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration

1.2.1 @AutoConfigurationPackage

@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage

导入的内部类:

	static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {

		//向IOC容器中导入组件,传入的包名恰好是当前主启动类的所在包名
		@Override
		public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
			register(registry, new PackageImport(metadata).getPackageName());
		}

		@Override
		public Set<Object> determineImports(AnnotationMetadata metadata) {
			return Collections.singleton(new PackageImport(metadata));
		}

	}

得到结论:解释了为什么SpringBoot应用的主启动类必须放在所有组件所在包的最外层!

1.2.2 AutoConfigurationImportSelector

它实现了ImportSelector接口:

	public String[] selectImports(AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return NO_IMPORTS;
		}
		AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
				.loadMetadata(this.beanClassLoader);
		AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
				annotationMetadata);
		return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
	}

核心方法:getAutoConfigurationEntry:

	protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
			AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return EMPTY_ENTRY;
		}
		AnnotationAttributes attributes = getAttributes(annotationMetadata);
		//尤为关键
		List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
		configurations = removeDuplicates(configurations);
		Set<String> exclusions = getExclusions(annotationMetadata, attributes);
		checkExcludedClasses(configurations, exclusions);
		configurations.removeAll(exclusions);
		configurations = filter(configurations, autoConfigurationMetadata);
		fireAutoConfigurationImportEvents(configurations, exclusions);
		return new AutoConfigurationEntry(configurations, exclusions);
	}

1.2.3 getCandidateConfigurations

	protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
		List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
				getBeanClassLoader());
		Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
				+ "are using a custom packaging, make sure that file is correct.");
		return configurations;
	}

1.2.4 loadFactoryNames ***

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

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

		try {
          //去META-INF/spring.factories里面读取key为org.springframework.boot.autoconfigure.EnableAutoConfiguration的值
          //这个值实际上是一堆自动配置类的全限定类名
			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 factoryClassName = ((String) entry.getKey()).trim();
					for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
						result.add(factoryClassName, factoryName.trim());
					}
				}
			}
			cache.put(classLoader, result);
			return result;
		}
		catch (IOException ex) {
			throw new IllegalArgumentException("Unable to load factories from location [" +
					FACTORIES_RESOURCE_LOCATION + "]", ex);
		}
	}

最终返回的String[]包含一组自动配置类的全限定类名,Spring会利用反射将其创建出来,并对应的执行自动配置。

这也就解释了为什么即便没有任何配置文件,SpringBoot的Web应用都能正常运行。

1.2.5 配置类的过滤规则

以WebMVCAutoConfiguration为例:

@Configuration
@ConditionalOnWebApplication(type = Type.SERVLET) //当前环境必须为Web环境,且必须是基于Servlet的
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class }) //当前的classpath下必须有Servlet类,DispatcherServlet类等
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class) //当前IOC容器中不能有WebMvcConfigurationSupport类型的Bean
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
		ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration

1.3 @ComponentScan

组件包扫描

2. SpringBoot-IOC启动原理

SpringApplication.run(DemoApplication.class, args);

main方法中只有这一句话,就可以启动SpringBoot的IOC容器,那一定是这一句代码中暗藏玄机。

run方法:

	public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
		return run(new Class<?>[] { primarySource }, args);
	}

	public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
		return new SpringApplication(primarySources).run(args);
	}

很明显发现SpringBoot的应用,启动分为两个动作:

  • new SpringApplication
  • run

run方法返回的是ApplicationContext的子接口:ConfigurableApplicationContext。

3. new SpringApplication(primarySources):创建SpringApplication

	public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
        //resourceLoader为null
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
        //将传入的SpringBootApplication启动类放入primarySources中,这样应用就知道主启动类在哪里,叫什么了
        this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
        //3.1 判断当前应用环境
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
        //3.2 设置初始化器,从classpath的所有jar包读取所有类型为ApplicationContextInitializer的已配置组件的全限定类名
        setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
        //3.3 设置监听器
        setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
        //3.4 选定主配置类
        this.mainApplicationClass = deduceMainApplicationClass();
	}

3.1 判断当前应用环境

    //Servlet的全限定类名,它的用途是判断应用的classpath里是否有该类
    private static final String[] SERVLET_INDICATOR_CLASSES = { "javax.servlet.Servlet",
            "org.springframework.web.context.ConfigurableWebApplicationContext" };

    private static final String WEBMVC_INDICATOR_CLASS = "org.springframework." + "web.servlet.DispatcherServlet";

    private static final String WEBFLUX_INDICATOR_CLASS = "org." + "springframework.web.reactive.DispatcherHandler";

    private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer";

    private static final String SERVLET_APPLICATION_CONTEXT_CLASS = "org.springframework.web.context.WebApplicationContext";

    private static final String REACTIVE_APPLICATION_CONTEXT_CLASS = "org.springframework.boot.web.reactive.context.ReactiveWebApplicationContext";
    
    static WebApplicationType deduceFromClasspath() {
        //先判断是否是Reactive环境,发现有WebFlux的类但没有WebMvc的类,则判定为Reactive环境(全NIO)
        if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
                && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
            return WebApplicationType.REACTIVE;
        }
        //检查是否有跟Servlet相关的类,如果没有,则判定为非Web环境
        for (String className : SERVLET_INDICATOR_CLASSES) {
            if (!ClassUtils.isPresent(className, null)) {
                return WebApplicationType.NONE;
            }
        }
        //检查到了,则为Servlet(WebMvc)环境
        return WebApplicationType.SERVLET;
    }

3.2 设置初始化器

setInitializers方法会将一组类型为ApplicationContextInitializer的初始化器放入SpringApplication中。

而这组ApplicationContextInitializer,是在构造方法中,通过getSpringFactoriesInstances得到的。

    //3.2.0 ApplicationContextInitializer 
    public void setInitializers(Collection<? extends ApplicationContextInitializer<?>> initializers) {
        this.initializers = new ArrayList<>();
        this.initializers.addAll(initializers);
    }

    private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
        return getSpringFactoriesInstances(type, new Class<?>[] {});
    }

    private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
        //3.2.2 类加载器的获取
        ClassLoader classLoader = getClassLoader();
        // Use names and ensure unique to protect against duplicates (使用名称并确保唯一,以防止重复)
        //3.2.1 SpringFactoriesLoader.loadFactoryNames:加载指定类型的所有已配置组件的全限定类名
        Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
        //3.2.3 createSpringFactoriesInstances:创建这些组件的实例
        List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
        AnnotationAwareOrderComparator.sort(instances);
        return instances;
    }

3.2.0 ApplicationContextInitializer

/**
 * Callback interface for initializing a Spring {@link ConfigurableApplicationContext}
 * prior to being {@linkplain ConfigurableApplicationContext#refresh() refreshed}.
 *
 * <p>Typically used within web applications that require some programmatic initialization
 * of the application context. For example, registering property sources or activating
 * profiles against the {@linkplain ConfigurableApplicationContext#getEnvironment()
 * context's environment}. See {@code ContextLoader} and {@code FrameworkServlet} support
 * for declaring a "contextInitializerClasses" context-param and init-param, respectively.
 *
 * <p>{@code ApplicationContextInitializer} processors are encouraged to detect
 * whether Spring's {@link org.springframework.core.Ordered Ordered} interface has been
 * implemented or if the @{@link org.springframework.core.annotation.Order Order}
 * annotation is present and to sort instances accordingly if so prior to invocation.
 *
 * 回调接口,用于在刷新之前初始化Spring ConfigurableApplicationContext。
 * 通常在需要对应用程序上下文进行某些编程初始化的Web应用程序中使用。
 * 例如,根据上下文环境注册属性源或激活配置文件。
 * 请参阅ContextLoader和FrameworkServlet支持,分别声明“contextInitializerClasses”context-param和init-param。
 * 鼓励ApplicationContextInitializer处理器检测是否已实现Spring的Ordered接口,或者是否存在@Order注释,并在调用之前相应地对实例进行排序。
 */
public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext>

3.2.2 getClassLoader默认获取的机制

	public ClassLoader getClassLoader() {
		if (this.resourceLoader != null) {
			return this.resourceLoader.getClassLoader();
		}
		return ClassUtils.getDefaultClassLoader();
	}

因为resourceLoader默认在创建SpringApplication时传入的是null,默认走下面的return:

	public static ClassLoader getDefaultClassLoader() {
		ClassLoader cl = null;
		try {
			//取当前线程
			cl = Thread.currentThread().getContextClassLoader();
		}
		catch (Throwable ex) {
			// Cannot access thread context ClassLoader - falling back...
		}
		if (cl == null) {
			// No thread context class loader -> use class loader of this class.
			cl = ClassUtils.class.getClassLoader();
			if (cl == null) {
				// getClassLoader() returning null indicates the bootstrap ClassLoader
				try {
					cl = ClassLoader.getSystemClassLoader();
				}
				catch (Throwable ex) {
					// Cannot access system ClassLoader - oh well, maybe the caller can live with null...
				}
			}
		}
		return cl;
	}

3.2.3 反射创建

    private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes,
            ClassLoader classLoader, Object[] args, Set<String> names) {
        List<T> instances = new ArrayList<>(names.size());
        for (String name : names) {
            try {
                //反射创建这些对象
                Class<?> instanceClass = ClassUtils.forName(name, classLoader);
                Assert.isAssignable(type, instanceClass);
                Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
                T instance = (T) BeanUtils.instantiateClass(constructor, args);
                instances.add(instance);
            }
            catch (Throwable ex) {
                throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);
            }
        }
        return instances;
    }

3.3 设置监听器

//加载所有类型为ApplicationListener的已配置的组件的全限定类名
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

3.4 选定主配置类

    private Class<?> deduceMainApplicationClass() {
        try {
            StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
            for (StackTraceElement stackTraceElement : stackTrace) {
                //谁有main方法,谁是主配置类
                if ("main".equals(stackTraceElement.getMethodName())) {
                    return Class.forName(stackTraceElement.getClassName());
                }
            }
        }
        catch (ClassNotFoundException ex) {
            // Swallow and continue
        }
        return null;
    }

3.5 与SpringBoot1.x的区别

    private final Set<Object> sources = new LinkedHashSet<Object>();

    private void initialize(Object[] sources) {
        //sources为null时没有终止应用继续启动
        //sources为SpringBoot1.x中使用的成员,SpringBoot2.x保留了它,但启动过程中不再使用
        if (sources != null && sources.length > 0) {
            this.sources.addAll(Arrays.asList(sources));
        }
        //deduceWebEnvironment方法在SpringApplication中
        //且SpringBoot1.x使用Spring4.x版本,没有WebFlux模块,故这里面只判断是否为WebMvc环境
        this.webEnvironment = deduceWebEnvironment();
        setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
        setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
        this.mainApplicationClass = deduceMainApplicationClass();
    }

4. run

连载中。。。

(adsbygoogle = window.adsbygoogle || []).push({});

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

编辑于

LinkedBear的个人空间

0 篇文章14 人订阅

扫码关注云+社区

领取腾讯云代金券