-- 以下内容均基于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. 1. mac 手动安装nodejs搭建vue环境

    为什么选择手动安装nodejs呢? 因为使用mac自动安装还要更新homebrew,还要安装xcode tool, 太费劲了,不如手动安装, 卸载起来也方便 再一个, 我是后台开发者, 对前端页面, ...

  2. 蓝湖 UI 设计稿上如何生成渐变色和复制渐变色

    蓝湖 UI 设计稿上如何生成渐变色和复制渐变色 Sketch 生成渐变色 不要上传图片,切图 如果是切图,切图模式下就不会生成 css 代码了 复制渐变色 OK .button { width: 28 ...

  3. js array flat all in one

    js array flat all in one array flat flatMap flatMap > flat + map https://developer.mozilla.org/en ...

  4. CSS3 & Grid Layout All In One

    CSS3 & Grid Layout All In One W3C https://www.w3.org/TR/css-grid-1/ Grid Layout is a new layout ...

  5. where is the storage location of the browser's HTTP cache? disk or memory

    where is the storage location of the browser's HTTP cache? disk or memory HTTP cache & storage l ...

  6. 为什么说USDN是一种应用型稳定币?

    USDN是由NGK Global出品的一种新型稳定币系统,里面是涵盖了包括货币供需.Bancor.抵押借贷等在内的一整套算法.该稳定币构想一经提出,便在社区引发了不小的热度. 官方对于USDN的定位是 ...

  7. 05.其他创建numpy数组的方法

    >>> import numpy as np >>> np.zeros(10,dtype=int) array([0, 0, 0, 0, 0, 0, 0, 0, 0 ...

  8. ipv4ipv6 地址字符串表示最大长度

    1 for IPV4 #define INET_ADDRSTRLEN 16 111.112.113.114 32位IPV4地址,使用10进制+句点表示时,所占用的char数组的长度为16,其中包括最后 ...

  9. 解决margin-top无效问题

    当两个空的块级元素嵌套时,如果内部的块设置有margin-top属性,而且父元素没有下边解决方法所述的特征,那么内部块的margin-top属性会绑架父元素(即将margin-top传递凌驾给了父元素 ...

  10. HarmonyOS三方件开发指南(13)-SwipeLayout侧滑删除

    鸿蒙入门指南,小白速来!0基础学习路线分享,高效学习方法,重点答疑解惑--->[课程入口] 目录:1. SwipeLayout组件功能介绍2. SwipeLayout使用方法3. SwipeLa ...