剑指Spring源码(一)

栏目: Java · 发布时间: 5年前

内容简介:Spring,相信每个Java开发都用过,而且是每天都在用,那强大又神秘的IoC,AOP,让我们的开发变得越来越简单,只需要一个注解搞定一切,但是它内部到底是什么样子的呢?跟着我,一起探究Spring源码把。写在前面的话:Spring项目距今已有15年左右的历史了,是众多Java大神们的杰作,由于我个人水平有限,时间有限,不保证我说的全部都是正确的,但是我可以保证每一句话都是反复推敲,经过验证,绝没有复制粘贴。当然在这里,也不可能把每个方法都进行深层次的分析,只能把重点集中在重要的方法上,有些(其实是绝大部

Spring,相信每个 Java 开发都用过,而且是每天都在用,那强大又神秘的IoC,AOP,让我们的开发变得越来越简单,只需要一个注解搞定一切,但是它内部到底是什么样子的呢?跟着我,一起探究Spring源码把。

写在前面的话:Spring项目距今已有15年左右的历史了,是众多Java大神们的杰作,由于我个人水平有限,时间有限,不保证我说的全部都是正确的,但是我可以保证每一句话都是反复推敲,经过验证,绝没有复制粘贴。当然在这里,也不可能把每个方法都进行深层次的分析,只能把重点集中在重要的方法上,有些(其实是绝大部分)只能采取黑盒理论,即:不去探究方法内部到底做了什么,只大概的知道执行这个方法后发生了什么。

本文中采用的Spring版本是5.0.0

由于现在JavaConfig风格+注解的方式来使用Spring,是Spring官方主推的,也是现在的主流方式,所以我们从这里出发:

AnnotationConfigApplicationContext context=new AnnotationConfigApplicationContext(AppConfig.class);
复制代码

让我们先来看看AnnotationConfigApplicationContext的关系图:

剑指Spring源码(一)

可以看到这个关系够复杂的,我们现在完全不需要特意全部记住,只要有一个大概的印象就可以了,后面随着源码分析的深入,自然而然会记住其中的一些关系。

创建AnnotationConfigApplicationContext对象,首先会跑到这里:

//根据参数类型可以知道,其实可以传入多个annotatedClasses,但是这种情况出现的比较少
	public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
		//调用无参构造函数,会先调用父类GenericApplicationContext的构造函数
		//父类的构造函数里面就是初始化DefaultListableBeanFactory,并且赋值给beanFactory
		//本类的构造函数里面,初始化了一个读取器:AnnotatedBeanDefinitionReader read,一个扫描器ClassPathBeanDefinitionScanner scanner
		//scanner的用处不是很大,它仅仅是在我们外部手动调用 .scan 等方法才有用,常规方式是不会用到scanner对象的
		this();
		//把传入的类进行注册,这里有两个情况,
		//传入传统的配置类
		//传入bean(虽然一般没有人会这么做
		//看到后面会知道spring把传统的带上@Configuration的配置类称之为FULL配置类,不带@Configuration的称之为Lite配置类
		//但是我们这里先把带上@Configuration的配置类称之为传统配置类,不带的称之为普通bean
		register(annotatedClasses);
		//刷新
		refresh();
	}
复制代码

这个方法第一眼看上去,很简单,无非就是三行代码,但是这三行代码包含了大千世界。

我们先来为构造方法做一个简单的说明:

  1. 这是一个有参的构造方法,可以接收多个配置类,不过一般情况下,只会传入一个配置类。

  2. 这个配置类有两种情况,一种是传统意义上的带上@Configuration注解的配置类,还有一种是没有带上@Configuration,但是带有@Component,@Import,@ImportResouce,@Service,@ComponentScan等注解的配置类,在Spring内部把前者称为Full配置类,把后者称之为Lite配置类。在本源码分析中,有些地方也把Lite配置类称为普通Bean。

我们先来看一下第一行代码:通过this()调用此类无参的构造方法,代码会跑到下面:

public class AnnotationConfigApplicationContext extends GenericApplicationContext implements AnnotationConfigRegistry {

	//注解bean定义读取器,主要作用是用来读取被注解的了bean
	private final AnnotatedBeanDefinitionReader reader;

	//扫描器,它仅仅是在我们外部手动调用 .scan 等方法才有用,常规方式是不会用到scanner对象的
	private final ClassPathBeanDefinitionScanner scanner;

	/**
	 * Create a new AnnotationConfigApplicationContext that needs to be populated
	 * through {@link #register} calls and then manually {@linkplain #refresh refreshed}.
	 */
	public AnnotationConfigApplicationContext() {
		//会隐式调用父类的构造方法,初始化DefaultListableBeanFactory

		//初始化一个Bean读取器
		this.reader = new AnnotatedBeanDefinitionReader(this);

		//初始化一个扫描器,它仅仅是在我们外部手动调用 .scan 等方法才有用,常规方式是不会用到scanner对象的
		this.scanner = new ClassPathBeanDefinitionScanner(this);
	}
}
复制代码

首先映入眼帘的是reader和scanner,无参构造方法中就是对reader和scanner进行了实例化,reader的类型是AnnotatedBeanDefinitionReader,从字面意思就可以看出它是一个 “打了注解的Bean定义读取器”,scanner的类型是ClassPathBeanDefinitionScanner,其实这个字段并不重要,它仅仅是在我们外面手动调用.scan方法,或者调用参数为String的构造方法,传入需要扫描的包名,才会用到,像我们这样传入配置类是不会用到这个scanner对象的。

AnnotationConfigApplicationContext类是有继承关系的,会隐式调用父类的构造方法:

public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {
	private final DefaultListableBeanFactory beanFactory;
	public GenericApplicationContext() {
		this.beanFactory = new DefaultListableBeanFactory();
	}
}
复制代码

这个代码很简单,就是初始化了DefaultListableBeanFactory。

我们再来看看DefaultListableBeanFactory的关系图:

剑指Spring源码(一)

DefaultListableBeanFactory是相当重要的,从字面意思就可以看出它是一个Bean的工厂,什么是Bean的工厂?当然就是用来生产和获得Bean的。

让我们把目光回到AnnotationConfigApplicationContext的无参构造方法,让我们看看Spring在初始化AnnotatedBeanDefinitionReader的时候做了什么:

public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) {
		this(registry, getOrCreateEnvironment(registry));
	}
复制代码

这里的BeanDefinitionRegistry当然就是AnnotationConfigApplicationContext的实例了,这里又直接调用了此类其他的构造方法:

public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
		Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
		Assert.notNull(environment, "Environment must not be null");
		this.registry = registry;
		this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
		AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
	}
复制代码

让我们把目光移动到这个方法的最后一行,进入registerAnnotationConfigProcessors方法:

public static void registerAnnotationConfigProcessors(BeanDefinitionRegistry registry) {
		registerAnnotationConfigProcessors(registry, null);
	}
复制代码

这又是一个门面方法,再点进去,这个方法的返回值Set,但是上游方法并没有去接收这个返回值,所以这个方法的返回值也不是很重要了,当然方法内部给这个返回值赋值也不重要了。由于这个方法内容比较多,这里就把最核心的贴出来,这个方法的核心就是注册Spring内置的多个Bean:

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));
}
复制代码
  1. 判断容器中是否已经存在了ConfigurationClassPostProcessor Bean

  2. 如果不存在(当然这里肯定是不存在的),就通过RootBeanDefinition的构造方法获得ConfigurationClassPostProcessor的BeanDefinition,RootBeanDefinition是BeanDefinition的子类:

剑指Spring源码(一)
  1. 执行registerPostProcessor方法,registerPostProcessor方法内部就是注册Bean。

当然这里注册其他Bean也是一样的流程。

BeanDefinition是什么,顾名思义,它是用来描述Bean的,里面存放着关于Bean的一系列信息,比如Bean的作用域,Bean所对应的Class,是否懒加载,是否Primary等等,这个BeanDefinition也相当重要,我们以后会常常和它打交道。

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);
	}
复制代码

这方法为BeanDefinition设置了一个Role,ROLE_INFRASTRUCTURE代表这是spring内部的,并非用户定义的,然后又调用了registerBeanDefinition方法,再点进去,Oh No,你会发现它是一个接口,没办法直接点进去了,首先要知道registry实现类是什么,那么它的实现是什么呢?答案是DefaultListableBeanFactory:

public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
			throws BeanDefinitionStoreException {
		this.beanFactory.registerBeanDefinition(beanName, beanDefinition);
	}
复制代码

这又是一个门面方法,再点进去,核心在于下面两行代码:

//beanDefinitionMap是Map<String, BeanDefinition>,
//这里就是把beanName作为key,ScopedProxyMode作为value,推到map里面
this.beanDefinitionMap.put(beanName, beanDefinition);

//beanDefinitionNames就是一个List<String>,这里就是把beanName放到List中去
this.beanDefinitionNames.add(beanName);
复制代码

从这里可以看出DefaultListableBeanFactory就是我们所说的容器了,里面放着beanDefinitionMap,beanDefinitionNames,beanDefinitionMap是一个hashMap,beanName作为Key,beanDefinition作为Value,beanDefinitionNames是一个集合,里面存放了beanName。打个断点,第一次运行到这里,监视这两个变量:

剑指Spring源码(一)
剑指Spring源码(一)

DefaultListableBeanFactory中的beanDefinitionMap,beanDefinitionNames也是相当重要的,以后会经常看到它,最好看到它,第一时间就可以反应出它里面放了什么数据

这里仅仅是注册,可以简单的理解为把一些原料放入工厂,工厂还没有真正的去生产。

上面已经介绍过,这里会一连串注册好几个Bean,在这其中最重要的一个Bean(没有之一)就是BeanDefinitionRegistryPostProcessor Bean。

ConfigurationClassPostProcessor实现BeanDefinitionRegistryPostProcessor接口,BeanDefinitionRegistryPostProcessor接口又扩展了BeanFactoryPostProcessor接口,BeanFactoryPostProcessor是Spring的扩展点之一,ConfigurationClassPostProcessor是Spring极为重要的一个类,必须牢牢的记住上面所说的这个类和它的继承关系。

剑指Spring源码(一)

除了注册了ConfigurationClassPostProcessor,还注册了其他Bean,其他Bean也都实现了其他接口,比如BeanPostProcessor接口。

BeanPostProcessor接口也是Spring的扩展点之一。

至此,实例化AnnotatedBeanDefinitionReader reader分析完毕。

由于常规使用方式是不会用到AnnotationConfigApplicationContext里面的scanner的,所以这里就不看scanner是如何被实例化的了。

把目光回到最开始,再分析第二行代码:

register(annotatedClasses);
复制代码

这里传进去的是一个数组,最终会循环调用如下方法:

<T> void doRegisterBean(Class<T> annotatedClass, @Nullable Supplier<T> instanceSupplier, @Nullable String name,
			@Nullable Class<? extends Annotation>[] qualifiers, BeanDefinitionCustomizer... definitionCustomizers) {
		//AnnotatedGenericBeanDefinition可以理解为一种数据结构,是用来描述Bean的,这里的作用就是把传入的标记了注解的类
		//转为AnnotatedGenericBeanDefinition数据结构,里面有一个getMetadata方法,可以拿到类上的注解
		AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);

		//判断是否需要跳过注解,spring中有一个@Condition注解,当不满足条件,这个bean就不会被解析
		if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
			return;
		}

		abd.setInstanceSupplier(instanceSupplier);

		//解析bean的作用域,如果没有设置的话,默认为单例
		ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
		abd.setScope(scopeMetadata.getScopeName());

		//获得beanName
		String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));

		//解析通用注解,填充到AnnotatedGenericBeanDefinition,解析的注解为Lazy,Primary,DependsOn,Role,Description
		AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);

		//限定符处理,不是特指@Qualifier注解,也有可能是Primary,或者是Lazy,或者是其他(理论上是任何注解,这里没有判断注解的有效性),如果我们在外面,以类似这种
		//AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(Appconfig.class);常规方式去初始化spring,
		//qualifiers永远都是空的,包括上面的name和instanceSupplier都是同样的道理
		//但是spring提供了其他方式去注册bean,就可能会传入了
		if (qualifiers != null) {
			//可以传入qualifier数组,所以需要循环处理
			for (Class<? extends Annotation> qualifier : qualifiers) {
				//Primary注解优先
				if (Primary.class == qualifier) {
					abd.setPrimary(true);
				}
				//Lazy注解
				else if (Lazy.class == qualifier) {
					abd.setLazyInit(true);
				}
				//其他,AnnotatedGenericBeanDefinition有个Map<String,AutowireCandidateQualifier>属性,直接push进去
				else {
					abd.addQualifier(new AutowireCandidateQualifier(qualifier));
				}
			}
		}

		for (BeanDefinitionCustomizer customizer : definitionCustomizers) {
			customizer.customize(abd);
		}

		//这个方法用处不大,就是把AnnotatedGenericBeanDefinition数据结构和beanName封装到一个对象中
		BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);

		definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);

		//注册,最终会调用DefaultListableBeanFactory中的registerBeanDefinition方法去注册,
		//DefaultListableBeanFactory维护着一系列信息,比如beanDefinitionNames,beanDefinitionMap
		//beanDefinitionNames是一个List<String>,用来保存beanName
		//beanDefinitionMap是一个Map,用来保存beanName和beanDefinition
		BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
	}
复制代码

在这里又要说明下,以常规方式去注册配置类,此方法中除了第一个参数,其他参数都是默认值。

  1. 通过AnnotatedGenericBeanDefinition的构造方法,获得配置类的BeanDefinition,这里是不是似曾相似,在注册ConfigurationClassPostProcessor类的时候,也是通过构造方法去获得BeanDefinition的,只不过当时是通过RootBeanDefinition去获得,现在是通过AnnotatedGenericBeanDefinition去获得。

    剑指Spring源码(一)
  2. 判断需不需要跳过注册,Spring中有一个@Condition注解,如果不满足条件,就会跳过这个类的注册。

  3. 然后是解析作用域,如果没有设置的话,默认为单例。

  4. 获得BeanName。

  5. 解析通用注解,填充到AnnotatedGenericBeanDefinition,解析的注解为Lazy,Primary,DependsOn,Role,Description。

  6. 限定符处理,不是特指@Qualifier注解,也有可能是Primary,或者是Lazy,或者是其他(理论上是任何注解,这里没有判断注解的有效性)。

  7. 把AnnotatedGenericBeanDefinition数据结构和beanName封装到一个对象中(这个不是很重要,可以简单的理解为方便传参)。

  8. 注册,最终会调用DefaultListableBeanFactory中的registerBeanDefinition方法去注册:

public static void registerBeanDefinition(
			BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
			throws BeanDefinitionStoreException {

		//获取beanName
		// Register bean definition under primary name.
		String beanName = definitionHolder.getBeanName();

		//注册bean
		registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
                
                //Spring支持别名
		// Register aliases for bean name, if any.
		String[] aliases = definitionHolder.getAliases();
		if (aliases != null) {
			for (String alias : aliases) {
				registry.registerAlias(beanName, alias);
			}
		}
	}
复制代码

这个registerBeanDefinition是不是又有一种似曾相似的感觉,没错,在上面注册Spring内置的Bean的时候,已经解析过这个方法了,这里就不重复了,此时,让我们再观察下beanDefinitionMap beanDefinitionNames两个变量,除了Spring内置的Bean,还有我们传进来的Bean,这里的Bean当然就是我们的配置类了:

剑指Spring源码(一)
剑指Spring源码(一)

到这里注册配置类也分析完毕了。

大家可以看到其实到这里,Spring还没有进行扫描,只是实例化了一个工厂,注册了一些内置的Bean和我们传进去的配置类,真正的大头是在第三行代码:

refresh();
复制代码

不过,这就是下一章的内容了。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

理解专业程序员

理解专业程序员

(美)杰拉尔德·温伯格(GeraldM.Weinberg) / 刘天北 / 清华大学出版社 / 2006-7 / 25.00元

《理解专业程序员》通过行内专家的独特视角,介绍了如何成为优秀程序员,如何提高工作绩效等问题。全书由多篇讨论程序员职业的短文组成,内容精彩绝伦,是一部任何在这个变化急剧的领域工作的人都不可错过的重要作品。本书论述生动翔实——你肯定能从中认出你自己和你的公司的故事——因此不仅极富教益,而且读来也引人入胜。 各篇主题包括:对于专业程序员重要的若干问题,成为专业程序员的途径,在企业官僚体......一起来看看 《理解专业程序员》 这本书的介绍吧!

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具