Spring 4.x IOC介绍(五.Spring boot 启动流程以及 IoC 构建)

18 篇文章 0 订阅

基于Spring boot - 2.0.1.RELEAS

之前的文章介绍了:BeanFactory 、ApplicationContext、WebApplicationContext
也写了各个部分的实现,总觉得缺点什么,最近整理自己博客的时候,终于发现了。少SpringBoot的啊。
Spring 4.x IOC介绍(一.BeanFactory 、ApplicationContext、WebApplicationContext之BeanFactory)

Spring 4.x IOC介绍(二.BeanFactory 、ApplicationContext、WebApplicationContext之ApplicationContext)

Spring 4.x IOC介绍(三.BeanFactory 、ApplicationContext、WebApplicationContext之WebApplicationContext)

Spring 4.x IOC介绍(四.BeanFactory 、ApplicationContext、WebApplicationContext——总结)

但是现在使用Spring Boot居多,在启动SpringBoot的时候,是如何创建Bean的?这篇文章说一下SpringBoot启动以及创建Bean。

Spring boot 启动代码

@SpringBootApplication
public class StudyApplication {

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

}

两个重点

  • @SpringBootApplication 注解
  • SpringApplication.run() 方法。

一个一个看

@SpringBootApplication 源码

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
		@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {

	/**
	 * Exclude specific auto-configuration classes such that they will never be applied.
	 * @return the classes to exclude
	 */
	@AliasFor(annotation = EnableAutoConfiguration.class)
	Class<?>[] exclude() default {};

	/**
	 * Exclude specific auto-configuration class names such that they will never be
	 * applied.
	 * @return the class names to exclude
	 * @since 1.3.0
	 */
	@AliasFor(annotation = EnableAutoConfiguration.class)
	String[] excludeName() default {};

	/**
	 * Base packages to scan for annotated components. Use {@link #scanBasePackageClasses}
	 * for a type-safe alternative to String-based package names.
	 * @return base packages to scan
	 * @since 1.3.0
	 */
	@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
	String[] scanBasePackages() default {};

	/**
	 * Type-safe alternative to {@link #scanBasePackages} for specifying the packages to
	 * scan for annotated components. The package of each class specified will be scanned.
	 * <p>
	 * Consider creating a special no-op marker class or interface in each package that
	 * serves no purpose other than being referenced by this attribute.
	 * @return base packages to scan
	 * @since 1.3.0
	 */
	@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
	Class<?>[] scanBasePackageClasses() default {};

}

SpringBootApplication 注解

比较重要的几个注解:

@SpringBootConfiguration

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {

}

@SpringBootConfiguration继承自@Configuration,二者功能也一致(可以穿插使用),标注当前类是配置类,
并会将当前类内声明的一个或多个以@Bean注解标记的方法的实例纳入到spring容器中,并且实例名就是方法名。

@EnableAutoConfiguration

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

	String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

	/**
	 * Exclude specific auto-configuration classes such that they will never be applied.
	 * @return the classes to exclude
	 */
	Class<?>[] exclude() default {};

	/**
	 * Exclude specific auto-configuration class names such that they will never be
	 * applied.
	 * @return the class names to exclude
	 * @since 1.3.0
	 */
	String[] excludeName() default {};

}

借助@Import的帮助,将所有符合自动配置条件的bean定义加载到IoC容器(建议放在根包路径下,这样可以扫描子包和类)。

可以看到@Import(AutoConfigurationImportSelector.class)
看下 AutoConfigurationImportSelector.Class

AutoConfigurationImportSelector

看下类继承关系:
AutoConfigurationImportSelector
AutoConfigurationImportSelector最终实现了ImportSelector#selectImports,代码如下

	@Override
	public String[] selectImports(AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return NO_IMPORTS;
		}
		// 1.从META-INF/spring-autoconfigure-metadata.properties文件中载入配置属性
		AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
				.loadMetadata(this.beanClassLoader);
		//2.获取注解属性
		AnnotationAttributes attributes = getAttributes(annotationMetadata);
		// 3.获取配置信息
		List<String> configurations = getCandidateConfigurations(annotationMetadata,
				attributes);
		// //4.移除重复的
		configurations = removeDuplicates(configurations);
		// 6.获取需要排除的
		Set<String> exclusions = getExclusions(annotationMetadata, attributes);
		// 7.校验排除类
		checkExcludedClasses(configurations, exclusions);
		// 8.删除所有需要排除的
		configurations.removeAll(exclusions);
		// 9.过滤器OnClassCondition(注解中配置的当存在某类才生效)
		configurations = filter(configurations, autoConfigurationMetadata);
		// 10.触发自动配置导入监听事件
		fireAutoConfigurationImportEvents(configurations, exclusions);
		return StringUtils.toStringArray(configurations);
	}

@ComponentScan

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {

	@AliasFor("basePackages")
	String[] value() default {};

	@AliasFor("value")
	String[] basePackages() default {};

	Class<?>[] basePackageClasses() default {};

	Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;

	Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class;

	ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT;

	String resourcePattern() default ClassPathScanningCandidateComponentProvider.DEFAULT_RESOURCE_PATTERN;
	
	boolean useDefaultFilters() default true;

	Filter[] includeFilters() default {};

	Filter[] excludeFilters() default {};

	boolean lazyInit() default false;

	@Retention(RetentionPolicy.RUNTIME)
	@Target({})
	@interface Filter {

		FilterType type() default FilterType.ANNOTATION;
		
		@AliasFor("classes")
		Class<?>[] value() default {};
		
		@AliasFor("value")
		Class<?>[] classes() default {};
		
		String[] pattern() default {};

	}

}

spring的自动扫描并加载符合条件的组件,可定义扫描范围,加载到IOC容器

我们可以通过basePackages等属性来细粒度的定制@ComponentScan自动扫描的范围,如果不指定,则默认Spring框架实现会从声明@ComponentScan所在类的package进行扫描。

SpringApplication.run() 方法

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

	public static ConfigurableApplicationContext run(Class<?>[] primarySources,
			String[] args) {
		// 创建SpringApplication对象,并调用run方法。
		return new SpringApplication(primarySources).run(args);
	}

new SpringApplication(primarySources).——创建SpringApplication实例

	public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
		this.resourceLoader = resourceLoader;
		Assert.notNull(primarySources, "PrimarySources must not be null");
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
		this.webApplicationType = deduceWebApplicationType();
		setInitializers((Collection) getSpringFactoriesInstances(
				ApplicationContextInitializer.class));
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		this.mainApplicationClass = deduceMainApplicationClass();
	}

	private WebApplicationType deduceWebApplicationType() {
		if (ClassUtils.isPresent(REACTIVE_WEB_ENVIRONMENT_CLASS, null)
				&& !ClassUtils.isPresent(MVC_WEB_ENVIRONMENT_CLASS, null)) {
			return WebApplicationType.REACTIVE;
		}
		for (String className : WEB_ENVIRONMENT_CLASSES) {
			if (!ClassUtils.isPresent(className, null)) {
				return WebApplicationType.NONE;
			}
		}
		return WebApplicationType.SERVLET;
	}

判断了webApplicationType是不是SERVLET,如果是,则创建Servlet的环境,否则创建基本环境。
spring boot是通过检查当前环境中是否存在
org.springframework.web.servlet.DispatcherServlet类来判断当前是否是web环境的。
接着往下看,获得了ConfigurableEnvironment环境以后,通过后面的代码对环境进行“微调”。
通过configureIgnoreBeanInfo(environment);如果System中的spring.beaninfo.ignore属性为空,就把当前环境中的属性覆盖上去:

run(String[] args) 核心方法。

public ConfigurableApplicationContext run(String... args) {
		// 创建秒表
		StopWatch stopWatch = new StopWatch();
		// 开始计时
		stopWatch.start();
		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
		configureHeadlessProperty();
		// 1.获取监听器
		SpringApplicationRunListeners listeners = getRunListeners(args);
		// 2.启动
		listeners.starting();
		try {
			// 3.获取ApplicationArguments对象
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(
					args);
			// 4.准备环境,并触发 ApplicationEnvironmentPreparedEvent 事件
			ConfigurableEnvironment environment = prepareEnvironment(listeners,
					applicationArguments);
			// 4.1.如果System中的spring.beaninfo.ignore属性为空,就把当前环境中的属性覆盖上去:
			configureIgnoreBeanInfo(environment);
			
			Banner printedBanner = printBanner(environment);
			// 5.创建应用上下文
			context = createApplicationContext();
			exceptionReporters = getSpringFactoriesInstances(
					SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);
			// 5.1.准备上下文
			prepareContext(context, environment, listeners, applicationArguments,
					printedBanner);
			// 5.2刷新上下文
			refreshContext(context);
			// 5.3刷新上下文后
			afterRefresh(context, applicationArguments);
			// 结束计时
			stopWatch.stop();
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass)
						.logStarted(getApplicationLog(), stopWatch);
			}
			listeners.started(context);
			callRunners(context, applicationArguments);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, listeners);
			throw new IllegalStateException(ex);
		}

		try {
			listeners.running(context);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, null);
			throw new IllegalStateException(ex);
		}
		return context;
	}

SpringApplicationRunListeners listeners = getRunListeners(args); —— 获取监听器

	private SpringApplicationRunListeners getRunListeners(String[] args) {
		Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
		return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
				SpringApplicationRunListener.class, types, this, args));
	}
	
	SpringApplicationRunListeners(Log log,
			Collection<? extends SpringApplicationRunListener> listeners) {
		this.log = log;
		this.listeners = new ArrayList<>(listeners);
	}
	
	private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
			Class<?>[] parameterTypes, Object... args) {
		ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
		// 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;
	}

	// 加载工厂名
	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 {
			// 路径:FACTORIES_RESOURCE_LOCATION = 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()) {
					List<String> factoryClassNames = Arrays.asList(
							StringUtils.commaDelimitedListToStringArray((String) entry.getValue()));
					result.addAll((String) entry.getKey(), factoryClassNames);
				}
			}
			cache.put(classLoader, result);
			return result;
		}
		catch (IOException ex) {
			throw new IllegalArgumentException("Unable to load factories from location [" +
					FACTORIES_RESOURCE_LOCATION + "]", ex);
		}
	}

	public static Properties loadProperties(Resource resource) throws IOException {
		Properties props = new Properties();
		// 填充属性
		fillProperties(props, resource);
		return props;
	}

	// 从给定的资源中填充给定的属性(采用ISO-8859-1编码)
	public static void fillProperties(Properties props, Resource resource) throws IOException {
		InputStream is = resource.getInputStream();
		try {
			String filename = resource.getFilename();
			if (filename != null && filename.endsWith(XML_FILE_EXTENSION)) {
				props.loadFromXML(is);
			}
			else {
				props.load(is);
			}
		}
		finally {
			is.close();
		}
	}

ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); — 准备环境

	private ConfigurableEnvironment prepareEnvironment(
			SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments) {
		// Create and configure the environment
		// 得到环境对象ConfigurableEnvironment,没有则创建一个StandardServletEnvironment
		// 
		ConfigurableEnvironment environment = getOrCreateEnvironment();
		// 配置环境信息(激活环境,通过从系统环境变量里取)
		configureEnvironment(environment, applicationArguments.getSourceArgs());
		// 发布ApplicationEnvironmentPreparedEvent事件,加载配置文件,具体请看(ConfigFileApplicationListener)。
		listeners.environmentPrepared(environment);
		bindToSpringApplication(environment);
		if (this.webApplicationType == WebApplicationType.NONE) {
			environment = new EnvironmentConverter(getClassLoader())
					.convertToStandardEnvironmentIfNecessary(environment);
		}
		ConfigurationPropertySources.attach(environment);
		return environment;
	}

	protected void configureEnvironment(ConfigurableEnvironment environment,String[] args) {
		configurePropertySources(environment, args);
		// 配置ConfigurableEnvironment中的激活属性
		configureProfiles(environment, args);
	}
	protected void configureProfiles(ConfigurableEnvironment environment, String[] args) {
		environment.getActiveProfiles(); // ensure they are initialized
		// additionalProfiles是项目启动时在main中SpringApplication.setAdditionalProfiles("")配置的
		Set<String> profiles = new LinkedHashSet<>(this.additionalProfiles);
		// 获取环境变量中设置的spring.profiles.active属性
		profiles.addAll(Arrays.asList(environment.getActiveProfiles()));
		// 赋值 activeProfiles
		environment.setActiveProfiles(StringUtils.toStringArray(profiles));
	}

createApplicationContext() —— 创建应用上下文

	protected ConfigurableApplicationContext createApplicationContext() {
		Class<?> contextClass = this.applicationContextClass;
		if (contextClass == null) {
			try {
				switch (this.webApplicationType) {
				case SERVLET:
					contextClass = Class.forName(DEFAULT_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);
	}

spring boot是根据不同的webApplicationType的类型,来创建不同的ApplicationContext的。

prepareContext —— 准备上下文

private void prepareContext(ConfigurableApplicationContext context,
			ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments, Banner printedBanner) {
		// 对ApplicationContext设置环境变量;
		context.setEnvironment(environment);
		// 配置属性ResourceLoader和ClassLoader属性;
		postProcessApplicationContext(context);
		// 循环初始化继承ApplicationContextInitializer接口的类
		applyInitializers(context);
		listeners.contextPrepared(context);
		if (this.logStartupInfo) {
			logStartupInfo(context.getParent() == null);
			logStartupProfileInfo(context);
		}

		// Add boot specific singleton beans
		context.getBeanFactory().registerSingleton("springApplicationArguments",
				applicationArguments);
		if (printedBanner != null) {
			context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
		}

		// Load the sources
		Set<Object> sources = getAllSources();
		Assert.notEmpty(sources, "Sources must not be empty");
		load(context, sources.toArray(new Object[0]));
		listeners.contextLoaded(context);
	}
	
	@Override
	public void setEnvironment(ConfigurableEnvironment environment) {
		super.setEnvironment(environment);
		this.reader.setEnvironment(environment);
		this.scanner.setEnvironment(environment);
	}

加粗样式

refreshContext —— 刷新上下文

	private void refreshContext(ConfigurableApplicationContext context) {
		refresh(context);
		if (this.registerShutdownHook) {
			try {
				context.registerShutdownHook();
			}
			catch (AccessControlException ex) {
				// Not allowed in some environments.
			}
		}
	}
@Override
	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// 准备刷新的上下文环境
			prepareRefresh();

			// 初始化BeanFactory
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// 对BeanFactory进行各种功能填充
			prepareBeanFactory(beanFactory);

			try {
				// 子类覆盖方法做额外的处理
				postProcessBeanFactory(beanFactory);

				// 激活各种BeanFactory处理器
				invokeBeanFactoryPostProcessors(beanFactory);

				// 注册拦截Bean创建的Bean处理,这里只是注册,真正调用是再拿去Bean的时候
				registerBeanPostProcessors(beanFactory);

				// 为上下文初始化Message源,即不同语言的消息体,国际化处理
				initMessageSource();

				// 初始化应用消息广播器,并放到applicationEventMulticaster bean中
				initApplicationEventMulticaster();

				// 留给子类来初始化其他bean
				onRefresh();

				// 在所有注册的bean中查找Listener bean,注册到消息广播中
				registerListeners();

				// 初始化剩下的单实例(非惰性)
				finishBeanFactoryInitialization(beanFactory);

				// 完成刷新过程,通知生命周期处理器lifecycleProcessor刷新过程,同时发出ContextRefreshEvent通知别人
				finishRefresh();
			}

			catch (BeansException ex) {
				if (logger.isWarnEnabled()) {
					logger.warn("Exception encountered during context initialization - " +
							"cancelling refresh attempt: " + ex);
				}

				// 销毁已经创建的单例Bean, 以避免资源占用.
				destroyBeans();

				// 取消refresh操作, 重置active标志. 
				cancelRefresh(ex);

				// Propagate exception to caller.
				throw ex;
			}

			finally {
				// Reset common introspection caches in Spring's core, since we
				// might not ever need metadata for singleton beans anymore...
				// 重置Spring的核心缓存
				resetCommonCaches();
			}
		}
	}

afterRefresh —— 刷新上下文后

protected void afterRefresh(ConfigurableApplicationContext context,
			ApplicationArguments args) {
	}

上面的refresh() 便是构建IoC的最重要的一步

总结

参考博文: https://blog.csdn.net/u010811939/article/details/80592461

概述:

  • 构造SpringApplication的实例(时序图步骤1-2)
  • 调用SpringApplication.run()方法(时序图步骤3)
    • 构造SpringApplicationRunListeners 实例(时序图步骤3.1.1)
    • 发布ApplicationStartedEvent事件(时序图步骤3.1.2)
    • SpringApplicationRunListeners 实例准备环境信息(时序图步骤3.1.3)
    • 创建ApplicationContext对象(时序图步骤3.1.4)
    • ApplicationContext实例准备环境信息(时序图步骤3.1.5)
    • 刷新的上下文(时序图步骤3.1.6)
      时序图
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值