MENU

Spring源码解析(一)入门

November 3, 2020 • Read: 3076 • 后端

前言:一入spring深似海,呃,后面不知道怎么写了,那就开干吧。
这篇是spring系列的第一篇文章,希望自己能坚持下去,愿阅读源码的各位都一同坚持下去。与君共勉!

0. 准备工作

首先应该准备相关的环境,我的环境是:

  1. IntelliJ IDEA 2020.2 (Ultimate Edition)
  2. JDK 1.8.*
  3. gradle-5.6.4
  4. spring版本:spring-framework-5.1.17.RELEASE

可以使用我的环境,然后参考后面的步骤时,只需要修改对应的版本,当然你也可以使用这篇的文章的环境对应的版本。如果有不对的地方,欢迎留言,我会尽量提供帮助的。

具体的步骤可以参考这篇文章 gradle+idea配置阅读Spring源码开发环境(解决jar包下载缓慢问题),强烈推荐,良心博主,不做重复工作(不,其实是懒!)。但是好像有一个错误,就是设置gradle环境的地方,具体操作,参看下图。

image-20200824124226441.png

1. 源码阅读

源码分析的部分主要是用JavaConfig技术启动spring环境,这也是spring官网推荐的方式。本系列文章是假设读者已经掌握了spring基本入门的应用。如未掌握,可以点击链接Java-based Container Configuration,前往官网学习入门,或者百度相关知识入门

1.1 编写基本代码

AppConfig.Java

首先写一个配置类

@Configuration
@ComponentScan("com.example")
public class AppConfig {
}

Test.Java

编写一个测试类

public class Test {
    public static void main(String[] args) throws IOException {
        // 把spring所有的前提环境准备好(比如:bean容器、bean工厂等)
        AnnotationConfigApplicationContext context = new
                AnnotationConfigApplicationContext();    
        context.register(AppConfig.class);
        context.refresh();
        AppConfig bean = context.getBean(AppConfig.class);
        System.out.println(bean);            
    }
}

如果成功打印出来,说明spring环境已经搭建成功,现在可以开心阅读源码了。

1.2 AnnotationConfigApplicationContext类

// 继承了GenericApplicationContext类
public class AnnotationConfigApplicationContext extends GenericApplicationContext implements AnnotationConfigRegistry

首先在上面的main方法中,调用了AnnotationConfigApplicationContext类的无参构造方法,在其实例化之前,会调用父类 GenericApplicationContext类的无参构造,其构造方法如下:

public GenericApplicationContext() {
    // 实例化了一个spring默认提供的BeanFactory ==> DefaultListableBeanFactory类
    // 保存到了成员变量 beanFactory中
    this.beanFactory = new DefaultListableBeanFactory();
}

然后再调用本类的构造方法,如下:

public AnnotationConfigApplicationContext() {
    // 创建一个读取注解的Bean定义读取器。Bean定义:BeanDefinition
    // 这个中完成了
    this.reader = new AnnotatedBeanDefinitionReader(this);
    /*
     * 可以用来扫描包或类,然后转换成bd
     * 但是实际上我们扫描包工作不是scanner对象
    */
    // 扫描器,这是仅仅是为了程序员能够在外部地调用AnnotationConfigApplicationContext对象的scan方法
    this.scanner = new ClassPathBeanDefinitionScanner(this);
}

下面主要介绍实例化 AnnotatedBeanDefinitionReader、ClassPathBeanDefinitionScanner两个类所做的事情

1.2.1 AnnotatedBeanDefinitionReader类

在上面实例化AnnotatedBeanDefinitionReader类

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

调用到,下面这个构造方法:

public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
    // .....省略
    // 梦开始的地方,嘿嘿
    // 非常重要的方法
    AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}

registerAnnotationConfigProcessors()方法

首先调用这个方法,一个空壳方法:

public static void registerAnnotationConfigProcessors(BeanDefinitionRegistry registry) {
    registerAnnotationConfigProcessors(registry, null);
}

真正的内容方法:

这里首先介绍一下BeanDefinition

BeanDefinition是什么,顾名思义,它是用来描述Bean的,里面存放着关于Bean的一系列信息,比如Bean的作用域,Bean所对应的Class,是否懒加载,是否Primary等等,这个BeanDefinition也相当重要,我们以后会常常和它打交道。如需详细的了解,可以点击spring官网的链接查看。(后面可能用bd来代表BeanDefinition)

这个方法注册了6个关键的bd(也是6个后置处理器)到beanDefinitionMap中,在后面会被使用,这个方法中的RootBeanDefinition是实现了 BeanDefinition的,其代表spring内部的BeanDefinition。

这里注册了一个在初始化 beanFactory环境重要的一个类:ConfigurationClassPostProcessor,这个类后在后面着重讲解。

public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
    BeanDefinitionRegistry registry, @Nullable Object source) {

    // 这里返回的是 在GenericApplicationContext类中的构造方法创建的一个 beanFactory
    DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);
    if (beanFactory != null) {
        if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) {
            // AnnotationAwareOrderComparator主要能解析@Order注解和@Priority。设置实例
            beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
        }
        if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) {
            // ContextAnnotationAutowireCandidateResolver提供处理延迟加载的功能
            beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
        }
    }

    Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8);
    // BeanDefinition的注册。这里注册了6个关键的bd
    // 这个是最核心的类 ConfigurationClassPostProcessor,这个类将会在后面重点介绍
    if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
        /*
             * ConfigurationClassPostProcessor 的类型是BeanDefinitionRegistryPostProcessor
             * 而BeanDefinitionRegistryPostProcessor 最终实现BeanFactoryPostProcessor
             * 可以把RootBeanDefinition理解成spring内部的bean定义,通过BeanDefinition实现,可以将一个类变成bean定义
             *
             *  为什么在这里注册这个ConfigurationClassPostProcessor?
             *  因为在spring的beanFactory初始化需要做一些事情,在执行invokeBeanFactoryPostProcessors()时
             * 委托调用其内部实现
             *  以此来实现解耦合。
             *
             */
        RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
        def.setSource(source);
        // 注册成一个bean定义 beanDefinitionMap
        // registerPostProcessor()方法,最终会把这个类放到beanFactory的 beanDefinitionMap中
        beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
    }
    //.....后面的省略
}       

registerPostProcessor()方法

private static BeanDefinitionHolder registerPostProcessor(
    BeanDefinitionRegistry registry, RootBeanDefinition definition, String beanName) {

    definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    // 注册成一个bean定义。这里的registry 是 我们开始实例的 DefaultListableBeanFactory
    registry.registerBeanDefinition(beanName, definition);
    return new BeanDefinitionHolder(definition, beanName);
}

registerBeanDefinition()方法

主要是将beanDefinition、beanName分别放入到Map、List中,这两个容器也是beanFactory工厂中非常重要的两个属性,将在后面逐步展示其所起的作用。

// ....省略
else {
    // Still in startup registration phase
    // Map
    this.beanDefinitionMap.put(beanName, beanDefinition);
    // List
    this.beanDefinitionNames.add(beanName);
    removeManualSingletonName(beanName);
}
// ... 省略
/** Map of bean definition objects, keyed by bean name. */
// bean的定义
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
/** List of bean definition names, in registration order. */
// beanName
private volatile List<String> beanDefinitionNames = new ArrayList<>(256);

1.2.2 ClassPathBeanDefinitionScanner类

这里并不介绍其内部结构,主要介绍其用途,因为一般很少被使用。

image-20201103233430902.png

正如上图注释所说那样,这里的scanner主要是作为调用者在外部调用,例如:

public class Test {
   public static void main(String[] args) throws IOException {
      // 把spring所有的前提环境准备好(比如:bean容器、bean工厂等)
      AnnotationConfigApplicationContext context = new
            AnnotationConfigApplicationContext();
      context.register(AppConfig.class);
       // 自己想要额外的扫描一些包,并不想要spring来扫描,可以调用该方法
      context.scan("com.example");
      context.refresh();
}

主要的使用地方

// AnnotationConfigApplicationContext类内部
public void scan(String... basePackages) {
    Assert.notEmpty(basePackages, "At least one base package must be specified");
    this.scanner.scan(basePackages);
}

到这里就介绍完了实例化AnnotationConfigApplicationContext类所做的事情了,Test类中剩下的方法将放在后面逐步更新,希望自己能当个秒男一样更新吧(雾

2 参考链接

gradle+idea配置阅读Spring源码开发环境(解决jar包下载缓慢问题
剑指Spring源码(一)

Last Modified: November 8, 2020
Leave a Comment

已有 1 条评论
  1. wrhn wrhn     Windows 7 /    Google Chrome

    免费单号购买 不降权单号www.uudanhaowang.com