Spring 源码解析——@Configuration 作用及其实现原理(一)

目录

一、概述

二、相关技术

2.1 @Configuration 作用

2.2 BeanDefinition 和 BeanDefinitionMap

2.3 CGLIB 简介

三、源码解析

3.1 概述

3.2 register(Bean 注册流程)

3.3 refresh(BeanClass 的替换)

四、内容总结


写文章不易,转载请标明出处。

同时,如果你喜欢我的文章,请关注我,让我们一起进步。

一、概述

在前面的几篇博文中已经对 Java JUC 包中的 AQS 类结合 ReentrantLock 类来进行了部分源码(主要是独占锁部分)分析,通过对源码的分析更好的了解了 ReentrantLock 内部的内部原理,并且对 AQS 的代码设计有了进一步的了解,因此决心将源码分析的路坚持走下去。

从这篇博文开始,会对 Spring 中的一些源码进行分析,比如 Spring 中的一些注解、Spring 的 IOC 原理、Spring 的 AOP 实现方式和 Spring MVC 的部分功能实现等等。在之前的一些项目中,我也曾比较多的使用 SSM 组合来进行开发,也使用过 SpringBoot 来开发一些小的项目,但是在开发中用到的很多技术都是知其然却不知所以然的状态,在使用的时候也总是一种模棱两可的状态,所以当进行过项目的实战选择进入到 Spring 的源码层面进行分析,一方面是更好的理解 Spring 的设计理念,另一方面是在阅读源码的过程中学习大师们的编程思想,在这个过程中进一步的提升自身的编程思维和代码设计水平。

同时,本着负责任的态度,我在编写每一篇技术博文之前都会阅读大量的资料,并且对代码尽量做到深入细节的思考,努力保证博文的质量和其准确性,但如果你在阅读的过程中发现可能存在问题的地方,可以评论在下方,我会及时的回复,有则改过无则加勉。因此,如果你也是跟我一样已经经历过使用 Spring 进行项目的实战开发,但是在开发结束后却又不知何去何从的话,那么你可以关注我,我们可以一起遨游在阅读源码快乐的海洋中,哈哈哈。废话不多说,下面直接进入到源码分析阶段。

为了便于大家理解,我的所有源码会尽量保留原英文注释。 

 

二、相关技术

2.1 @Configuration 作用

Indicates that a class declares one or more @Bean methods and may be processed by the Spring container to generate bean definitions and service requests for those beans at runtime。

@Configuration classes may not only be bootstrapped using component scanning, but may also themselves configure component scanning using the @ComponentScan annotation。 

https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/annotation/Configuration.html

上面的解释截取自官方文档,第一段大概的意思就是说通过使用 @Configuration 注解的类会将其中 @Bean 注解的方法所生成并返回的对象交由 Spring 容器来进行管理,而第二段的意思大概就是我们同样可以 @Configuration 和 @ComponentScan 组合的方式完成 Bean 的自动扫描,对于 @Configuration 的使用方法和其作用这里就不做过的解释,如果有兴趣可以直接去上面提供的官方文档中查看。

接下来这里我们要说的是,当我们使用 @Configuration 注解的时候,一般是将其所注解的类作为 Spring 的配置类,也就是对应于 xml 配置的一种方式,通常的使用方法是将其对应的类信息作为入参来实例化一个 AnnotationConfigApplicationContext 对象,从而完成 Spring 容器的初始化。但是你有没有测试过,假如当我们去掉 @Configuration 注解,再将这个类作为 AnnotationConfigApplicationContext 实例化方法的入参时,会有什么样的效果,下面我们就来测试一下。

首先在这里我们统一使用的测试代码如下,SpringConfig 类就是我们所说的那个配置类,我们在初始化容器后再从容器中将这个配置类所对应的 Bean 拿出来。

public static void main(String[] args) {
	AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
	System.out.println(context.getBean(SpringConfig.class));
}

下面是两次运行的结果(第一次为没有添加 @Configuration 注解,第二次添加了 @Configuration 注解)。

 通过上面两次运行结果的对比,我们发现了一个很有趣的现象,即当我们没有为配置类添加 @Configuration 注解,但把它扔进 AnnotationConfigApplicationContext 构造器中时代码并不会报错,并且这样启动之后我们还从容器中拿到了这个 Bean 对象,反而当我们为其添加 @Configuration 注解后再入参,启动会却会得到一个奇奇怪怪的 Bean 对象,这样看来是不是事情变得有趣起来了。

而对于这样对比结果,其实也正是从侧面显示出了 @Configuration 发挥作用的方式(换句话说也就是它的作用机理)。在这里我们可以先大概介绍一下添加了 @Configuration 注解后得到的那个 Bean 对象,首先从它的命名上我们可以看到一些端倪,如果仔细观察就会发现它的命名中包含了 CGLIB 这个关键词,而 CGLIB 正是一种 Spring 所支持的动态代理的方式(使用子类继承的方式实现动态代理)。

因此,我们接下来对于 @Configuration 的作用和内部实现原理的分析就有了一个比较明确的方向,同时我们可以在下面对源码分析的过程中继续思考一个问题,为什么在使用了 @Configuration 注解后,要对这个配置类进行代理,这样的作用和意义是什么?

2.2 BeanDefinition 和 BeanDefinitionMap

在开始阅读源码之前,我们准备一下预备知识 BeanDefinition 和 BeanDefinitionMap,下面我们就来大概谈一下什么是 BeanDefinition 和 BeanDefinitionMap 以及它们的作用是什么(这里只是先大概讲一下,这两个点要是扩展开来,可能就不是这一篇博文能搞定的了)。

(1)BeanDefinition

A BeanDefinition describes a bean instance, which has property values, constructor argument values, and further information supplied by concrete implementations.

https://docs.spring.io/spring/docs/2.5.x/javadoc-api/org/springframework/beans/factory/config/BeanDefinition.html

按照惯例,开始之前直接先抛出官方文档,通过官方文档的描述我们可以知道 BeanDefinition 通过属性值、构造函数参数值以及更多地具体实现类描述了一个bean实例。上面这种描述可能有点抽象,但首先我们需要明白它只是一个接口,其次它的主要作用就是用来描述一个具体的类,通过它的名字(Bean 声明)也可以猜到。

而对于 BeanDefinition 其实更多使用到的是它的一些子类,比如 GenericBeanDefinition(这个类是 AbstractBeanDefinition 的子类,也是最常用的一个 BeanDefinition 类,可供用户在后置处理器中对其进行操作,并允许动态定义父依赖关系,具有更高的灵活性)和 RootBeanDefinition 等等。但不管具体到哪些子类,它的作用都是描述一个具体的类。

如果这样你还是觉得很模糊的话,可以再具体一点,那就是每个 BeanDefinition 对象都对应描述了一个类,在这个对象的属性中会记录一些类的基本信息(类的类型信息)和该类所对应的位于 Spring 中 Bean 的一些属性(是否为懒加载等)。说白了它就是用来记录类的一些特征,并且在 Spring 当中并不是通过类来直接生成相应的对象的,而是通过 BeanDefinition 来生成的。

关于 BeanDefinition 先大概讲这么多,具体的更多细节后面我会写博文来专门分析,在这篇博文里你只要知道 BeanDefinition 对象就是用来记录类的特征信息,并且 Spring 是根据 BeanDefinition 来生成容器内的对象的。

(2)BeanDefinitionMap

从命名即可得其作用,BeanDefinitionMap 顾名思义就是用来存储 BeanDefinition 的一个 Map 结构,是 BeanFactory 中的一个属性,并可以算是 Spring 容器中至关重要的一个组件,因为大部分几乎所有的 Bean 所对应的 BeanDefinition 都会存储在这里。它的位置是在 DefaultListableBeanFactory 中,下面直接贴出它的代码。

	/** Map of bean definition objects, keyed by bean name. */
	private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);

跟上面的 BeanDefinition 一样,这个 BeanDefinitionMap 我们也不展开来讲,后面会专门写博文来分析,现在只需要知道它是位于 BeanFactory 当中,键为 Bean 名称,值为 Bean 所对应的 BeanDefinition,主要是用于存储 BeanDefinition 的就足够了。

2.3 CGLIB 简介

从上面的对比测试中我们我们看到了 CGLIB 这个关键词,意味着 Spring 在使用 @Configuration 时运用了 CGLIB 这个动态代理机制(准确来说是使用了 CGLIB 代码生成包)。我们经常说的代理机制其实可以理解为代理设计模式,即使用代理类来对我们自己的类进行代理,当外部代码想要访问我们自己的类时,将其访问的请求交由代理类来进行处理,从而实现代码功能的无侵入性扩展。

经常使用的代理一般分为动态代理和静态代理两种,对于静态代理就是在代码中手动创建代理类,即在编译前代理类就已经确定(在编译器层面实现),而动态代理则恰好相反,它不会在代码中显示的实现代理类,而是在编译过程中自动实现代理类的创建工作,因此相对于静态代理,动态代理具有更高的灵活性,并且使用动态代理的代码一般具备更加良好的可扩展性。

对于动态代理一般又存在两种实现方式,第一种就是 JDK 1.3 就引入的 JDK 动态代理,这种原生的 JDK 动态代理主要的原理是使用 Java 的反射机制,并要求被代理的类必须实现一个或多个接口,如果一个类没有实现任何接口,那么它是无法被代理的。另一种就是 CGLIB 动态代理,它的底层是直接通过使用一个小而快的字节码处理框架 ASM,来转换字节码并生成新的类,所有的代理工作是在编译的过程中动态织入,同时其不要求被代理的类必须实现接口(因为其原理主要是通过继承被代理类,创建其子类来实现对被代理类的代理工作),所以具备更高的灵活性,在 Spring 的 AOP 中就大量的用到了这种代理技术。

 

三、源码解析

3.1 概述

在介绍完必要的预备知识之后,从这里开始,我们进入到源码的分析阶段,我们的入手点就是通过调用有参构造器而实例化 AnnotationConfigApplicationContext 对象。

	/**
	 * Create a new AnnotationConfigApplicationContext, deriving bean definitions
	 * from the given annotated classes and automatically refreshing the context.
	 * @param annotatedClasses one or more annotated classes,
	 * e.g. {@link Configuration @Configuration} classes
	 */
	public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
		this();
		register(annotatedClasses);
		refresh();
	}

 首先按照注释,这个方法的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值