上一篇我们已经简单看了,我启动一个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我会在监听器里面好好整理下,这里先不整理。
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);
}
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
三种枚举类型返回值的意义是:
Web环境为Reactive主要指的是内嵌的ReactiveWeb应用,比如SpringFramework5.0添加的新功能Spring Webflux。而在网上查到的资料相对于传统的web框架来说,它可以运行在诸如Netty,Undertow及支持Servlet3.1的容器上,因此它的运行环境的可选择行要比传统web框架多的多。
根据官方的说法,webflux主要在如下两方面体现出独有的优势:
因为个人也是看到这些才了解到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 重启初始化的数组
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根据需要提供了一些实现这个接口的实现类。我这里简单介绍其中几个。
public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {
void initialize(C var1);
}
类图 (部分)
主要进行配置检查的逻辑
@Override
public void initialize(ConfigurableApplicationContext context) {
// 注册ConfigurationWarningsPostProcessor到spring容器中
context.addBeanFactoryPostProcessor(
new ConfigurationWarningsPostProcessor(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 + "'";
}
主要是进行参数检验的操作
@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输出
主要是创建一个默认的应用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);
}
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主要使用了一种递增策略生成
主要是将环境中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);
}
}
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);
}
}