SpringBoot源码简读——2.1 SpringApplication 构造方法

概述

上一篇我们已经简单看了,我启动一个spring boot项目spring会做的操作。大的步骤主要有两部分,一步在构造函数中,一部分在run中。那么我们先看下构造函数中需要关注哪些。

个人习惯把(ApplicationContext成为容器,但是网上很多文章都成为应用,所以下说的应用指的就是ApplicationContext,环境指的Environment)

构造函数主要三个东西比较关键

推断当前环境是哪种Web环境

// web应用类型
this.webApplicationType = WebApplicationType.deduceFromClasspath();

初始化ApplicationContextInitializer

// web应用类型
this.webApplicationType = WebApplicationType.deduceFromClasspath();

初始化ApplicationListener

// web应用类型
this.webApplicationType = WebApplicationType.deduceFromClasspath();

其中初始化ApplicationListener我会在监听器里面好好整理下,这里先不整理。

推断当前环境是哪种Web环境

webApplicationType 主要是创建应用或者容器(ApplicationContext)的关键。在run方法中主要通过这个webApplicationType创建有应用

run中创建应用

	protected ConfigurableApplicationContext createApplicationContext() {
		// 获得上面定义的容器类
		Class<?> contextClass = this.applicationContextClass;
		// 如果为空,则重新进行一次web容器类型判断一次返回对应的容器类,默认使用DEFAULT_CONTEXT_CLASS
		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);
	}

webApplicationType的确定

org/springframework/boot/WebApplicationType.java

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

上面总体逻辑就是检测存在相关类然后返回三种类型之一

1.第一个判断
当存在 org.springframework.web.reactive.DispatcherHandler
不存在 org.springframework.web.servlet.DispatcherServlet
不存在 org.glassfish.jersey.servlet.ServletContainer
返回 WebApplicationType.REACTIVE

2.第二个判断
当存在 javax.servlet.Servlet 或 org.springframework.web.context.ConfigurableWebApplicationContext
返回 WebApplicationType.NONE

否则返回 WebApplicationType.SERVLET

三种枚举类型返回值的意义是:

  • WebApplicationType.REACTIVE:Web环境为Reactive
  • WebApplicationType.SERVLET:Web环境为Servlet
  • WebApplicationType.NONE:Web环境为不是Web环境

关于Web环境为Reactive

Web环境为Reactive主要指的是内嵌的ReactiveWeb应用,比如SpringFramework5.0添加的新功能Spring Webflux。而在网上查到的资料相对于传统的web框架来说,它可以运行在诸如Netty,Undertow及支持Servlet3.1的容器上,因此它的运行环境的可选择行要比传统web框架多的多。

根据官方的说法,webflux主要在如下两方面体现出独有的优势:

  1. 非阻塞式
  2. 函数式编程端点

因为个人也是看到这些才了解到Spring Webflux,后续会单独了解这个功能,目前这一块先不细说。

容器的创建

根据上面我们贴的run方法中创建容器代码可以知道三种枚举分别对应的类

WebApplicationType.REACTIVE

org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext
根据类上注释可以知道,这个类继承ReactiveWebServerApplicationContext主要支持了类似注解加载配置。主要是针对@Configuration当然也支持@Component

WebApplicationType.SERVLET

org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext
根据类上注释可以知道,这个类继承ServletWebServerApplicationContext主要支持了类似注解加载配置。主要是针对@Configuration当然也支持@Component

WebApplicationType.NONE

org.springframework.context.annotation.AnnotationConfigApplicationContext实现基于Java的配置类加载Spring的应用上下文。这一块主要是spring的内容

初始化ApplicationContextInitializer

获得ApplicationContextInitializer对象

获得ApplicationContextInitializer对象的代码相当简单

	// ApplicationContextInitializer 重启初始化的数组
	setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
				
	/**
	 * 获得指定类型的数组
	 * @param type
	 * @param 
	 * @return
	 */
	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) {
		ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
		// Use names and ensure unique to protect against duplicates
		// 加载在 `META-INF/spring.factories` 里的类名的数组
		// 在 META-INF/spring.factories 文件中,会以 KEY-VALUE 的格式,配置每个类对应的实现类们
		Set<String> names = new LinkedHashSet<>(
				SpringFactoriesLoader.loadFactoryNames(type, classLoader));
		// 创建对象们
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
				classLoader, args, names);
		// 进行排序
		AnnotationAwareOrderComparator.sort(instances);
		return instances;
	}	

而ApplicationContextInitializer这个接口是springframework提供的主要用来初始化应用的回调接口,他的调用时机是在容器刷新前。

springboot根据需要提供了一些实现这个接口的实现类。我这里简单介绍其中几个。

  • ConfigurationWarningsApplicationContextInitializer 配置检查
  • ContextIdApplicationContextInitializer 容器编号生成
  • DelegatingApplicationContextInitializer ApplicationContextInitializer 类

接口

public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {
    void initialize(C var1);
}

类图 (部分)

ConfigurationWarningsApplicationContextInitializer

initialize

主要进行配置检查的逻辑

	@Override
	public void initialize(ConfigurableApplicationContext context) {
		// 注册ConfigurationWarningsPostProcessor到spring容器中
		context.addBeanFactoryPostProcessor(
				new ConfigurationWarningsPostProcessor(getChecks()));
	}

getChecks

主要新建了一个检查配置的方法

 	/**
	 * 只有一个 ComponentScanPackageCheck 对象
	 * 检查是否使用了 @ComponentScan 注解,扫描了指定扫描的包
	 */
	protected Check[] getChecks() {
		return new Check[] { new ComponentScanPackageCheck() };
	}
 

ComponentScanPackageCheck 内部调用的检测方法

		
		@Override
		public String getWarning(BeanDefinitionRegistry registry) {
			// 获得需要被扫描的包
			Set<String> scannedPackages = getComponentScanningPackages(registry);
			// 获得被扫描包中出错的包
			List<String> problematicPackages = getProblematicPackages(scannedPackages);
			if (problematicPackages.isEmpty()) {
				return null;
			}
			// 如果有错误则返回错误信息
			return "Your ApplicationContext is unlikely to "
					+ "start due to a @ComponentScan of "
					+ StringUtils.collectionToDelimitedString(problematicPackages, ", ")
					+ ".";
		}

获得所有ComponentScan注解

  		protected Set<String> getComponentScanningPackages(
				BeanDefinitionRegistry registry) {
			// 扫描的包的集合
			Set<String> packages = new LinkedHashSet<>();
			// 获得所有bean的名字
			String[] names = registry.getBeanDefinitionNames();
			for (String name : names) {
				// 从容器中获得BeanDefinition,如果其是AnnotatedBeanDefinition
				BeanDefinition definition = registry.getBeanDefinition(name);
				if (definition instanceof AnnotatedBeanDefinition) {
					AnnotatedBeanDefinition annotatedDefinition = (AnnotatedBeanDefinition) definition;
					// 如果有@ComponentScan注解,则添加到packages 中
					addComponentScanningPackages(packages,
							annotatedDefinition.getMetadata());
				}
			}
			return packages;
		}
  

获得错误的表配置,PROBLEM_PACKAGES已经在静态中配置了

   
        static {
			Set<String> packages = new HashSet<>();
			packages.add("org.springframework");
			packages.add("org");
			// 有问题包的集合,禁止扫描集合中的包
			PROBLEM_PACKAGES = Collections.unmodifiableSet(packages);
		}
		
 		private List<String> getProblematicPackages(Set<String> scannedPackages) {
			List<String> problematicPackages = new ArrayList<>();
			for (String scannedPackage : scannedPackages) {
				// 判断是否在PROBLEM_PACKAGES 中如果是则添加到问题包集合中
				if (isProblematicPackage(scannedPackage)) {
					problematicPackages.add(getDisplayName(scannedPackage));
				}
			}
			return problematicPackages;
		}
		
		private boolean isProblematicPackage(String scannedPackage) {
			if (scannedPackage == null || scannedPackage.isEmpty()) {
				return true;
			}
			// scannedPackage是否存在于PROBLEM_PACKAGES中
			return PROBLEM_PACKAGES.contains(scannedPackage);
		}

		private String getDisplayName(String scannedPackage) {
			if (scannedPackage == null || scannedPackage.isEmpty()) {
				return "the default package";
			}
			return "'" + scannedPackage + "'";
		}
 

BeanDefinitionRegistryPostProcessor

主要是进行参数检验的操作

		@Override
		public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)
				throws BeansException {
			// 遍历Check数组,获得Warning信息,假如存在则打印Warning日志
			for (Check check : this.checks) {
				String message = check.getWarning(registry);
				if (StringUtils.hasLength(message)) {
					warn(message);
				}
			}

		}

核心方法主要是调用check的getWarning方法然后将信息进行warn输出

ContextIdApplicationContextInitializer

initialize

主要是创建一个默认的应用ID

	@Override
	public void initialize(ConfigurableApplicationContext applicationContext) {
		// 根据容器获得ContextId
		ContextId contextId = getContextId(applicationContext);
		// 设置容器id
		applicationContext.setId(contextId.getId());
		// 注册容器id到容器中
		applicationContext.getBeanFactory().registerSingleton(ContextId.class.getName(),
				contextId);
	}

ID获得策略

private ContextId getContextId(ConfigurableApplicationContext applicationContext) {
	// 获得父容器
	ApplicationContext parent = applicationContext.getParent();
	// 如果父容器存在,且有对应的ContextId对象,则使用它生产当前容器的ContextId
	if (parent != null && parent.containsBean(ContextId.class.getName())) {
		return parent.getBean(ContextId.class).createChildId();
	}
	// 创建ContextId对象
	return new ContextId(getApplicationId(applicationContext.getEnvironment()));
}



/**
 * The ID of a context.
 * Spring 容器编号的封装
 */
class ContextId {

	/**
	 * 线程安全的递增编号
	 */
	private final AtomicLong children = new AtomicLong(0);
	/**
	 * 编号
	 */
	private final String id;

	ContextId(String id) {
		this.id = id;
	}

	ContextId createChildId() {
		return new ContextId(this.id + "-" + this.children.incrementAndGet());
	}

	String getId() {
		return this.id;
	}

}

可以看到应用的ID主要使用了一种递增策略生成

DelegatingApplicationContextInitializer

initialize

主要是将环境中context.initializer.classes的值对应类,初始化后绑定到应用上,并且启动

@Override
public void initialize(ConfigurableApplicationContext context) {
	// 获得容器中环境
	ConfigurableEnvironment environment = context.getEnvironment();
	// 从环境中获得配置的ApplicationContextInitializer
	List<Class<?>> initializerClasses = getInitializerClasses(environment);
	// 进行初始化,假如有的话
	if (!initializerClasses.isEmpty()) {
		applyInitializerClasses(context, initializerClasses);
	}
}

获得context.initializer.classes的值对应类

private List<Class<?>> getInitializerClasses(ConfigurableEnvironment env) {
	// 从环境中获得指定参数的类名称
	String classNames = env.getProperty(PROPERTY_NAME);
	List<Class<?>> classes = new ArrayList<>();
	if (StringUtils.hasLength(classNames)) {
		// 将名称使用,分割,获得class字节码后放入集合
		for (String className : StringUtils.tokenizeToStringArray(classNames, ",")) {
			classes.add(getInitializerClass(className));
		}
	}
	return classes;
}

private Class<?> getInitializerClass(String className) throws LinkageError {
	try {
		// 获得对应类名称的class对象
		Class<?> initializerClass = ClassUtils.forName(className,
				ClassUtils.getDefaultClassLoader());
		Assert.isAssignable(ApplicationContextInitializer.class, initializerClass);
		return initializerClass;
	}
	catch (ClassNotFoundException ex) {
		throw new ApplicationContextException(
				"Failed to load context initializer class [" + className + "]", ex);
	}
}

绑定到环境中并执行

	/**
	 * 初始化指定类
	 * @param context
	 * @param initializerClasses
	 */
	private void applyInitializerClasses(ConfigurableApplicationContext context,
			List<Class<?>> initializerClasses) {
		// 获得容器的类型
		Class<?> contextClass = context.getClass();
		List<ApplicationContextInitializer<?>> initializers = new ArrayList<>();
		for (Class<?> initializerClass : initializerClasses) {
			// 将容器的初始化类放入集合中
			initializers.add(instantiateInitializer(contextClass, initializerClass));
		}
		// 执行初始化逻辑
		applyInitializers(context, initializers);
	}
	
	/**
	 * 执行初始化逻辑
	 * @param context
	 * @param initializers
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	private void applyInitializers(ConfigurableApplicationContext context,
			List<ApplicationContextInitializer<?>> initializers) {
		// 进行一次排序
		initializers.sort(new AnnotationAwareOrderComparator());
		for (ApplicationContextInitializer initializer : initializers) {
			// 执行初始化类的初始化方法
			initializer.initialize(context);
		}
	}

你可能感兴趣的:(#,Spring,Boot源码,源码)