-- 以下内容均基于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. css background transparent All In One

    css background transparent All In One opacity ul { max-height: 100px; /* max-height: 187px; */ overf ...

  2. Koa & node.js

    KOA https://github.com/koajs/koa https://koajs.com/ $ nvm install 7 # node.js 7 + $ nvm install 10 $ ...

  3. Android低功耗蓝牙(蓝牙4.0)——BLE开发(上)

    段时间,公司项目用到了手机APP和蓝牙设备的通讯开发,这里也正好对低功耗蓝牙(蓝牙4.0及以后标准)的开发,做一个总结. 蓝牙技术联盟在2010年6月30号公布了蓝牙4.0标准,4.0标准在蓝牙3.0 ...

  4. 灰度发布 & A/B 测试

    灰度发布 & A/B 测试 http://www.woshipm.com/pmd/573429.html 8 https://testerhome.com/topics/15746 scree ...

  5. Guava - LoadingCache实现Java本地缓存

    前言 Guava是Google开源出来的一套工具库.其中提供的cache模块非常方便,是一种与ConcurrentMap相似的缓存Map. 官方地址:https://github.com/google ...

  6. Docker 镜像加速教程

    原文链接:https://fuckcloudnative.io/posts/docker-registry-proxy/ 在使用 Docker 和 Kubernetes 时,我们经常需要访问 gcr. ...

  7. 用go实现常见的数据结构

    目录 1 golang常见数据结构实现 1.1 链表 1.2 可变数组 1.3 栈和队列 1.3.1 原生切片实现栈和队列 1.3.1.1 切片原生栈实现 1.3.1.2 切片原生队列实现 1.3.2 ...

  8. 记录mysql查询数据遇到的一个小问题

    今天在测试的时候,需要使用mysql对插入的数据进行检验,但是写完查询语句的时候执行会报错.原因很简单,这个表名是order(订单),在MySQL语言中order是用来排序的关键字,原则上讲是不能作为 ...

  9. Jacobi与SOR迭代法的实现与性能比较及均匀间距与Chebyshev插值的实现、性能分析及二者生成的插值误差比较

    这篇文章给出(1)Jacobi与SOR迭代法的实现与性能比较及(2)均匀间距与Chebyshev插值的实现.性能分析及二者生成的插值误差比较,给出完整的实现代码,没有进行性能优化,仅供参考. (1)J ...

  10. kubernetes Pod亲和性

    三种调度粘性,主要根据官方文档说明: NodeSelector(定向调度).NodeAffinity(Node亲和性).PodAffinity(Pod亲和性). 1.      nodeSelecto ...