读源码,我们可以从第一行读起

本文涉及的产品
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
简介: 读源码,我们可以从第一行读起

在前面的文章中,我们已经完成了《Spring官网阅读》,有了上面的基础,那么源码的阅读也就不会太难了,从今天开始我们一步步走进Spring的源码。我们整个源码的解析将以下面这句代码为入口:

AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(Config.class);

下面就跟着我来看看,Spring第一行代码到底干了什么!

本系列文章对应Spring版本为5.x。

上面这行代码就是执行了AnnotationConfigApplicationContext中的一个构造函数,其代码如下:

public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
    // 1.调用了AnnotationConfigApplicationContext的空参构造方法
    this();
    // 2.将配置类注册到容器中
    register(annotatedClasses);
  // 3.刷新容器    
    refresh();
}

找到第一行代码


我们继续查看AnnotationConfigApplicationContext的空参构造

其代码如下:

public AnnotationConfigApplicationContext() {
    // 1.1 构造一个reader对象
    this.reader = new AnnotatedBeanDefinitionReader(this);
    // 1.2 构造一个scanner对象
    this.scanner = new ClassPathBeanDefinitionScanner(this);
}

它主要做了两件事


1.创建一个AnnotatedBeanDefinitionReader

2.创建一个ClassPathBeanDefinitionScanner


本文的重点就是分析第一行代码,也就是创建AnnotatedBeanDefinitionReader的过程。在分析代码前,我们首先应该搞懂一件事情,就是创建的这个AnnotatedBeanDefinitionReader到底是什么呢?


AnnotatedBeanDefinitionReader是什么?


这个类的主要作用就是注册BeanDefinition,与之相类似的功能的类还有一个就是ClassPathBeanDefinitionScanner。它们最大的不同在于AnnotatedBeanDefinitionReader支持注册单个的BeanDefinition,而ClassPathBeanDefinitionScanner会一次注册所有扫描到的BeanDefinition。关于ClassPathBeanDefinitionScanner本文不专门做过多解释,只要能看懂这篇文章ClassPathBeanDefinitionScanner自然就懂了。


如果对于BeanDefinition不了解的推荐先阅读:


Spring官网阅读(四)BeanDefinition(上)

Spring官网阅读(五)BeanDefinition(下)


AnnotatedBeanDefinitionReader源码解析

// 这里只保留了这个类中的部分代码,其余不重要我都没有放出来
public class AnnotatedBeanDefinitionReader {
    // 上面我们已经说了,这个reader对象主要的工作就是注册BeanDefinition,那么将BeanDefinition注册到哪里去呢?所以它内部就保存了一个BeanDefinition的注册表。对应的就是我们代码中的AnnotationConfigApplicationContext
  private final BeanDefinitionRegistry registry;
    // 见名知意,Bean名称的生成器,生成BeanName
  private BeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator();
    // 解析@Scope注解
  private ScopeMetadataResolver scopeMetadataResolver = new AnnotationScopeMetadataResolver();
    // 解析@Conditional注解
  private ConditionEvaluator conditionEvaluator;
    // 将解析指定的类成为BeanDefinition并注册到容器中
  public void registerBean(Class<?> annotatedClass, String name, Class<? extends Annotation>... qualifiers) {
    doRegisterBean(annotatedClass, null, name, qualifiers);
  }
    // 真正的执行注册的方法
    <T> void doRegisterBean(Class<T> annotatedClass, @Nullable Supplier<T> instanceSupplier, @Nullable String name,
                            @Nullable Class<? extends Annotation>[] qualifiers, BeanDefinitionCustomizer... definitionCustomizers) {
        // Spring在这里写死了,直接new了一个AnnotatedGenericBeanDefinition,也就是说通过reader对象注册的BeanDefinition都是AnnotatedGenericBeanDefinition。
        AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);
        // 调用conditionEvaluator的shouldSkip方法
        // 判断当前的这个bd是否需要被注册
        if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
            return;
        }
    // 在注册时可以提供一个instanceSupplier
        abd.setInstanceSupplier(instanceSupplier);
        // 解析@Scope注解,得到一个ScopeMetadata
        ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
        // 将@Scope注解中的信息保存到bd中
        abd.setScope(scopeMetadata.getScopeName());
        // 调用beanNameGenerator生成beanName
        // 所谓的注册bd就是指定将bd放入到容器中的一个beanDefinitionMap中
        // 其中的key就是beanName,value就是解析class后得到的bd
        String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));
        // 这句代码将进一步解析class上的注解信息,Spring在创建这个abd的信息时候就已经将当前的class放入其中了,所有这行代码主要做的就是通过class对象获取到上面的注解(包括@Lazy,@Primary,@DependsOn注解等等),然后将得到注解中对应的配置信息并放入到bd中的属性中
        AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
        // 下面的这段不用过多关注,为了文章的完整性我这里还是说说我的理解
        // 正常的容器启动阶段qualifiers肯定等于null
        // 我能想到的不为空的方法就是直接在外部调用了register方法并且出入了qualifiers参数
        // 就是说我们手动直接注册了一个类,但是我们没有在类上添加@Lazy,@Primary注解,但是我们又希望能将其标记为Primary为true/LazyInit为true,这个时候就手动传入Primary.class跟Lazy.class即可。
        if (qualifiers != null) {
            for (Class<? extends Annotation> qualifier : qualifiers) {
                if (Primary.class == qualifier) {
                    abd.setPrimary(true);
                }
                else if (Lazy.class == qualifier) {
                    abd.setLazyInit(true);
                }
                else {
                    abd.addQualifier(new AutowireCandidateQualifier(qualifier));
                }
            }
        }
        // 我们注册时,我们可以传入一些回调方法,在解析得到bd后调用
        for (BeanDefinitionCustomizer customizer : definitionCustomizers) {
            customizer.customize(abd);
        }
        // bd中是没有beanName属性的,BeanDefinitionHolder中就是保存了beanName以及对应的BeanDefinition
        BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
        // 这个地方主要是解析Scope中的ProxyMode属性,默认为no,不生成代理对象
    // 后文做详细分析
        definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
        // 注册bd到容器中,实际上最终就是将bd放到了beanFactory中的一个map里(beanDefinitionMap)
        // key为beanName,value为bd
        BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
    }
}

applyScopedProxyMode做了什么?


上面的代理有一个地方没有将清楚,就是AnnotationConfigUtils.applyScopedProxyMode到底做了什么?

带着这个问题,我们继续看看这个方法的代码


1、applyScopedProxyMode

// 这个方法名称直译过来就是----应用Scoped中的ProxyMode属性
// 这个属性有什么用呢?
// ProxyMode属性一共有下面几种取值
//  1.DEFAULT:默认值,默认情况下取no
//  2.NO:不开启代理
//  3.INTERFACES:使用jdk动态代理
//  4.TARGET_CLASS:使用cglib代理
// 假设我们有一个单例的对象A,其中有一个属性B,B的作用域是session的,这个时候容器在启动时创建A的过程中需要为A注入属性B,但是属性B的作用域为session,这种情况下注入必定会报错的
// 但是当我们将ProxyMode属性配置为INTERFACES/TARGET_CLASS时,它会暴露一个代理对象,ProxyMode可以配置代理对象的生成策略是使用jdk动态代理还是生成cglib动态代理,那么当我们在创建A时,会先注入一个B的代理对象而不是直接报错
static BeanDefinitionHolder applyScopedProxyMode(
    ScopeMetadata metadata, BeanDefinitionHolder definition, BeanDefinitionRegistry registry) {
  // 根据scopedProxyMode进行判断,如果是NO,直接返回原有的bd并添加到容器的bdMap中
    ScopedProxyMode scopedProxyMode = metadata.getScopedProxyMode();
    if (scopedProxyMode.equals(ScopedProxyMode.NO)) {
        return definition;
    }
    // 是否采用cglib代理
    boolean proxyTargetClass = scopedProxyMode.equals(ScopedProxyMode.TARGET_CLASS);
    // 调用ScopedProxyCreator的createScopedProxy,创建代理对象对应的bd
    return ScopedProxyCreator.createScopedProxy(definition, registry, proxyTargetClass);
}

2、ScopedProxyCreator.createScopedProxy

public static BeanDefinitionHolder createScopedProxy(
    BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry, boolean proxyTargetClass) {
  // 继续调用ScopedProxyUtils的方法
    return ScopedProxyUtils.createScopedProxy(definitionHolder, registry, proxyTargetClass);
}

3、ScopedProxyUtils.createScopedProxy

// 根据目标对象的bd生成代理对象的bd,并且会将目标对象的bd注册到容器中
public static BeanDefinitionHolder createScopedProxy(BeanDefinitionHolder definition,
                                                     BeanDefinitionRegistry registry, boolean proxyTargetClass) {
  // 目标对象的名称
    String originalBeanName = definition.getBeanName();
    // 目标对象的bd
    BeanDefinition targetDefinition = definition.getBeanDefinition();
    // 将来会将目标对象的bd注册到容器中,targetBeanName作为注册时的key
    // targetBeanName = "scopedTarget."+originalBeanName
    String targetBeanName = getTargetBeanName(originalBeanName);
  // 创建代理对象的bd,可以看到代理对象会是一个factoryBean的介绍请参考《》,关于factoryBean的介绍请参考我的Spring官网阅读系列第七篇,这里不做过多介绍
    RootBeanDefinition proxyDefinition = new RootBeanDefinition(ScopedProxyFactoryBean.class);
    // 代理对象所装饰的bd就是目标对象的bd
    // 拷贝了部分目标对象bd中的属性到代理对象的bd中
    proxyDefinition.setDecoratedDefinition(new BeanDefinitionHolder(targetDefinition, targetBeanName));
    proxyDefinition.setOriginatingBeanDefinition(targetDefinition);
    proxyDefinition.setSource(definition.getSource());
    proxyDefinition.setRole(targetDefinition.getRole());
    proxyDefinition.getPropertyValues().add("targetBeanName", targetBeanName);
    if (proxyTargetClass) {
        targetDefinition.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
        // ScopedProxyFactoryBean's "proxyTargetClass" default is TRUE, so we don't need to set it explicitly here.
    }
    else {
        proxyDefinition.getPropertyValues().add("proxyTargetClass", Boolean.FALSE);
    }
    // Copy autowire settings from original bean definition.
    proxyDefinition.setAutowireCandidate(targetDefinition.isAutowireCandidate());
    proxyDefinition.setPrimary(targetDefinition.isPrimary());
    if (targetDefinition instanceof AbstractBeanDefinition) {
        proxyDefinition.copyQualifiersFrom((AbstractBeanDefinition) targetDefinition);
    }
    // The target bean should be ignored in favor of the scoped proxy.
    targetDefinition.setAutowireCandidate(false);
    targetDefinition.setPrimary(false);
    // 这一步会将原始的bd注册到容器中,其中的key="scopedTarget."+originalBeanName
    registry.registerBeanDefinition(targetBeanName, targetDefinition);
  // 返回这个用于创建代理对象的bd
    return new BeanDefinitionHolder(proxyDefinition, originalBeanName, definition.getAliases());
}

从上面的代码可以看出,当我们选择在@Scope注解中配置了proxyMode属性时(INTERFACES/TARGET_CLASS),那么Spring会在注册bd时,在容器中注册一个代理的bd,这个bd是一个ScopedProxyFactoryBean类型的bd,并且没有特别指定它的作用域,所以它是单例的,并且这个FactoryBean返回就是对应的目标对象的代理对象。基于此,Spring就可以利用这个bd来完成在启动阶段对session/request域对象的注入。


创建AnnotatedBeanDefinitionReader的过程中做了什么?


现在我们已经知道了这个reader对象有什么用,那么接下来我们就一起分析下Spring在创建这个对象的过程中还做了什么,也就是reader对象在执行构造函数时还完成了什么事情


1、创建environment

// 第一步:调用了第一个构造函数,在这个过程中创建了environment,关于environment可以参考我的《Spring官网阅读(十一)ApplicationContext详细介绍(上)》
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) {
    // 这个environment实际上就是包含了系统环境变量以及JVM启动参数
    this(registry, getOrCreateEnvironment(registry));
}

2、对属性进行赋值,并注册容器需要的bd

// 第二步:进一步调用构造函数
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
  // 为read对象中的属性赋值
    this.registry = registry;
    this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
    // 从这个方法可以看出,Spring在创建reader对象的时候就开始注册bd了,那么Spring注册了哪些bd呢?注册的bd有什么用呢?我们接着往下看
    AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}

3、注册容器需要的bd

public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
    BeanDefinitionRegistry registry, @Nullable Object source) {
    DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);
    if (beanFactory != null) {
        // 1.指定容器使用的比较器,通过这个比较器能够解析@Order注解以及Ordered接口
        if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) {
            beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
        }
        // 2.指定容器使用的AutowireCandidateResolver
        if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) {
            beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
        }
    }
    Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8);
  // 3.注册ConfigurationClassPostProcessor
    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));
    }
  // 4.注册AutowiredAnnotationBeanPostProcessor
    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));
    }
    // 5.如果支持JSR-250,注册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));
    }
    // 6.如果支持jpa,注册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));
    }
    // 7.注册EventListenerMethodProcessor,用于处理@EventListener注解
    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));
    }
    // 8.注册DefaultEventListenerFactory,用于将@EventListener所标注的方法转换成EventListener对象
    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;
}

上面这段代码,主要做了8件事,接下来我们就详细看看


1.指定容器使用的比较器,通过这个比较器能够解析@Order注解以及Ordered接口

指定容器中使用的比较器为AnnotationAwareOrderComparator,这个比较器继承了OrderComparator。

OrderComparator自身提供了对Ordered接口的处理功能,而AnnotationAwareOrderComparator进一步做了扩展,除了会处理Ordered接口外还会处理@Order注解,但是接口的优先级高于注解。


2.指定容器使用的AutowireCandidateResolver

参考我之前的问题:Spring中的AutowireCandidateResolver


这个类主要是在依赖注入期间起作用

  • 判断一个bean的类型是否符合依赖的条件
  • 处理@Vlaue注解
  • 处理@Qualifier注解
  • 处理@Lazy注解为延迟注入的依赖生成代理


3.注册ConfigurationClassPostProcessor

这个类是Spring启动过程中非常重要的一个后置处理器,它完成了对配置类的解析,并根据配置类上的配置信息去进行扫描并将扫描得到的bd注册到容器中,之后又会去解析注册到容器中的bd(因为在前一次扫描中可能会注册新的配置类),重复上面的操作确保所有的配置类都被解析处理完成。

这个类的源码之后单独用一篇文章分析,本文就不过多介绍了。

只要记住一点,Spring在构建Reader对象时就已经将这个后置处理器添加到容器中了,这个后置处理器属于**“开天辟地”**的那一类Bean


4.注册AutowiredAnnotationBeanPostProcessor

这个后置处理器主要工作于依赖注入阶段,专门用于处理@Autowired注解


5.如果支持JSR-250,注册CommonAnnotationBeanPostProcessor

这个后置处理器也是工作于依赖注入阶段,专门用于处理@Inject注解


6.如果支持jpa,注册PersistenceAnnotationBeanPostProcessor

提供了JPA的支持,不是重点,了解即可


7.注册EventListenerMethodProcessor,用于处理@EventListener注解

在Spring完成Bean的创建后,会调用这个处理器的afterSingletonsInstantiated,在这个方法中会完成@EventListener注解的解析,将所有的@EventListener注解标注的方法通过后面注册的DefaultEventListenerFactory转换成为ApplicationListener并注册到容器中


8.注册DefaultEventListenerFactory

将@EventListener标注的方法转换成ApplicationListener


至此,整个AnnotatedBeanDefinitionReader创建完成。


总结


本文作为Spring源码阅读系列的第一篇,主要介绍了整个AnnotatedBeanDefinitionReader的创建过程。我们从AnnotatedBeanDefinitionReader是什么开始,详细介绍了Spring的第一行代码到底干了什么。


可以看到,Spring在创建AnnotatedBeanDefinitionReader的过程中,还往容器中添加了开天辟地用的几个后置处理器,关于这几个后置处理器我将会在后面的文章中做详细的介绍。


源码的学习过程是枯燥的,这一系列的文章的目的一方面是总结自己在阅读Spring的源码过程中一些心得,朝着精通Spring的方向发展,一方面也希望能帮助到那些跟我一样正在学习Spring的同学可以减少一些源码阅读过程中的困扰。


相关文章
|
存储 缓存 C语言
【C语言进阶】文件的顺序读写、随机读写、文本文件和二进制文件、文件读取结束的判定以及文件缓冲区相关知识(下)
【C语言进阶】文件的顺序读写、随机读写、文本文件和二进制文件、文件读取结束的判定以及文件缓冲区相关知识(下)
|
机器学习/深度学习 存储 C语言
二进制优化的快读模板,以及常用的读入形式。
二进制优化的快读模板,以及常用的读入形式。
66 0
210字,一个例子,讲通事务?
210字,一个例子,讲通事务?
|
存储 C语言
【C语言进阶】文件的顺序读写、随机读写、文本文件和二进制文件、文件读取结束的判定以及文件缓冲区相关知识(中)
【C语言进阶】文件的顺序读写、随机读写、文本文件和二进制文件、文件读取结束的判定以及文件缓冲区相关知识(中)
|
编译器 数据库 C语言
【C语言进阶】文件的顺序读写、随机读写、文本文件和二进制文件、文件读取结束的判定以及文件缓冲区相关知识(上)
【C语言进阶】文件的顺序读写、随机读写、文本文件和二进制文件、文件读取结束的判定以及文件缓冲区相关知识(上)
|
Python
python如何读取xlsx文件,并指定读取某一列,逐个逐个取出指定列中的值
要读取xlsx文件并指定读取某一列,可以使用Python中的pandas库。以下是一个读取xlsx文件并逐个逐个取出指定列中的值的示例代码:
1406 0
编写一个程序,如果名为Exercise12_15.txt的文件不存在,则创建该文件。使用文本I/O将随机产生的100个整数写入文件,文件中的整数由空格分开。从文件中读回数据并以升序显示数据。
编写一个程序,如果名为Exercise12_15.txt的文件不存在,则创建该文件。使用文本I/O将随机产生的100个整数写入文件,文件中的整数由空格分开。从文件中读回数据并以升序显示数据。
294 0
|
算法
保存不带循环的表
保存不带循环的表
94 0
输入一个整形数(最多可以到亿位),然后按汉语的习惯,将其读出来并输出。如1052,读作:一千零五十二。 程序运行示例: 1052 一千零五十二
输入一个整形数(最多可以到亿位),然后按汉语的习惯,将其读出来并输出。如1052,读作:一千零五十二。 程序运行示例: 1052 一千零五十二
186 0
|
PHP
PHP面试题:请写一段程序,在服务器创建一个文件fruit.dat,将试题3中得到的数组写入到改文件中,然后写一段程序从文件中读取并还原数组@author zhuwenqiong
PHP面试题:请写一段程序,在服务器创建一个文件fruit.dat,将试题3中得到的数组写入到改文件中,然后写一段程序从文件中读取并还原数组@author zhuwenqiong
133 0