Spring源码解析容器初始化构造方法

目录
  • 前言
  • 构造方法

前言

Spring框架被广泛应用于我们的日常工作中,但是很长时间以来我都是只会使用,不懂它的作用原理。通过最近一段时间的阅读源码,个人发现通过阅读源码,能够帮助我们了解Spring的设计理念,并且对Java编程中的一些设计模式更加熟悉,所以记录一下自己对Spring源码的理解。

在开始进行源码学习前,首先再回顾一下三种Spring编程风格:

  • 基于Schema,即通过xml标签的配置方式
  • 基于Annotation的注解技术,使用@Component等注解配置bean
  • 基于Java Config,简单来说就是使用@Configuration@Bean进行配置

基于注解的方式需要通过xml或java config来开启。

在使用xml时,需要手动开启对注解的支持:

<context: annotation-config/> 

当然,如果在xml中配置了扫描包,现在也可以光添加下面这一行,这行代码中已经包含了注解的开启功能。

<context: component-sacn base-package="com"/>

如果你使用的是下面AnnotationConfigApplicationContext这种方式,那么就不需要添加任何操作了,其中已经包含了对注解的支持。

AnnotationConfigApplicationContext ctx
	=new AnnotationConfigApplicationContext(SpringConfig.class);

在实际使用过程中,三种方式是可以混合使用的,不存在冲突。按照下面这种方式作为AnnotationConfigApplicationContext传入的配置文件,即可实现三种风格的统一使用:

@Configuration
@ComponentScan("com")
@ImportResource("classpath:spring.xml")
public class SpringConfig{
}

之前也有小伙伴对我说,在开始学习Spring的时候,差点因为配置繁杂的xml被劝退,我也翻阅了一下网上spring入门的技术文章,确实很多还是停留在使用xml的方式上。但是其实如果你翻阅一下spring5的官方文档,可以看出官方是推荐我们使用注解的方式的。

尤其是现在的Spring Boot更多的是基于注解,省略了很多配置的过程,对新手更加友好,降低了劝退率,所以本文将基于注解的方式进行源码解析,另外再说明一下本文基于spring-framework-5.0.x源码。

使用注解的方式初始化一个Spring环境,只需要下面一行代码:

AnnotationConfigApplicationContext context
    = new AnnotationConfigApplicationContext(SpringConfig.class);

如果看一下它的构造方法,那么可以将它做的工作拆分为三步,为了便于理解可以写成下面的形式,并分为三大模块分别进行说明。

构造方法

首先看一下AnnotationConfigApplicationContext的继承关系:

AnnotationConfigApplicationContext继承了GenericApplicationContext,那么我们先看GenericApplicationContext的构造方法:

public GenericApplicationContext() {
  this.beanFactory = new DefaultListableBeanFactory();
}

在这里初始化了一个beanFactory的实现类DefaultListableBeanFactory,这就是我们常提到的spring中重要的bean工厂,这里面存放了很多非常重要的数据结构。这里先列出比较重要的beanDefinitionMap,会在后面频繁使用:

private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
private volatile List<String> beanDefinitionNames = new ArrayList<>(256);

在上面的这个beanDefinitionMap中就维护了beanNameBeanDefinition的对应关系,beanDefinitionNames则是一个存放beanName的List。

AnnotationConfigApplicationContext的构造方法开始分析:

public AnnotationConfigApplicationContext() {
  this.reader = new AnnotatedBeanDefinitionReader(this);
  this.scanner = new ClassPathBeanDefinitionScanner(this);
}

首先实例化了一个AnnotatedBeanDefinitionReader对象,看一下AnnotatedBeanDefinitionReader的构造函数:

public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) {
  this(registry, getOrCreateEnvironment(registry));
}

那么,为什么在这能够将AnnotationConfigApplicationContext对象作为BeanDefinitionRegistry传入呢?

回头看一下继承关系那张图,AnnotationConfigApplicationContext继承了BeanDefinitionRegistry,并且最终实现了接口BeanFactoryBeanFactory可以说是Spring中的顶层类,它是一个工厂,能够产生bean对象,提供了一个非常重要的方法getBean,会在后面讲到。

到这,我们可以得出一个结论:

BeanDefinitionRegistry可以等同于AnnotationConfigApplicationContext ,看做spring的上下文环境。

AnnotatedBeanDefinitionReader在实例化时,会调用registerAnnotationConfigProcessors方法。先看前半段代码:

public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
    BeanDefinitionRegistry 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());
      }
}

在这里先获取在父类构造函数中实例好的beanFactory,并为它填充一些属性:

  • AnnotationAwareOrderComparator:主要用于排序,解析@order@Priority注解
  • ContextAnnotationAutowireCandidateResolver:提供处理延迟加载的功能

再看后半段代码,下面生成了6个重要类的BeanDefinitionHolder,并存放到一个Set中:

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

    if (!registry.containsBeanDefinition(REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
      RootBeanDefinition def = new RootBeanDefinition(RequiredAnnotationBeanPostProcessor.class);
      def.setSource(source);
      beanDefs.add(registerPostProcessor(registry, def, REQUIRED_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;
  }

这里是使用RootBeanDefinition来将普通类转换为BeanDefinition,并进一步封装成BeanDefinitionHolder。封装成BeanDefinitionHolder的操作在registerPostProcessor方法中:

 private static BeanDefinitionHolder registerPostProcessor(
      BeanDefinitionRegistry registry, RootBeanDefinition definition, String beanName) {
    definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    registry.registerBeanDefinition(beanName, definition);
    return new BeanDefinitionHolder(definition, beanName);
  }

通过registerBeanDefinition方法将BeanDefinition注册到spring环境中,这个操作其实就是执行了上面的beanDefinitionMapput操作:

this.beanDefinitionMap.put(beanName, beanDefinition);

在上面的操作全部完成后,在还没有实例化用户自定义的bean前,已经有了6个spring自己定义的beanDefinition

用于实现spring自身的初始化:

这里有必要对BeanDefinition进行一下说明,它是对具有属性值的bean实例的一个说明,或者说是定义。就像是在java类加载的过程,普通java文件要先生成字节码文件,再加载到jvm中生成class对象,spring初始化过程中首先要将普通类转化为BeanDefinition,然后再实例化为bean。

在实例化AnnotatedBeanDefinitionReader完成后,实例化了一个ClassPathBeanDefinitionScanner,可以用来扫描包或者类,并将扫描到的类转化为BeanDefinition。但是翻阅源码,我们可以看到实际上扫描包的工作不是这个scanner对象来完成的,而是在后面spring自己实例化了一个ClassPathBeanDefinitionScanner来负责的。

这里的scanner仅仅是对外提供一个扩展,可以让我们能够在外部调用AnnotationConfigApplicationContext对象的scan方法,实现包的扫描,

例如:

context.scan("com.hydra");

到这里,AnnotationConfigApplicationContext的构造函数就执行完了,下一篇,我们来详细说说接下来被调用的register方法。

到此这篇关于Spring源码解析容器初始化构造方法的文章就介绍到这了,更多相关Spring构造方法内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • spring容器启动实现初始化某个方法(init)

    spring容器启动 初始化某方法(init) 1.前言 很多时候,我们需要在项目启动的时候,就要完成某些方法的执行.今天整理了一个简单的方法,使用spring容器中bean的属性:init-method 2.代码 /* 初始化的类.这里不需要添加任何注解 */ public class InitData { @Autowired private UserService userService; /* 初始化方法 */ public void inits(){ System.out.printl

  • Spring容器初始化及问题解决方案

    1.Spring bean组件 "默认为单例模式scope="singleton, 运行JavaApplication容器启动时自动创建对象 scope="prototype"为多例模式,请求条件下才创建对象 2beans组件 里面default-init-method初始化方法加载,范围比较大,当没有此方法时不会报错,default-destroy-method销毁方法,default-lazy-init="true/false" 对象延时实例

  • 理解 MyBatis 是如何在 Spring 容器中初始化的

    MyBatis 初始化过程就是生成一些必须的对象放到 Spring 容器中.问题是这个过程到底生成了哪些对象?当遇到 MyBatis 初始化失败时,如何正确的找到分析问题的切入点?本文将针对这些问题进行介绍. 本文基于 MyBatis 3 和 Spring,假设读者已经知道如何使用 Maven 和 MyBatis,以及了解 Spring 的容器机制. 一.Mybatis 三件套 我们知道 MyBatis 的主要功能是由 SqlSessionFactory 和 Mapper 两者提供的,初始化 M

  • 浅谈spring容器中bean的初始化

    当我们在spring容器中添加一个bean时,如果没有指明它的scope属性,则默认是singleton,也就是单例的. 例如先声明一个bean: public class People { private String name; private String sex; public String getName() { return name; } public void setName(String name) { this.name = name; } public String get

  • spring容器初始化遇到的死锁问题解决

    前言 最近启动spring项目的时候遇到一个死锁问题,使用jstack获取线程堆栈的时候,可以看到2个线程出现了死锁: 解决过程: DefaultSingletonBeanRegistry.getSingleton()源码如下,可以看到这个方法需要对singletonObjects加锁 第二处xxx.subject.core.cache.DataLocalcacheInit.afterPropertiesSet源码如下: 可以看到:这个bean在初始化的时候,会开启线程,调用另外一个bean的i

  • 一个简单的Spring容器初始化流程详解

    前言 首先我们初始化一个最简单的容器,用这个容器研究初始化的流程. 下面就是一个再简单不过的IoC容器了,该容器包含了一个名为beanA的bean,我们初始化容器后,取出该Bean,并调用方法. public class BeanA { private String testStr = "Test"; public BeanA(){ System.out.println("Running A"); } public void sayHello(){ System.o

  • 使用spring容器在初始化Bean时前和后的操作

    目录 spring容器初始化Bean操作 @PostConstruct和@PreDestroy注解 在XML中定义init-method和destory-method方法 Bean实现InitializingBean和DisposableBean接口 Spring bean 初始化顺序 1.概述 2.InitializingBean vs init-method 3.@PostConstruct 4.小结一下吧 spring容器初始化Bean操作 在某些情况下,Spring容器在初始化Bean的

  • 浅谈Spring与SpringMVC父子容器的关系与初始化

    Spring和SpringMVC的容器具有父子关系,Spring容器为父容器,SpringMVC为子容器,子容器可以引用父容器中的Bean,而父容器不可以引用子容器中的Bean. 了解了Spring与SpringMVC父子容器的关系,接下来让我们看看Spring与SpringMVC容器的初始化过程. 以下讲解使用的web.xml文件如下: <context-param> <param-name>contextConfigLocation</param-name>//指定

  • SpringBoot 如何添加容器启动的初始化逻辑的操作方法

    背景 传统 SpringMVC 项目中,我们可以定义容器初始化 Servlet ,然后在 web.xml 配置该 Servlet ,指定 load-on-startup 就可以在容器启动后,执行一些系统的初始化逻辑.比如设置全局资源文件路径.加载系统定时任务.数据初始化等. 技术转换到 SpringBoot 的时候,该在哪里添加应用初始化的代码呢? 如果初始化逻辑中,有一些普通类,需要通过 Spring 的 ApplicationContext 的 getBean 获取依赖属性,如何保证 Spr

  • Spring源码解析容器初始化构造方法

    目录 前言 构造方法 前言 Spring框架被广泛应用于我们的日常工作中,但是很长时间以来我都是只会使用,不懂它的作用原理.通过最近一段时间的阅读源码,个人发现通过阅读源码,能够帮助我们了解Spring的设计理念,并且对Java编程中的一些设计模式更加熟悉,所以记录一下自己对Spring源码的理解. 在开始进行源码学习前,首先再回顾一下三种Spring编程风格: 基于Schema,即通过xml标签的配置方式 基于Annotation的注解技术,使用@Component等注解配置bean 基于Ja

  • Spring源码解析之推断构造方法

    Spring推断构造方法 贴个测试代码直接开干,这只是个样例,其他情况自行分析 @Component public class OrderService { public OrderService() { System.out.println("无参构造方法"); } @Autowired(required = false) public OrderService(UserService userService) { System.out.println("一个参数的构造方法

  • Spring源码解析之BeanPostProcessor知识总结

    一.简介 BeanPostProcessor是Spring IOC容器给我们提供的一个扩展接口. 实例化Bean做前置处理.后置处理 二.接口定义 @Component public class BeanPost implements BeanPostProcessor { /** * 在每个bean创建之后的初始化方法之前调用 * @param bean 当前实例化的bean * @param beanName bean的名称 * @return 返回实例化的bean或者可以对对象进行再封装返

  • Spring源码解析 Bean的实例化

    目录 前言 准备工作 实例化bean 1.AbstractBeanFactory 的 doGetBean方法 2.AbstractAutowireCapableBeanFactory 的 createBean方法 3.AbstractAutowireCapableBeanFactory 的 doCreateBean方法 4.AbstractAutowireCapableBeanFactory 的 createBeanInstance方法 5.AbstractAutowireCapableBean

  • Spring源码解析后置处理器梳理总结

    目录 前言 1.InstantiationAwareBeanPostProcessor的postProcessBeforeInstantiation()方法 2.SmartInstantiationAwareBeanPostProcessor的determineCandidateConstructors()方法 3.MergedBeanDefinitionPostProcessor的postProcessMergedBeanDefinition()方法 4.SmartInstantiationA

  • Spring源码解析之Configuration

    一.@Configuration 1.1 未加@Configuration <!--logback-test.xml,配置不打印日志--> <?xml version="1.0" encoding="UTF-8"?> <configuration> <include resource="org/springframework/boot/logging/logback/base.xml" /> <

  • Spring源码解析之Bean的生命周期

    一.Bean的实例化概述 前一篇分析了BeanDefinition的封装过程,最终将beanName与BeanDefinition以一对一映射关系放到beanDefinitionMap容器中,这一篇重点分析如何利用bean的定义信息BeanDefinition实例化bean. 二.流程概览 其实bean的实例化过程比较复杂,中间细节很多,为了抓住重点,先将核心流程梳理出来,主要包含以下几个流程: step1: 通过反射创建实例: step2:给实例属性赋初始值: step3:如果Bean类实现B

  • Spring源码分析容器启动流程

    目录 前言 源码解析 1.初始化流程 流程分析 核心代码剖析 2.刷新流程 流程分析 核心代码剖析 前言 本文基于 Spring 的 5.1.6.RELEASE 版本 Spring的启动流程可以归纳为三个步骤: 1.初始化Spring容器,注册内置的BeanPostProcessor的BeanDefinition到容器中 2.将配置类的BeanDefinition注册到容器中 3.调用refresh()方法刷新容器 Spring Framework 是 Java 语言中影响最为深远的框架之一,其

  • Spring源码解析之编程式事务

    一.前言 在Spring中,事务有两种实现方式: 编程式事务管理: 编程式事务管理使用TransactionTemplate可实现更细粒度的事务控制.声明式事务管理: 基于Spring AOP实现.其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务. 声明式事务管理不需要入侵代码,通过@Transactional就可以进行事务操作,更快捷而且简单(尤其是配合spring boot自动配置,可以说是精简至极!),且大部分业务都可

  • Spring源码解析之循环依赖的实现流程

    目录 前言 循环依赖实现流程 前言 上篇文章中我们分析完了Spring中Bean的实例化过程,但是没有对循环依赖的问题进行分析,这篇文章中我们来看一下spring是如何解决循环依赖的实现. 之前在讲spring的过程中,我们提到了一个spring的单例池singletonObjects,用于存放创建好的bean,也提到过这个Map也可以说是狭义上的spring容器. private final Map<String, Object> singletonObjects = new Concurr

随机推荐