JAVA 开发微指南

读完需要

18

分钟

速读仅需 6 分钟

/ 前言 /

本文旨在为读者解析 Spring 源码中的关键类,以便读者在深入阅读源码时,能够了解关键类的作用和用途。在阅读 Spring 源码时,经常会遇到一些不熟悉的概念,了解关键类的作用可以帮助读者更好地理解这些概念。

/ BeanDefinition /

BeanDefinition 是 Spring 框架中的一个重要概念,它定义了一个 Bean 的基本属性和行为,比如:

  1. BeanClassName,当前的 bean 名字

  2. Scope,是否单例,具体枚举:#SCOPE_SINGLETON、#SCOPE_PROTOTYPE

  3. LazyInit,是否懒加载,默认不是

  4. DependsOn,是否依赖其他 bean,如果依赖,则会先创建依赖 bean

  5. InitMethodName,初始化方法名称

  6. DestroyMethodName,销毁类方法名称

  7. ......还有更多,但是这几个大体已经差不多了

BeanDefinition 的作用非常重要,它可以帮助 Spring 容器更好地管理 Bean 的生命周期和依赖关系。在 Spring 框架中,我们经常会通过注解方式来定义 Bean:

  1. < bean/>

  2. @Bean

  3. @Component(@Controller、@Service)

这些都是被称为申明式定义 Bean。就是使用 Spring 提供好的封装。

除了注解方式,我们还可以通过编程方式来定义 Bean,这时就需要直接使用 BeanDefinition 来创建 BeanDefinition 对象,并设置对应的属性,然后将其注册到 Spring 容器中,比如

    // 创建一个Spring容器        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition(); beanDefinition.setBeanClass(UserService.class); //当然还可以设置其他上面我说的其他属性:懒加载什么的 applicationContext.registerBeanDefinition("userService", beanDefinition);
UserService userService = (UserService) applicationContext.getBean("userService"); userService.test();


无论是通过注解方式还是编程方式来定义 Bean,最终都是需要使用 BeanDefinition 来描述 Bean 的基本属性和行为,然后将其放入 Spring 容器中进行管理。

/ BeanDefinitionReader /

BeanDefinitionReader 是 Spring 框架中的一个重要组件,主要用于读取和操作 BeanDefinition 对象。虽然我们在使用 Spring 框架时很少直接使用 BeanDefinitionReader,但在 Spring 源码中却扮演着非常重要的角色,相当于 Spring 源码的基础设施。

BeanDefinitionReader 的核心方法包括以下几个:

  1. BeanDefinitionRegistry,用来注册 bean 定义,相当于一个工厂

  2. BeanNameGenerator,用来生成 bean 名字的生成器

  3. loadBeanDefinitions,从资源中加载 bean

1


   

XmlBeanDefinitionReader

XmlBeanDefinitionReader 是 BeanDefinitionReader 的子类,可以用于从 XML 文件中读取 BeanDefinition 并注册到 Spring 容器中。使用 XmlBeanDefinitionReader 的步骤如下:

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(context);//加载xml中配置的所有<bean>int i = xmlBeanDefinitionReader.loadBeanDefinitions("spring.xml");System.out.println(context.getBean("userService"))


2


   

AnnotatedBeanDefinitionReader

细心的朋友,应该可以发现 AnnotatedBeanDefinitionReader 是一个单独的类,不是 BeanDefinitionReader 的子类,但它的方法与 BeanDefinitionReader 基本相同,官方说是方便的适配器,用于编程注册 bean 类,他可以解析@Conditional,@Scope、@Lazy、@Primary、@DependsOn、@Role、@Description 相关注解,具体操作如下:

// 创建一个Spring容器        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
// AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();// beanDefinition.setBeanClass(UserService.class);// applicationContext.registerBeanDefinition("userService", beanDefinition);
new AnnotatedBeanDefinitionReader(applicationContext).registerBean(UserService.class);
UserService userService = (UserService) applicationContext.getBean("userService"); userService.test();


同样的,他也可以让我们注册的 bean 走完创建的整个生命周期过程。

3


   

ClassPathBeanDefinitionScanner

ClassPathBeanDefinitionScanner 也是一个用于注册 BeanDefinition 的工具类,与 BeanDefinition 接口没有直接关系。ClassPathBeanDefinitionScanner 可以扫描指定包路径下带有特定注解的类,并将其解析成 BeanDefinition,注册到 Spring 容器中。主要是他有个 scan 方法对我们定义的 basepackage 包路径进行解析扫描所有带有@component、@ManagedBean(JSR-250 标准)、@Named(JSR-330 标准)使用 ClassPathBeanDefinitionScanner 的步骤如下:

// 创建一个Spring容器  AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);    new ClassPathBeanDefinitionScanner(applicationContext).scan("com.xiaoyu");  UserService userService = (UserService) applicationContext.getBean("userService");  userService.test();


/ BeanFactory /

BeanFactory 是 Spring 框架中的一个重要接口,他就是 Spring 用于管理 Bean 对象的创建和管理,看他的几个主要方法就知道了:

  1. getBean,可以根据 name、type 等获取 bean 对象

  2. containsBean,是否 bean 工厂中有某个对象

  3. isSingleton,判断是否是单例

  4. isTypeMatch,判断改 name 是否匹配类型

  5. getType,根据 bean 名字获取类型

  6. getAliases。获取别名数组看着主要几个接口实现,基本都是围绕 bean 所做的,然后根据接口再看他的实现类就方便许多了,

4


   

DefaultListableBeanFactory

如果看过源码的朋友肯定对这个实现类不陌生,如果对这个实现类陌生的朋友,那请记住这个重要的实现类,它实现了很多接口、且继承了多层父类,所以他的功能也是相当之多。我们来看看他的主要方法:

  1. containsBeanDefinition,查看是否包含某个 bean 定义,因为该类维护了一个 Map<String, BeanDefinition> beanDefinitionMap 属性。

  2. determineAutowireCandidate,决定注入哪个 bean,@Primary-->优先级最高--->name

  3. doResolveDependency,解析依赖,进行注入

  4. registerBeanDefinition,注册 bean 定义到 beanDefinitionMap 属性

  5. preInstantiateSingletons,进行创建 bean 实例

具体使用操作也是基本类似的

AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();  beanDefinition.setBeanClass(UserService.class);  defaultListableBeanFactory.registerBeanDefinition("userService",beanDefinition);    UserService userService1 = (UserService) defaultListableBeanFactory.getBean("userService");  userService1.test();

从他的结构图也能看出来:

5


   

AbstractBeanFactory

该类是抽象 bean,介绍他主要目的就是 getbean 时,走的主要逻辑就是该类实现的 dogetbean 方法(请记住这个重要的方法),所以确实需要关注下,主要方法如下:

  1. doGetBean,获取 bean 的主要逻辑,没有则创建

  2. getMergedBeanDefinition,bean 定义的合并逻辑,主要是将父类 beanfactory 中的属性被子类覆盖

6


   

AbstractAutowireCapableBeanFactory

继承自刚才提到的 AbstractBeanFactory,主要方法如下:

  1. autowireByName,按照 name 注入

  2. autowireByType,根据类型

  3. createBean,创建 bean 流程,实例化前可以使用 BeanPostProcessors 后置处理器

  4. createBeanInstance,正在创建 bean,这边使用到了之前入门讲过的推断构造器实现实例化

  5. doCreateBean,创建 bean,循环依赖、属性填充、初始化

  6. initializeBean,初始化 bean,包括初始化前、初始化、初始化后

  7. instantiateUsingFactoryMethod,利用 factorymethod 初始化 bean

  8. invokeAwareMethods,初始化 bean 时的回调函数 Aware 接口

  9. populateBean,初始化之前,属性赋值

可以从他的关键方法看出,主要作用就是初始化 bean 的全过程,也是很重要的类

7


   

HierarchicalBeanFactory

这里说下 HierarchicalBeanFactory 类,他只是一个接口类,但是如果想要使用 beanfactory 的层次结构,例如获取父 beanfactory,那就必须实现 HierarchicalBeanFactory 类,比如前面说的 bean 定义的合并逻辑,就需要获取父 beanfactory,从而实现父子 bean 定义的覆盖合并

/ ApplicationContext /

ApplicationContext 是个接口,实际上也是一个 BeanFactory,不过比 BeanFactory 更加强大,它本身并没有太多方法,但是它继承了很多接口,因为接口之间是可以多继承的。

关于他的父接口,这里不做多说明,详情的话请看下子文章(后续更新)。

8


   

AnnotationConfigApplicationContext

一看这个类,大家都知道了,我们用的实例全是用这个类去启动我们的 spring 的,我们看看他的主要方法:

  1. AnnotationConfigApplicationContext,构造器,会初始化 DefaultListableBeanFactory、AnnotatedBeanDefinitionReader、ClassPathBeanDefinitionScanner;然后开始调用 refresh()方法。

  2. register,会使用编程式定义将 bean 注入 spring 容器。就如我们的 APPConfig

  3. scan,走 ClassPathBeanDefinitionScanner 的 scan,扫描包路径,将声明式的 bean 注入进 spring 容器

  4. setBeanNameGenerator,bean 名称生成器

9


   

ClassPathXmlApplicationContext

主要就是去解析 xml 配置的 bean 定义将其注入到 spring 容器中,功能其实跟 AnnotationConfigApplicationContext 类似,但是却没有 AnnotationConfigApplicationContext 强大,比如不能注册 BeanDefinition。

/ BeanPostProcessor /

BeanPostProcess 表示 Bena 的后置处理器,可以有多个 BeanPostProcessor,我们自己也可以去定义一个 BeanPostProcessor;

@Componentpublic class MyBeanPostProcessor implements BeanPostProcessor {
@Override public Object postProcessBeforeInitialization(Object bean, String beanName) { if ("userService".equals(beanName)) { System.out.println("userService"); return new User(); } System.out.println("MyBeanPostProcessor.postProcessBeforeInitialization"); return bean; }
@Override public Object postProcessAfterInitialization(Object bean, String beanName) { System.out.println("MyBeanPostProcessor.postProcessAfterInitialization"); return bean; }


我们可以通过实现 bean 的后置处理器,来对某一个 bean 或者所有 bean 的进行干预,博主只是随便写了一个,没有什么太大意义。

10


   

BeanFactoryPostProcessor

BeanFactoryPostProcessor 表示 Bean 工厂的后置处理器,其实和 BeanPostProcessor 类似,BeanPostProcessor 是干涉 Bean 的创建过程,BeanFactoryPostProcessor 是干涉 BeanFactory 的创建过程,我们也可以自定义:

@Componentpublic class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { System.out.println("加工beanFactory"); }}


/ FactoryBean /

FactoryBean 和 BeanFactory 不是一个东西,大家不要混淆两个概念,BeanFactory 是管理我们注入的 bean 等,而 FactoryBean 本身也会被 Spring 管理,一旦 Spring 知道我们的 bean 实现了 FactoryBean,那么会自动调用 getObject 方法获取我们自己创建的 bean,这个 bean 完完全全交给我们自己创建了,我们可以这样定义一个 FactoryBean:


@Componentpublic class MyFactoryBean implements FactoryBean {
@Override public Object getObject() throws Exception { UserService service = new UserService(); return service; }
@Override public Class<?> getObjectType() { return UserService.class; }
}


但是需要注意的是,这些注入 UserService 时,是不会有属性依赖注入的,毕竟他没有走 bean 的生命创建周期。细心的朋友会发现,这根我在配置类中写@bean 形式的类有啥区别,现象来讲,他俩都可以被创建出来,但是值得一提的是,FactoryBean 创建出来的 bean 是没走 spring 定义的 bean 生命周期的。

/ MetadataReader、ClassMetadata、AnnotationMetadata /

Spring 启动时需要扫描指定包路径下的所有类文件来获取需要注入或管理的 Bean 信息。然而,并非所有类都是需要的,这时可以使用 ASM 技术来解析类文件的元数据信息,包括类上的注解信息和类的基本信息。ASM 技术可以在运行时动态生成和修改 Java 字节码,从而高效地解析类文件的元数据信息,避免了大量的 IO 操作和类加载,提高了应用的性能。以下是一个简单的实例:

   SimpleMetadataReaderFactory simpleMetadataReaderFactory = new SimpleMetadataReaderFactory();   MetadataReader metadataReader = simpleMetadataReaderFactory.getMetadataReader("com.xiaoyu.service.UserService");   System.out.println(metadataReader.getClassMetadata().getClassName());   metadataReader.getAnnotationMetadata().getAnnotationTypes().forEach(System.out::println);


/ 结语 /

通过本文的解析,我们大致了解了 Spring 框架中的一些关键组件及其用途,这有助于我们在深入理解 Spring 源码过程中建立起一个整体框架。Spring 源码量很大,要真正理解透彻还需要投入大量时间进行细致学习和总结。但如果先对一些关键组件有一个大致的认识,有助于我们进行针对性学习,避免迷失在繁杂的细节中。希望本文能够对读者有一定的帮助,更希望读者在学习 Spring 源码的过程中,不断总结和提高,并在一定阶段有所突破。祝读者顺利!

本篇文章来源于微信公众号: JAVA 开发微指南



微信扫描下方的二维码阅读本文

此作者没有提供个人介绍
最后更新于 2023-05-28