-- 以下内容均基于2.1.8.RELEASE版本

紧接着上一篇(二)SpringBoot启动过程的分析-环境信息准备,本文将分析环境准备完毕之后的下一步操作:ApplicationContext的创建。

创建指定类型的应用程序上下文容器

// SpringApplication.java

ConfigurableApplicationContext context = 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);
}

创建ApplicationContext时可以指定需要创建的上下文类型,默认是根据当前应用类型来创建。在SpringApplication.java中指定了几种默认类型,他们分别是默认的非Web应用上下文容器、默认的Web应用上下文容器和响应式Web上下文容器

public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context." + "annotation.AnnotationConfigApplicationContext";

public static final String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot." + "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";

public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework." + "boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext";

① - 是否有指定的ApplicationContext,若有直接实例化

② - 实例化Web应用上下文容器

③ - 实例化响应式Web应用上下文容器

④ - 实例化非Web应用上下文容器

⑤ - 使用无参构造函数对指定的上下文容器进行实例化

AnnotationConfigServletWebServerApplicationContext

对于常规的以SpringBoot开发的web应用来说,AnnotationConfigServletWebServerApplicationContext将会是默认的上下文容器。它可以解析@Configuration等JSR-330定义的注解,查看它的包路径,可以看到,它是属于SpringBoot下的上下文容器。

public class AnnotationConfigServletWebServerApplicationContext extends ServletWebServerApplicationContext implements AnnotationConfigRegistry {

	private final AnnotatedBeanDefinitionReader reader;

	private final ClassPathBeanDefinitionScanner scanner;

	private final Set<Class<?>> annotatedClasses = new LinkedHashSet<>();

	private String[] basePackages;

	public AnnotationConfigServletWebServerApplicationContext() {
// ①
this.reader = new AnnotatedBeanDefinitionReader(this);
// ②
this.scanner = new ClassPathBeanDefinitionScanner(this);
}
}

① - 构造AnnotatedBeanDefinitionReader,传入当前对象作为Bean注册器

② - 构造ClassPathBeanDefinitionScanner,传入当前对象作为Bean注册器

它内部的无参构造方法就干了两件事:初始化reader和scanner。他们两个的存在使当前的上下文容器支持以带有@Configuration注解的类作为加载Bean入口,也支持以指定的basePackage来作为加载Bean的入口,同时还支持注解的过滤。

上下文容器重要成员之 AnnotatedBeanDefinitionReader

用编程方式注册带注解的Bean到容器中,也就是基于注解配置的Bean的注册。

public class AnnotatedBeanDefinitionReader {

	// ①
private final BeanDefinitionRegistry registry; // ②
private BeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator(); // ③
private ScopeMetadataResolver scopeMetadataResolver = new AnnotationScopeMetadataResolver(); // ④
private ConditionEvaluator conditionEvaluator; // ⑤
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) {
this(registry, getOrCreateEnvironment(registry));
}
// ...省略部分代码
}

① - BeanDefinition注册器

② - BeanName生成器

③ - Bean的作用域元数据解析器

④ - 条件注解评估器,用于判断是否跳过被条件注解修饰的类的加载

⑤ - 通过Bean注册器来构造Reader

注册内定的PostProcessor

在初始化AnnotatedBeanDefinitionReader的时候,将一些用于作基础处理的类处理器优先注册到容器内部,不太明白也没关系,照着代码看下去,看完几个就明白是干什么的了。

public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
Assert.notNull(environment, "Environment must not be null");
this.registry = registry;
this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
// 注册一些固定的内部指定的类到容器中
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
} public static void registerAnnotationConfigProcessors(BeanDefinitionRegistry registry) {
registerAnnotationConfigProcessors(registry, null);
} public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors( registry, @Nullable Object source) { DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry); if (beanFactory != null) {
// ①
if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) {
beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
}
// ②
if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) {
beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
}
} // ③
Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8); if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
} if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
} // Check for JSR-250 support, and if present add the CommonAnnotationBeanPostProcessor.
if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
} // Check for JPA support, and if present add the PersistenceAnnotationBeanPostProcessor.
if (jpaPresent && !registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition();
try {
def.setBeanClass(ClassUtils.forName(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, AnnotationConfigUtils.class.getClassLoader()));
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException("Cannot load optional framework class: " + PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, ex);
}
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME));
} if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME));
} if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME));
} return beanDefs;
}

① - 给BeanFactory添加AnnotationAwareOrderComparator,用于处理Bean的排序

② - 给BeanFactory添加ContextAnnotationAutowireCandidateResolver,用于处理@Lazy注解和@Qualifier注解

③ - 注册指定的类到容器

SpringBoot默认优先注册了以下PostProcessor类,并以不同的名称来称呼这些类:

左边的为BeanDefinition的名称,右边为对应的类,名称的前缀都统一是org.springframework.context.annotation

  1. 由Spring内部管理的用于处理配置注解的处理器

    internalConfigurationAnnotationProcessor -> ConfigurationClassPostProcessor.class

  2. 由Spring内部管理的用于处理自动装配注解的处理器

    internalAutowiredAnnotationProcessor -> AutowiredAnnotationBeanPostProcessor.class

  3. 由Spring内部管理的用于处理JSR-250注解的处理器

    internalCommonAnnotationProcessor -> CommonAnnotationBeanPostProcessor.class

  4. 由Spring内部管理的用于处理JPA注解的处理器

    internalPersistenceAnnotationProcessor -> PersistenceAnnotationBeanPostProcessor.class

  5. 由Spring内部管理的用于处理@EventListener注解的处理器

    internalEventListenerProcessor -> EventListenerMethodProcessor.class

  6. 由Spring内部管理的用于指定EventListenerFactory默认实现的类

    internalEventListenerFactory -> DefaultEventListenerFactory.class

其实看到这里就明白许多,单纯的就是Spring固定了一些类要首先注册进去容器,也就是在处理注解的Reader准备完毕的时候,它已经准备好了一些后续要使用的类,他们用于在不同阶段来处理不同的事情。至于注册的这些类分别可以完成什么工作,将在他们真正开始执行的时候再进行分析。至此AnnotatedBeanDefinitionReader的初始化完成了。

上下文容器重要成员之 ClassPathBeanDefinitionScanner

和上面的Reader不同的是它用于扫描类路径上的Bean,通过给定的basePackage路径来扫描类。它可以使用可配置的过滤器来检测候选类,已确定他们将是否被加载。默认的过滤器包括Spring框架的@Component、@Controller、@Service、@Repository

// 构造方法一
public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry) {
this(registry, true);
} // 构造方法二
public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters) {
this(registry, useDefaultFilters, getOrCreateEnvironment(registry));
} // 构造方法三
public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,
Environment environment) { this(registry, useDefaultFilters, environment,
(registry instanceof ResourceLoader ? (ResourceLoader) registry : null));
} // 构造方法四
public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters, Environment environment, @Nullable ResourceLoader resourceLoader) { Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
this.registry = registry;
// 使用默认过滤器,从构造方法一直接传入true,代表它是默认的。
if (useDefaultFilters) {
// 注册默认过滤器
registerDefaultFilters();
}
// 设置环境对象和资源加载器
setEnvironment(environment);
setResourceLoader(resourceLoader);
}

通过多个重载的构造方法来构造ClassPathBeanDefinitionScanner对象。最后一个实际使用的构造方法传入了类注册器、是否使用默认过滤器、环境对象、资源加载器。若传入的是一个普通的BeanDefinitionRegistry(实现了BeanDefinitionRegistry接口但没有实现ResourceLoader接口的类)那么默认的资源加载器将会是org.springframework.core.io.support.PathMatchingResourcePatternResolver.

注册默认注解过滤器

为@Component注册过滤器, 还会隐式的注册@Controller、@Service、@Repository。为何说是隐式的呢?查看这几个注解便知。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller { @AliasFor(annotation = Component.class)
String value() default "";
} @Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service { @AliasFor(annotation = Component.class)
String value() default "";
} @Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Repository { @AliasFor(annotation = Component.class)
String value() default ""; }

他同时使用了@Component注解,并将别名设置为Component.class

// ClassPathScanningCandidateComponentProvider.java

protected void registerDefaultFilters() {
// 新增Component注解过滤器(包含Controller、Service、Repository)
this.includeFilters.add(new AnnotationTypeFilter(Component.class));
ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
try {
// 如果可用的话,支持JSR-250的ManagedBean注解过滤器
this.includeFilters.add(new AnnotationTypeFilter(((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));
logger.trace("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning");
}
catch (ClassNotFoundException ex) {
// JSR-250 1.1 API (as included in Java EE 6) not available - simply skip.
}
try {
// 如果可用的话,支持JSR-330支持的Named注解过滤器
this.includeFilters.add(new AnnotationTypeFilter(((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false));
logger.trace("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");
}
catch (ClassNotFoundException ex) {
// JSR-330 API not available - simply skip.
}
}

总结

在上下文容器创建完毕且未刷新的情况下,内部已经优先注册了一批PostProcessor,毫无疑问,他们将会在容器接下来的操作中扮演重要角色。这里只需要理解容器在程序运行的不同阶段会有不同的执行逻辑。大概知晓它在创建的时候做了什么,在后续文章中会逐步分析。

(三)SpringBoot启动过程的分析-创建应用程序上下文的更多相关文章

  1. (四)SpringBoot启动过程的分析-预处理ApplicationContext

    -- 以下内容均基于2.1.8.RELEASE版本 紧接着上一篇(三)SpringBoot启动过程的分析-创建应用程序上下文,本文将分析上下文创建完毕之后的下一步操作:预处理上下文容器. 预处理上下文 ...

  2. (五)SpringBoot启动过程的分析-刷新ApplicationContext

    -- 以下内容均基于2.1.8.RELEASE版本 紧接着上一篇[(四)SpringBoot启动过程的分析-预处理ApplicationContext] (https://www.cnblogs.co ...

  3. (一)SpringBoot启动过程的分析-启动流程概览

    -- 以下内容均基于2.1.8.RELEASE版本 通过粗粒度的分析SpringBoot启动过程中执行的主要操作,可以很容易划分它的大流程,每个流程只关注重要操作为后续深入学习建立一个大纲. 官方示例 ...

  4. (二)SpringBoot启动过程的分析-环境信息准备

    -- 以下内容均基于2.1.8.RELEASE版本 由上一篇SpringBoot基本启动过程的分析可以发现在run方法内部启动SpringBoot应用时采用多个步骤来实现,本文记录启动的第二个环节:环 ...

  5. SpringBoot启动过程原理

    最近这两年springboot突然火起来了,那么我们就来看看springboot的运行原理. 一.springboot的三种启动方式: 1.运行带有main方法的2.通过命令 Java -jar命令3 ...

  6. Android应用程序组件Content Provider的启动过程源代码分析

    文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6963418 通过前面的学习,我们知道在Andr ...

  7. Android系统默认Home应用程序(Launcher)的启动过程源代码分析

    在前面一篇文章中,我们分析了Android系统在启动时安装应用程序的过程,这些应用程序安装好之后,还需要有一个 Home应用程序来负责把它们在桌面上展示出来,在Android系统中,这个默认的Home ...

  8. Spring Boot 学习笔记一(SpringBoot启动过程)

    SpringBoot启动 Spring Boot通常有一个名为*Application的入口类,在入口类里有一个main方法,这个main方法其实就是一个标准的java应用的入口方法. 在main方法 ...

  9. U-Boot启动过程完全分析

    U-Boot启动过程完全分析 1.1       U-Boot工作过程 U-Boot启动内核的过程可以分为两个阶段,两个阶段的功能如下: (1)第一阶段的功能 硬件设备初始化 加载U-Boot第二阶段 ...

随机推荐

  1. cnblogs blogs backup & node.js crawler

    cnblogs blogs backup & node.js crawler refs xgqfrms 2012-2020 www.cnblogs.com 发布文章使用:只允许注册用户才可以访 ...

  2. Chrome 黑科技

    Chrome 黑科技 秒变记事本 data:text/html, <htmlcontenteditable> OK <a href="data:text/html, &qu ...

  3. JavaScript 设计模式: 发布者-订阅者模式

    JavaScript 设计模式: 发布者-订阅者模式 发布者-订阅者模式 https://github.com/Kelichao/javascript.basics/issues/22 https:/ ...

  4. 如何关闭 iPad Pro 自动开启 wifi 和蓝牙

    如何关闭 iPad Pro 自动开启 wifi 和蓝牙 为了省电,明明关闭了,但是发现每天都会自动开启,什么鬼设计 https://support.apple.com/zh-cn/HT208086 h ...

  5. how to write an ebook that can support published by format PDF, Epub, Mobi and so on

    how to write an ebook that can support published by format PDF, Epub, Mobi and so on 如何写一本自己的电子书,支持各 ...

  6. moment.js 时间格式转换

    moment.js 时间格式转换 moment.js 时间转化 bug 格式错误 bug 02:00 => 14:00 format HH 与 hh HH === 24 小时制 hh === 1 ...

  7. 币圈沸腾!SPC空投上线!不要错过!

    币圈最近处于沸腾的时刻,NGK侧链代币SPC已上线钱包,3.0公链NGK生态之SPC空投又来了,NGK的上一个项目BGV投资收益率最高破一千七百倍,NGK官方此次以算力持有者为中心,将发起第二轮福利- ...

  8. 为什么说NGK的去中心化预言机越来越受欢迎?

    2020年区块链市场非常火热,从年初的交易所杠杆,到Defi热潮,一波连着一波,风向不断切换,很多人无奈感叹跟不上时代,很多人欢欣雀跃登上了早班车.随着Defi的不断火热,预言机也进入了大众视野.NG ...

  9. LayUI之数据表格扩展

    1.点击一行 选中 以下代码需要在表格渲染完成时加载. 1)当单击表格行时,把单选按钮设为选中状态 //当单击表格行时,把单选按钮设为选中状态 $(document).on("click&q ...

  10. Linux解压缩相关命令

    Linux解压缩相关命令 运行级别: 0:关机 1:单用户 2:多用户无网络连接 3:多用户有网络连接 4:系统保留 5:图形界面 6:系统重启 通过init[0123456]来切换不同的运行级别 g ...