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 注解)。

Spring 源码解析——@Configuration 作用及其实现原理(一)_第1张图片

Spring 源码解析——@Configuration 作用及其实现原理(一)_第2张图片

 通过上面两次运行结果的对比,我们发现了一个很有趣的现象,即当我们没有为配置类添加 @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 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();
	}

 首先按照注释,这个方法的主要功能就是创建一个 AnnotationConfigApplicationContext 对象,从给定的带注释的类派生bean 定义(BeanDefinition)并自动刷新容器上下文。同时它给了一个注解示例,正是我们所说的 @Configuration,然后在这个方法的第一行它调用了自身的无参构造器(无参构造器主要是创建了两个不同种类的 Bean 扫描器,其中一个注解扫描器的属性名为 reader,这个注解扫描器会在下面的 register 方法中用到),然后接下来调用了 register 方法 进行注册,因此我们跟进这个方法。

3.2 register(Bean 注册流程)

	/**
	 * Register one or more annotated classes to be processed.
	 * 

Note that {@link #refresh()} must be called in order for the context * to fully process the new classes. * @param annotatedClasses one or more annotated classes, * e.g. {@link Configuration @Configuration} classes * @see #scan(String...) * @see #refresh() */ public void register(Class... annotatedClasses) { Assert.notEmpty(annotatedClasses, "At least one annotated class must be specified"); this.reader.register(annotatedClasses); }

 在这里它直接调用了注解扫描器 reader 的 register 方法,我们继续跟进。

	/**
	 * Register one or more annotated classes to be processed.
	 * 

Calls to {@code register} are idempotent; adding the same * annotated class more than once has no additional effect. * @param annotatedClasses one or more annotated classes, * e.g. {@link Configuration @Configuration} classes */ public void register(Class... annotatedClasses) { for (Class annotatedClass : annotatedClasses) { registerBean(annotatedClass); } }

在这个 reader 的 register 方法中它对我们刚刚传进来的注解类集合进行遍历,并调用 registerBean 方法对它们依次进行处理。这里需要注意的是在注解中解释到,对于 register 方法的多次调用是幂等(意味着多次调用会得到相同的结果)的,并且其强调多次添加相同的带注释的类没有额外的效果。接着我们跟进 registerBean 方法。

	/**
	 * Register a bean from the given bean class, deriving its metadata from
	 * class-declared annotations.
	 * @param annotatedClass the class of the bean
	 */
	public void registerBean(Class annotatedClass) {
		doRegisterBean(annotatedClass, null, null, null);
	}

这个方法主要作用就是注册 Bean 类并从其类声明中获取元数据(也就是获取注解信息),然后没做别的事就又直接调了 doRegisterBean 方法,因此我们继续跟进。

	/**
	 * Register a bean from the given bean class, deriving its metadata from
	 * class-declared annotations.
	 * @param annotatedClass the class of the bean
	 * @param instanceSupplier a callback for creating an instance of the bean
	 * (may be {@code null})
	 * @param name an explicit name for the bean
	 * @param qualifiers specific qualifier annotations to consider, if any,
	 * in addition to qualifiers at the bean class level
	 * @param definitionCustomizers one or more callbacks for customizing the
	 * factory's {@link BeanDefinition}, e.g. setting a lazy-init or primary flag
	 * @since 5.0
	 */
	 void doRegisterBean(Class annotatedClass, @Nullable Supplier instanceSupplier, @Nullable String name,
			@Nullable Class[] qualifiers, BeanDefinitionCustomizer... definitionCustomizers) {
                // 1. 为我们传入的 class 创建对应的 BeanDefinition
		AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);
                // 2. 判断当前类是否应当跳过(根据 @Conditional 判断)
		if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
			return;
		}

		abd.setInstanceSupplier(instanceSupplier);
                // 3. 从类元数据中解析获取 scope 作用范围信息(默认为 singleton)
		ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
                // 4. 为 BeanDefinition 设置 scope 作用范围信息
		abd.setScope(scopeMetadata.getScopeName());
		String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));

                // 5. 处理通用注解(例如 @Lazy 等)
		AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
		if (qualifiers != null) {
			for (Class qualifier : qualifiers) {
				if (Primary.class == qualifier) {
					abd.setPrimary(true);
				}
				else if (Lazy.class == qualifier) {
					abd.setLazyInit(true);
				}
				else {
					abd.addQualifier(new AutowireCandidateQualifier(qualifier));
				}
			}
		}
		for (BeanDefinitionCustomizer customizer : definitionCustomizers) {
			customizer.customize(abd);
		}

                // 6. 创建 BeanDefinition 的 BeanDefinitionHolder
		BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
                // 7. 应用 ScopedProxyMode
		definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
                // 8. 注册 BeanDefinition
		BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
	}

这个 doRegisterBean 方法是注册我们传进来的配置类的主要方法,代码的逻辑比较复杂,这里我在注释中已经先简要标注(未标注的代码就是跟目前 @Configuration 代码分析的无关代码),接下来我们就对每一步的操作进行大概的分析。

(1)为我们传入的 class 创建对应的 BeanDefinition 并进行相关默认值的初始化,这里的 BeanDefinition 指的是 AnnotatedGenericBeanDefinition,它是 GenericBeanDefinition 的子类,主要添加了对注解元数据的支持(这点通过下面的构造器代码也能够看出来,它将类元数据 metadata 单独进行保存,这个 StandardAnnotationMetadata 中有一个属性 annotations 就是用来专门保存类的注解信息的)。

	/**
	 * Create a new AnnotatedGenericBeanDefinition for the given bean class.
	 * @param beanClass the loaded bean class
	 */
	public AnnotatedGenericBeanDefinition(Class beanClass) {
		setBeanClass(beanClass);
		this.metadata = new StandardAnnotationMetadata(beanClass, true);
	}

(2)根据 @Conditional 注解来判断当前 Bean 实例化是否需要跳过(即当前不满足 @Conditional 注解条件)。

	/**
	 * Determine if an item should be skipped based on {@code @Conditional} annotations.
	 * The {@link ConfigurationPhase} will be deduced from the type of item (i.e. a
	 * {@code @Configuration} class will be {@link ConfigurationPhase#PARSE_CONFIGURATION})
	 * @param metadata the meta data
	 * @return if the item should be skipped
	 */
	public boolean shouldSkip(AnnotatedTypeMetadata metadata) {
		return shouldSkip(metadata, null);
	}

(3)从类元数据(上面提到的 BeanDefinition 中的 metadata)中解析获取 Bean 的作用范围 scope(该值默认为 singleton,注意此时刚刚创建的 AnnotatedGenericBeanDefinition 对象的 scope 属性为空)。

(4)将获取到的 scope 赋值给 BeanDefinition(这步完成后刚刚创建的 AnnotatedGenericBeanDefinition 对象的 scope 属性为 scope 值)。

(5)获取并处理类的通用注解(@Lazy、@Primary、@DependsOn、@Role 和 @Description)信息,这里调用的是 processCommonDefinitionAnnotations 方法,这个方法没有什么太大营养,就是把你的类元数据中的注解信息取出来,然后依次检查是否设置了对应的注解,并将值赋给刚刚创建的 BeanDefinition 对象

	static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd, AnnotatedTypeMetadata metadata) {
		AnnotationAttributes lazy = attributesFor(metadata, Lazy.class);
		if (lazy != null) {
			abd.setLazyInit(lazy.getBoolean("value"));
		}
		else if (abd.getMetadata() != metadata) {
			lazy = attributesFor(abd.getMetadata(), Lazy.class);
			if (lazy != null) {
				abd.setLazyInit(lazy.getBoolean("value"));
			}
		}

		if (metadata.isAnnotated(Primary.class.getName())) {
			abd.setPrimary(true);
		}
		AnnotationAttributes dependsOn = attributesFor(metadata, DependsOn.class);
		if (dependsOn != null) {
			abd.setDependsOn(dependsOn.getStringArray("value"));
		}

		AnnotationAttributes role = attributesFor(metadata, Role.class);
		if (role != null) {
			abd.setRole(role.getNumber("value").intValue());
		}
		AnnotationAttributes description = attributesFor(metadata, Description.class);
		if (description != null) {
			abd.setDescription(description.getString("value"));
		}
	}

(6)创建 BeanDefinition 对应的 BeanDefinitionHolder 对象,这个对象没什么太多特殊的作用,就是持有一个 BeanDefinition 对象。

(7)接下里调用 applyScopedProxyMode 方法来应用(创建)一个 ScopedProxyMode 对象(跟 @Configuration 无关,所以不去过细分析)。

(8)最后一步,也就是最重要的注册 BeanDefinition 到 BeanDefinitionMap 中,这里直接进方法看代码。

	/**
	 * Register the given bean definition with the given bean factory.
	 * @param definitionHolder the bean definition including name and aliases
	 * @param registry the bean factory to register with
	 * @throws BeanDefinitionStoreException if registration failed
	 */
	public static void registerBeanDefinition(
			BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
			throws BeanDefinitionStoreException {

		// 9. Register bean definition under primary name.
		String beanName = definitionHolder.getBeanName();
		registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

		// Register aliases for bean name, if any.
		String[] aliases = definitionHolder.getAliases();
		if (aliases != null) {
			for (String alias : aliases) {
				registry.registerAlias(beanName, alias);
			}
		}
	}

 (9)这里首先使用 Bean 的主名称进行注册,这个主名称其实就是我们的 Bean 名称(当前的名称就是我们刚刚传入的配置类的类名 SpringConfig),然后又使用了标签进行注册,这里我们先不考虑标签,所以直接使用 Bean 名称进行注册,继续跟进 registry.registerBeanDefinition 方法。

	public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
			throws BeanDefinitionStoreException {

                // 10. 验证 BeanName 和 BeanDefinition 不为空
		Assert.hasText(beanName, "Bean name must not be empty");
		Assert.notNull(beanDefinition, "BeanDefinition must not be null");

                // 11. 如果当前的 BeanDefinition 是 AbstractBeanDefinition 子类则验证对象信息
		if (beanDefinition instanceof AbstractBeanDefinition) {
			try {
				((AbstractBeanDefinition) beanDefinition).validate();
			}
			catch (BeanDefinitionValidationException ex) {
				throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
						"Validation of bean definition failed", ex);
			}
		}

		BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
                // 如果容器中已经存在了该 BeanName 所对应的 BeanDefinition
		if (existingDefinition != null) {
                        // 如果不允许重写(替换)原始的 BeanDefinition直接抛异常
			if (!isAllowBeanDefinitionOverriding()) {
				throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
			}
                        // 如果允许替换则比较两个 BeanDefinition 的 @Role 值
			else if (existingDefinition.getRole() < beanDefinition.getRole()) {
				// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
				// 这里省略打印 info 信息
			}
			else if (!beanDefinition.equals(existingDefinition)) {
				// 这里省略打印 debug信息
			}
			else {
				// 这里省略打印 trace 信息
			}
            
                        // 将新的 BeanDefinition 对象添加到 Map 中
			this.beanDefinitionMap.put(beanName, beanDefinition);
		}
                // 12. 如果 Map 中不存在 BeanName 所对应的的 BeanDefinition 
		else {
                        // 13. 判断当前工厂是否已经开始创建 Bean 对象
			if (hasBeanCreationStarted()) {
				// Cannot modify startup-time collection elements anymore (for stable iteration)
				synchronized (this.beanDefinitionMap) {
                                        // 将该 BeanDefinition 添加到 Map 中
					this.beanDefinitionMap.put(beanName, beanDefinition);
                                        // 按照原始 BeanName List 大小 +1 创建新 BeanName List
					List updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
                                        // 将原始 BeanName List 内容复制到新 BeanName List 中
					updatedDefinitions.addAll(this.beanDefinitionNames);
                                        // 将新添加的 BeanName 添加到新的 BeanName List 中
					updatedDefinitions.add(beanName);
                                        // 让原始的 BeanName List 指向新 BeanName List
					this.beanDefinitionNames = updatedDefinitions;
                                        // 移除工厂内部的手动单例名称集
					removeManualSingletonName(beanName);
				}
			}
			else {
				// Still in startup registration phase
                                // 14. 将 BeanDefinition 直接添加到 Map 中
				this.beanDefinitionMap.put(beanName, beanDefinition);
                                // 15. 如果非正在创建则直接将 BeanName 添加到 BeanName List 中
				this.beanDefinitionNames.add(beanName);
                                // 从 manualSingletonNames 移除 BeanName
				removeManualSingletonName(beanName);
			}
			this.frozenBeanDefinitionNames = null;
		}
        
                // 如果 Map 中已存在 BeanName 对应的 BeanDefinition 或者 BeanName 为单例模式
		if (existingDefinition != null || containsSingleton(beanName)) {
                        // 重置 BeanName 在容器中对应的 BeanDefinition
			resetBeanDefinition(beanName);
		}
	}

(10)验证 BeanName 和 BeanDefinition 不为空。

(11)然后判断当前的 BeanDefinition 是否是 AbstractBeanDefinition 的子类,如果是的话就调用 prepareMethodOverrides 方法就对其抽象方法进行验证,判断这个 BeanDefinition 是否正确重写了它的抽象方法。

(12)接着判断当前容器中是否已经存在 BeanName 所对应的 BeanDefinition(BeanDefinition 存储于 BeanDefinitionMap 中,键为 BeanName,值为 BeanDefinition),如果已经存在就判断是否可以进行替换等一系列逻辑,如果不存在就进入 else 语句块。

(13)接着调用 hasBeanCreationStarted 判断当前工厂是否已经开始创建 Bean(这个方法里面主要是通过判断 alreadyCreated 容器内是否为空,因为当工厂将要实例化创建一个 Bean 对象的时候都会先将其存放在这个 alreadyCreated 容器中),如果此时工厂还未开始创建 Bean 对象,那么我们就可以直接将当前的 BeanName 和 BeanDefinition 直接添加到容器中。

但是如果此时工厂已经开始创建 Bean 对象了(根据 BeanDefinition),那么正如它注释中所讲这个时候再对 BeanDefinitionMap 和 BeanDefinitionNames 来进行操作就是不安全的了,因此这个时候我们就需要通过加锁来保证整个的添加操作线程安全。但是在什么情况下会出现这个问题呢,显然如果我们按照正常的代码逻辑到目前为止还没有进行任何的 Bean 的创建工作,但是假如在我们自己的业务代码中,在另一个线程中,当容器开始创建 Bean 对象时,我们直接获取到容器中的这个 DefaultListableBeanFactory,并调用这个 registerBeanDefinition 方法对其当中的 BeanDefinitionMap 进行添加或删除等修改操作,那么就会造成非线程安全的问题(这里的安全问题主要是指虽然 BeanDefinitionMap 是 concurrentHashMap 的线程安全 Map,对于将 BeanDefinition 添加到其中的操作时线程安全的,但是我们无法保证 BeanDefinitionName 和 BeanDefinition 能够同时被分别添加到两个容器中(非原子操作),即整个操作流程是非线程安全的)。

并且还可以注意到在下面的 BeanDefinitionNames 这个 List 当中顺序保存了所有的 BeanName,在锁的过程中我们锁的是 BeanDefinitionMap 这个对象,并没有锁住这个 BeanDefinitionNames,因此跟上面一样的道理,假如有其他的线程正在容器中遍历读取 BeanDefinitionNames List 当中的信息对 List 当中的 Bean 进行实例化,那么此时我们在当前线程对 BeanDefinitionNames List 直接进行添加操作会引发快速失败(抛出 ConcurrentModificationException 异常),所以这里我们是采用了类似于安全失败的机制,即先创建一个新的 List,将原 List 内的元素拷贝过来,再添加新的元素,最后再将原 List 指向新 List 即可。

(14)如果此时容器未开始创建 Bean 对象,也就是正常启动流程的话,那么就会进入到第二个语句块,直接将 BeanDefinition 添加到 BeanDefinitionMap 中。

(15)将 BeanName 添加到 BeanDefinitionNames List 当中,注册流程完成。

下图为 register Bean 注册的整体流程,可以对照代码来看(这里仅针对第一次启动容器时,仅具备 @Configuration 注解的情况,在整个注册流程中其实有很多的分支,但是为了具体分析所以都直接省略掉了)。

Spring 源码解析——@Configuration 作用及其实现原理(一)_第3张图片

3.3 refresh(BeanClass 的替换)

接下来进入到 refresh 方法,在这个方法执行的过程中完成了 BeanClass 的替换(原始类替换为 CGLIB 代理类),这个方法的整体流程比较复杂,但是对于此文我们仅分析 @Configuration 的作用方式和其相关的代码,所以会省略掉一些代码的分析,下面直接进入到 refresh 方法的分析。

	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// Prepare this context for refreshing.
			prepareRefresh();
			// Tell the subclass to refresh the internal bean factory.
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
			// Prepare the bean factory for use in this context.
			prepareBeanFactory(beanFactory);

			try {
				// Allows post-processing of the bean factory in context subclasses.
				postProcessBeanFactory(beanFactory);

				// Invoke factory processors registered as beans in the context.
				invokeBeanFactoryPostProcessors(beanFactory);

				// Register bean processors that intercept bean creation.
				registerBeanPostProcessors(beanFactory);
				// Initialize message source for this context.
				initMessageSource();
				// Initialize event multicaster for this context.
				initApplicationEventMulticaster();
				// Initialize other special beans in specific context subclasses.
				onRefresh();
				// Check for listener beans and register them.
				registerListeners();

				// Instantiate all remaining (non-lazy-init) singletons.
				finishBeanFactoryInitialization(beanFactory);

				// Last step: publish corresponding event.
				finishRefresh();
			}

			catch (BeansException ex) {
				if (logger.isWarnEnabled()) {
					logger.warn("Exception encountered during context initialization - " +
							"cancelling refresh attempt: " + ex);
				}
				// Destroy already created singletons to avoid dangling resources.
				destroyBeans();
				// Reset 'active' flag.
				cancelRefresh(ex);
				// Propagate exception to caller.
				throw ex;
			}

			finally {
				// Reset common introspection caches in Spring's core, since we
				// might not ever need metadata for singleton beans anymore...
				resetCommonCaches();
			}
		}
	}

在这个方法里面有好多好多好多的方法,第一眼看上去感觉无从下手,但是我们可以确定的一点是在 register 方法中我们将刚刚的那个 SpringConfig 所对应的 BeanDefinition 已经注册到了 DefaultListableBeanFactory 的 BeanDefinitionMap 中,通过下面的 Debug 也可以确认这一点(断点打在未进入 refresh 方法之前),并且此时它的 BeanClass 认为原始类(非 CGLIB 代理类)。

Spring 源码解析——@Configuration 作用及其实现原理(一)_第4张图片

 然后我们又可以确定的是当我们最后从容其中取这个 Bean 对象时,我们取出来的并不是这个原始类所对应的对象,那么也就意味着,在上面的这些方法中,Spring 一定是在其中的某个方法里将其替换为了代理类。有了这个思路,我们可以通过一步一步执行的方式,来找到最终那个执行替换的方法。

通过一步一步的 debug 我们定位到,当执行完 invokeBeanFactoryProcessors 方法后这个 BeanName 所对应的 BeanDefinition 中的 beanClass 就已经被替换掉了,因此我们可以确定 Spring 对于 @Configuration 指定类的替换工作就是在这个方法中完成的,所以我们直接跟进这个方法。

	/**
	 * Instantiate and invoke all registered BeanFactoryPostProcessor beans,
	 * respecting explicit order if given.
	 * 

Must be called before singleton instantiation. */ protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) { PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors()); // Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime // (e.g. through an @Bean method registered by ConfigurationClassPostProcessor) if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) { beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory)); beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader())); } }

这个方法的主要作用就是实例化并调用所有的被注册的 BeanFactoryPostProcessor Bean,接着跟进 invokeBeanFactoryPostProcessors 方法。

public static void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory, List beanFactoryPostProcessors) {
                // 省略无关代码...

		invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);

		// Now, invoke the postProcessBeanFactory callback of all processors handled so far.
		invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);

		// 省略无关代码..
}

因为这个方法实在实在是太长了太复杂了,我自己 Debug 跟了几次,发现上下的许多代码跟当前分析的这个 @Configuration 都是没有什么关系的,因此我就都省略掉了,我们只关注重点的实现代码,在这里最重要的两个方法一个是 invokeBeanDefinitionRegistryPostProcessors,另一个就是 invokeBeanFactoryPostProcessors

(1)invokeBeanDefinitionRegistryPostProcessors 方法中对于 @Configuration 主要的作用就是将其 @Configuration 注解信息解析到它的 BeanDefinition 的 Attributes 当中。

(2)invokeBeanFactoryPostProcessors 方法的作用主要是对 BeanDefinition 中的 Attitudes 进行判断,选取全注解(Attributes 当中的 configurationClass 所对应的值为 full)类,将其 BeanDefinition 中的 beanClass 替换为 CGLIB 的代理类。

3.3.1 invokeBeanDefinitionRegistryPostProcessors

因此下面我来以此介绍这两个方法,首先是跟进 invokeBeanDefinitionRegistryPostProcessors 方法,然后再跟进它所调用的 postProcessBeanDefinitionRegistry 方法。

	/**
	 * Invoke the given BeanDefinitionRegistryPostProcessor beans.
	 */
	private static void invokeBeanDefinitionRegistryPostProcessors(
			Collection postProcessors, BeanDefinitionRegistry registry) {

		for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {
			postProcessor.postProcessBeanDefinitionRegistry(registry);
		}
	}

 在这个方法中我们省略无关代码,直接跟进 processConfigBeanDefinitions 方法。

	/**
	 * Derive further bean definitions from the configuration classes in the registry.
	 */
	@Override
	public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
		// 省略无关代码...
		processConfigBeanDefinitions(registry);
	}

这个 processConfigBeanDefinitions 方法就是一个比较核心的方法了,在这个方法中就执行了解析类元数据注解信息,并将其 @Configuration 属性设置到 BeanDefinition 的 Attributes 属性中的逻辑。

	/**
	 * Build and validate a configuration model based on the registry of
	 * {@link Configuration} classes.
	 */
	public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
		List configCandidates = new ArrayList<>();
		String[] candidateNames = registry.getBeanDefinitionNames();

                // 1. 遍历所有的 BeanDefinition
		for (String beanName : candidateNames) {
			BeanDefinition beanDef = registry.getBeanDefinition(beanName);
                        // 如果已经被设置为全注解或者非全注解则打印 Debug 信息
			if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
					ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
				if (logger.isDebugEnabled()) {
					logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
				}
			}
                        // 2. 检查该 BeanDefinition 是否具备 @Configuration 注解,如果具备则添加属性
			else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
                                // 3. 添加到全注解集合中
				configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
			}
		}

		// Return immediately if no @Configuration classes were found
		if (configCandidates.isEmpty()) {
			return;
		}

		// 4. Sort by previously determined @Order value, if applicable
		configCandidates.sort((bd1, bd2) -> {
			int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
			int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
			return Integer.compare(i1, i2);
		});

		// 省略部分无关代码...

		// 5. Parse each @Configuration class
		ConfigurationClassParser parser = new ConfigurationClassParser(
				this.metadataReaderFactory, this.problemReporter, this.environment,
				this.resourceLoader, this.componentScanBeanNameGenerator, registry);

		Set candidates = new LinkedHashSet<>(configCandidates);
		Set alreadyParsed = new HashSet<>(configCandidates.size());
		do {
			parser.parse(candidates);
			parser.validate();

			Set configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
			configClasses.removeAll(alreadyParsed);

			// Read the model and create bean definitions based on its content
			if (this.reader == null) {
				this.reader = new ConfigurationClassBeanDefinitionReader(
						registry, this.sourceExtractor, this.resourceLoader, this.environment,
						this.importBeanNameGenerator, parser.getImportRegistry());
			}
			this.reader.loadBeanDefinitions(configClasses);
			alreadyParsed.addAll(configClasses);

			candidates.clear();
			
                        // 省略部分无关代码...
		}

		// 省略部分无关代码...
	}

 这个方法的代码很多,我在上面已经做了一些无关代码的省略,这里来大概梳理一下它的主要逻辑。

(1)首先遍历所有的 BeanDefinitionMap 中所有的 BeanDefinition(这里主要是通过 BeanDefinitionNames 当中所保存的 BeanName 作为键值,然后到 BeanDefinitionMap 中进行查找,这里进行循环查找的主要原因是在初始化的过程中我们可能传入了多个带有 @Configuration 注解的类);

(2)判断每个 BeanDefinition 的 Attributes 当中是否已经设置了 ConfigurationClass 键值对,如果已经设置了那么就打印一下 Debug 信息;

(3)如果目前还没有设置 ConfigurationClass 所对应的键值对,那么就调用 checkConfigurationClassCandidate 方法检查类元数据判断其是否具备 @Configuration 注解,如果具备则将其 ConfigurationClass 的值设置为 full ,否则为 lite,如果该类为全注解,那么将其添加到全注解集合中;

(4)按照 @Order 注解对 BeanDefinition 进行排序;

(5)解析每一个带 @Configuration 注解的类;

下面是刚刚所提到的 checkConfigurationClassCandidate 方法,可以大概看一下。

	/**
	 * Check whether the given bean definition is a candidate for a configuration class
	 * (or a nested component class declared within a configuration/component class,
	 * to be auto-registered as well), and mark it accordingly.
	 * @param beanDef the bean definition to check
	 * @param metadataReaderFactory the current factory in use by the caller
	 * @return whether the candidate qualifies as (any kind of) configuration class
	 */
	public static boolean checkConfigurationClassCandidate(
			BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {

		// 省略验证代码...

		if (isFullConfigurationCandidate(metadata)) {
			beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
		}
		else if (isLiteConfigurationCandidate(metadata)) {
			beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
		}
		else {
			return false;
		}

		// It's a full or lite configuration candidate... Let's determine the order value, if any.
		Integer order = getOrder(metadata);
		if (order != null) {
			beanDef.setAttribute(ORDER_ATTRIBUTE, order);
		}

		return true;
	}

	/**
	 * Check the given metadata for a full configuration class candidate
	 * (i.e. a class annotated with {@code @Configuration}).
	 * @param metadata the metadata of the annotated class
	 * @return {@code true} if the given class is to be processed as a full
	 * configuration class, including cross-method call interception
	 */
	public static boolean isFullConfigurationCandidate(AnnotationMetadata metadata) {
		return metadata.isAnnotated(Configuration.class.getName());
	}

3.3.2 invokeBeanFactoryPostProcessors

另一个比较重要的方法就是这个 invokeBeanFactoryPostProcessors 方法,因此我们直接跟进去。

	/**
	 * Invoke the given BeanFactoryPostProcessor beans.
	 */
	private static void invokeBeanFactoryPostProcessors(
			Collection postProcessors, ConfigurableListableBeanFactory beanFactory) {

		for (BeanFactoryPostProcessor postProcessor : postProcessors) {
			postProcessor.postProcessBeanFactory(beanFactory);
		}
	}

 接着又通过一个循环调用了所有 BeanFactoryPostProcessor 的 postProcessBeanFactory 方法(与 @Configuration 作用不大不详细分析),在这里我们只有一个 BeanFactory 就是最开始创建的那个 DefaultListableBeanFactory,所以我们继续跟进这个方法。

	/**
	 * Prepare the Configuration classes for servicing bean requests at runtime
	 * by replacing them with CGLIB-enhanced subclasses.
	 */
	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
		int factoryId = System.identityHashCode(beanFactory);
		if (this.factoriesPostProcessed.contains(factoryId)) {
			throw new IllegalStateException(
					"postProcessBeanFactory already called on this post-processor against " + beanFactory);
		}
		this.factoriesPostProcessed.add(factoryId);
		if (!this.registriesPostProcessed.contains(factoryId)) {
			// BeanDefinitionRegistryPostProcessor hook apparently not supported...
			// Simply call processConfigurationClasses lazily at this point then.
			processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);
		}
        
                // 代理 @Configuration 对象
		enhanceConfigurationClasses(beanFactory);
		beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));
	}

 首先该方法是属于 ConfigurationClassPostProcessor 类的,而当我们见到上面这段注解的时候,我们就已经胜利一半了,如注解所示,该方法的主要作用就是通过将配置类替换为 CGLIB 增强的子类,准备用于在运行时为 Bean 请求提供服务的 Configuration 类(@Configuration),因此我们直接抓住最核心的方法 enhanceConfigurationClasses

	/**
	 * Post-processes a BeanFactory in search of Configuration class BeanDefinitions;
	 * any candidates are then enhanced by a {@link ConfigurationClassEnhancer}.
	 * Candidate status is determined by BeanDefinition attribute metadata.
	 * @see ConfigurationClassEnhancer
	 */
	public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
		Map configBeanDefs = new LinkedHashMap<>();

                // 遍历所有的 BeanDefinition
		for (String beanName : beanFactory.getBeanDefinitionNames()) {
			BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);

                        // 判断当前 Bean 是否为全注解(具有 @Configuration 注解)
			if (ConfigurationClassUtils.isFullConfigurationClass(beanDef)) {
				// 省略一些验证代码..

                                // 如果是全注解类将其放入 configBeanDefs 容器中
				configBeanDefs.put(beanName, (AbstractBeanDefinition) beanDef);
			}
		}
                // 如果没有发现全注解类直接返回
		if (configBeanDefs.isEmpty()) {
			// nothing to enhance -> return immediately
			return;
		}

                // CGLIB 代理逻辑
		ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer();
		for (Map.Entry entry : configBeanDefs.entrySet()) {
			AbstractBeanDefinition beanDef = entry.getValue();
			// If a @Configuration class gets proxied, always proxy the target class
			beanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
			try {
				// Set enhanced subclass of the user-specified bean class
				Class configClass = beanDef.resolveBeanClass(this.beanClassLoader);
				if (configClass != null) {
					Class enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);
					if (configClass != enhancedClass) {
						if (logger.isTraceEnabled()) {
							logger.trace(String.format("Replacing bean definition '%s' existing class '%s' with " +
									"enhanced class '%s'", entry.getKey(), configClass.getName(), enhancedClass.getName()));
						}

                                                // 使用代理类替换原始类
						beanDef.setBeanClass(enhancedClass);
					}
				}
			}
			catch (Throwable ex) {
				throw new IllegalStateException("Cannot load configuration class: " + beanDef.getBeanClassName(), ex);
			}
		}
	}


	/**
	 * Determine whether the given bean definition indicates a full {@code @Configuration}
	 * class, through checking {@link #checkConfigurationClassCandidate}'s metadata marker.
	 */
	public static boolean isFullConfigurationClass(BeanDefinition beanDef) {
		return CONFIGURATION_CLASS_FULL.equals(beanDef.getAttribute(CONFIGURATION_CLASS_ATTRIBUTE));
	}

在这个方法中主要就执行了获取 BeanDefinition 的 Attributes 中键为 ConfigurationClass 值为 full 的全注解类,并对其使用 CGLIB 进行代理的过程。

(1)遍历获取所有的 BeanDefinition,获取全注解类,并将其添加到全注解类容器(configBeanDefs)中;

(2)遍历 configBeanDefs 容器,取出全注解类,并使用 CGLIB 进行代理;

(3)使用代理后的类来替换原始类(替换 BeanDefinition 中的 BeanClass);

到这里我们就已经完成了对于 @Configuration 注解处理的大致代码流程分析。

 

四、内容总结

在这篇博文里主要是对 @Configuration 注解的作用过程(CGLIB 代理流程)进行了大概的分析,但是还有一些比较细节的点没有分析到,并且还没有具体分析 @Configuration 为什么要使用 CGLIB 代理,这样到底有什么样的作用。但是因为这篇博文的篇幅已经太长了,所以打算将剩余内容的分析、内容总结和整体的流程图放在下一篇博文中。

只有登上山顶,才能看到那边的风光。

你可能感兴趣的:(Spring,源码解析,Spring注解式开发)