曹工说Spring Boot源码系列开讲了(1)-- Bean Definition到底是什么,附spring思维导图分享

写在前面的话 &&About me#

网上写 spring 的文章多如牛毛,为什么还要写呢,因为,很简单,那是人家写的;网上都鼓励你不要造轮子,为什么你还要造呢,因为,那不是你造的。

我不是要造 spring,我只是想把自己学习 spring 的一些感想,一些心得说出来,希望大家看到有不对的地方,请一定不吝赐教。

说说我自己,13 年小本毕业,软件工程专业,校招去了最近疯传的牢厂总部里待了 2 年,15 年越狱出来,某落魄互联网公司(PC 时代风头无两)待了 1 年,慨叹深圳买房之艰难,遂于 16 年底回蓉。趁着热血未冷,去了一家创业公司,9 个月后,欠薪 3 月,靠刷信用卡还贷,不得不含泪辞职;17 年投奔国企,目前从事公共安全相关工作,趁着对技术还有一腔热情,没事写写文章,目前主要兴趣是:分布式、微服务等后端技术;对 k8s 等新技术保持关注;没事参加一些线下技术活动,欢迎大家和我交流。

本系列的源码讲解思路#

本来,我是想分享一些 spring cloud 的东西,但后来发现,我自己在读 spring cloud 的过程中,有些东西也不是理解得很透彻,比如各种 @Enable 注解其实是用到了 spring boot 的东西,然后我觉得应该倒回去先看看 spring boot,然后呢,看 spring boot 的过程中,发现 spring 和 spring boot 其实是一个深度融合,“你中有我,我中有你”的关系,比如 spring boot 启动时,不是会连带启动 spring 容器吗,等等。

我就想着,干脆把 spring boot 系统研究一把算了,我在 github 上找了 spring boot 的工程,克隆到了码云上(速度要快得多),然后自己回退到了 spring boot 的第一个版本,时间大概是 2013 年 4 月,其实这第一个版本,基本的代码也已经成型了,我就拿这个版本的源码在本地 idea 里面看,配套的 spring 版本是 4.0.0,也还行。

我自己加了不少注释在工程里,工程地址在这:

https://gitee.com/ckl111/spring-boot-first-version-learn

工程结构如下:

这个工程我也会一直维护着,我觉得,spring 4.0.0 的版本,暂时对我阅读代码来说,足够了,如果大家大概了解 spring 每个版本的新特性的话,可以发现,spring 4.0 开始,各种注解已经很完善了,现在虽然已经出到 5.2 版本了,但核心的东西也还是没有变化,所以,对我们研读源码,影响不大。如果真的把这个版本能读得差不多了,那想必对 spring /spring boot 的核心也理解差不多了,到时候再读新版本的源码也不迟,是吧。

总体来说:

spring boot 版本,2013 年 4 月,first version。

配套的 spring 版本,4.0.0.BOOTSTRAP-SNAPSHOT

那时候的 spring boot 长什么样子,我这边给个地址(我这已经克隆到码云了)

https://gitee.com/ckl111/spring-boot/tree/fb6b2244707dd5dfad12d62cb6a3c396555270d1/

目前已更新的部分#

曹工说 Spring Boot 源码(1)-- Bean Definition 到底是什么,附 spring 思维导图分享

曹工说 Spring Boot 源码(2)-- Bean Definition 到底是什么,咱们对着接口,逐个方法讲解

曹工说 Spring Boot 源码(3)-- 手动注册 Bean Definition 不比游戏好玩吗,我们来试一下

曹工说 Spring Boot 源码(4)-- 我是怎么自定义 ApplicationContext,从 json 文件读取 bean definition 的?

曹工说 Spring Boot 源码(5)-- 怎么从 properties 文件读取 bean

曹工说 Spring Boot 源码(6)-- Spring 怎么从 xml 文件里解析 bean 的

曹工说 Spring Boot 源码(7)-- Spring 解析 xml 文件,到底从中得到了什么(上)

曹工说 Spring Boot 源码(8)-- Spring 解析 xml 文件,到底从中得到了什么(util 命名空间)

曹工说 Spring Boot 源码(9)-- Spring 解析 xml 文件,到底从中得到了什么(context 命名空间上)

曹工说 Spring Boot 源码(10)-- Spring 解析 xml 文件,到底从中得到了什么(context:annotation-config 解析)

曹工说 Spring Boot 源码(11)-- context:component-scan,你真的会用吗(这次来说说它的奇技淫巧)

曹工说 Spring Boot 源码(12)-- Spring 解析 xml 文件,到底从中得到了什么(context:component-scan 完整解析 )

曹工说 Spring Boot 源码(13)-- AspectJ 的运行时织入(Load-Time-Weaving),基本内容是讲清楚了(附源码)

曹工说 Spring Boot 源码(14)-- AspectJ 的 Load-Time-Weaving 的两种实现方式细细讲解,以及怎么和 Spring Instrumentation 集成

曹工说 Spring Boot 源码(15)-- Spring 从 xml 文件里到底得到了什么(context:load-time-weaver 完整解析)

曹工说 Spring Boot 源码(16)-- Spring 从 xml 文件里到底得到了什么(aop:config 完整解析【上】)

曹工说 Spring Boot 源码(17)-- Spring 从 xml 文件里到底得到了什么(aop:config 完整解析【中】)

曹工说 Spring Boot 源码(18)-- Spring AOP 源码分析三部曲,终于快讲完了 (aop:config 完整解析【下】)

曹工说 Spring Boot 源码(19)-- Spring 带给我们的工具利器,创建代理不用愁(ProxyFactory)

曹工说 Spring Boot 源码(20)-- 码网恢恢,疏而不漏,如何记录 Spring RedisTemplate 每次操作日志

曹工说 Spring Boot 源码(21)-- 为了让大家理解 Spring Aop 利器 ProxyFactory,我已经拼了

曹工说 Spring Boot 源码(22)-- 你说我 Spring Aop 依赖 AspectJ,我依赖它什么了

曹工说 Spring Boot 源码(23)-- ASM 又立功了,Spring 原来是这么递归获取注解的元注解的

曹工说 Spring Boot 源码(24)-- Spring 注解扫描的瑞士军刀,asm 技术实战(上)

曹工说 Spring Boot 源码(25)-- Spring 注解扫描的瑞士军刀,ASM + Java Instrumentation,顺便提提 Jar 包破解

曹工说 Spring Boot 源码(26)-- 学习字节码也太难了,实在不能忍受了,写了个小小的字节码执行引擎

曹工说 Spring Boot 源码(27)-- Spring 的 component-scan,光是 include-filter 属性的各种配置方式,就够玩半天了

spring 思维导图(bean definition 部分)#

因为我这个系列,大概会按照思维导图的流程来走,然后思维导图太大了,我这里先直接贴前面这部分:

思维导图完整链接:https://www.processon.com/view/link/5deeefdee4b0e2c298aa5596

我大概的讲解思路也会是上面那样,从上到下,每个点细细地讲。

正文#

bean definition 是什么#

闲言少叙,进入正题。第一讲,先说说 bean definition 吧,这个东西,实在太重要了,核心的存储结构啊。

大家可以再想一想,spring 当初刚出来的时候,主打的是 ioc 容器,容器里装了啥呢,bean 啊!bean 是什么呢?

恩。。。我也不知道是啥,反正 spring 里拿出来的就是 bean。

行,那 bean 有什么特征吗?

哦,bean 是一个对象,有名字,有 class 类型,有 scope(单例、prototype 那些),有 role(属于应用的 bean、还是 spring 框架的 bean),有是否延迟初始化(lazy-init),有它依赖的其他 bean,如果这个 bean 不好造(不能直接反射生成的话),可能还有个工厂方法和工厂 bean 呢,哎,好像还说漏了,反正挺多的。

那是不是每个 bean 都有这些属性呢?

仔细想想,好像是的吧。

img

既然都有这些东西,那这个东西感觉像是个模板了,就像是最近写了年终总结,hr 小姐姐就给我们发了模板,上面姓名啊、部门啊、职位啊、述职的基本格式啊,都是固定的,我们只要拿来,填上自己的信息就行了,那我们是不是可以抽象一下,搞个 class 啊,比如下面这样:

Copy
package com.learn;

import lombok.Data;

@Data
public class SpringDefinition {

<span class="token comment">/**
 * bean class名
 */</span>
<span class="token class-name">String</span> beanClassName<span class="token punctuation">;</span>

<span class="token comment">/**
 * 工厂bean的名称
 */</span>
<span class="token class-name">String</span> factoryBeanName<span class="token punctuation">;</span>

<span class="token comment">/**
 * 工厂方法的名称
 */</span>
<span class="token class-name">String</span> factoryMethodName<span class="token punctuation">;</span>

<span class="token comment">/**
 * singleton/prototype等
 */</span>
<span class="token class-name">String</span> scope<span class="token punctuation">;</span>

<span class="token comment">/**
 * 是否延迟初始化
 */</span>
<span class="token keyword">boolean</span> isLazyInit<span class="token punctuation">;</span>

<span class="token comment">/**
 * 依赖的bean
 */</span>
<span class="token class-name">String</span><span class="token punctuation">[</span><span class="token punctuation">]</span> dependsOn<span class="token punctuation">;</span>

<span class="token comment">/**
 * bean的角色,比如:1:框架;2:应用
 */</span>
<span class="token keyword">int</span> role<span class="token punctuation">;</span>

<span class="token comment">/**
 * 是否为主候选bean
 */</span>
<span class="token keyword">boolean</span> primary<span class="token punctuation">;</span>

<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>其他属性

}

实话说,是可以的,一些简单的,轻量级 ioc 容器就是这么玩的,但是 spring 作为优秀代码的代表,肯定不能这么 low,接口的抽象性要好得多,方便我们替换不同的实现,该用接口来抽象,肯定要抽象为接口。

下边我们就看看该接口, 先看接口的描述:

Copy
* A BeanDefinition describes a bean instance, which has property values, * constructor argument values, and further information supplied by * concrete implementations. * * <p>This is just a minimal interface: The main intention is to allow a * {@link BeanFactoryPostProcessor} such as {@link PropertyPlaceholderConfigurer} * to introspect and modify property values and other bean metadata. * @author Juergen Hoeller * @author Rob Harrop * @since 19.03.2004

这里说的是,bean definition 描述一个 bean 实例的各种属性,尤其声明了:这是一个最小化接口,主要目的是允许 bean factory 后置处理器,对 bean property 和其他元数据进行修改。而且,这是 2004 年的接口,可想而知,是多么核心的 api 了。

再看看具体定义的方法,更好理解:

Copy
public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
<span class="token comment">/**
 * Return the name of the parent definition of this bean definition, if any.
 */</span>
<span class="token class-name">String</span> <span class="token function">getParentName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">/**
 * Set the name of the parent definition of this bean definition, if any.
 */</span>
<span class="token keyword">void</span> <span class="token function">setParentName</span><span class="token punctuation">(</span><span class="token class-name">String</span> parentName<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">/**
 * Return the current bean class name of this bean definition.
 * &lt;p&gt;Note that this does not have to be the actual class name used at runtime, in
 * case of a child definition overriding/inheriting the class name from its parent.
 * Hence, do &lt;i&gt;not&lt;/i&gt; consider this to be the definitive bean type at runtime but
 * rather only use it for parsing purposes at the individual bean definition level.
 */</span>
<span class="token class-name">String</span> <span class="token function">getBeanClassName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">/**
 * Override the bean class name of this bean definition.
 * &lt;p&gt;The class name can be modified during bean factory post-processing,
 * typically replacing the original class name with a parsed variant of it.
 */</span>
<span class="token keyword">void</span> <span class="token function">setBeanClassName</span><span class="token punctuation">(</span><span class="token class-name">String</span> beanClassName<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">/**
 * Return the factory bean name, if any.
 */</span>
<span class="token class-name">String</span> <span class="token function">getFactoryBeanName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">/**
 * Specify the factory bean to use, if any.
 */</span>
<span class="token keyword">void</span> <span class="token function">setFactoryBeanName</span><span class="token punctuation">(</span><span class="token class-name">String</span> factoryBeanName<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">/**
 * Return a factory method, if any.
 */</span>
<span class="token class-name">String</span> <span class="token function">getFactoryMethodName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">/**
 * Specify a factory method, if any. This method will be invoked with
 * constructor arguments, or with no arguments if none are specified.
 * The method will be invoked on the specified factory bean, if any,
 * or otherwise as a static method on the local bean class.
 * @param factoryMethodName static factory method name,
 * or {@code null} if normal constructor creation should be used
 * @see #getBeanClassName()
 */</span>
<span class="token keyword">void</span> <span class="token function">setFactoryMethodName</span><span class="token punctuation">(</span><span class="token class-name">String</span> factoryMethodName<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">/**
 * Return the name of the current target scope for this bean,
 * or {@code null} if not known yet.
 */</span>
<span class="token class-name">String</span> <span class="token function">getScope</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">/**
 * Override the target scope of this bean, specifying a new scope name.
 * @see #SCOPE_SINGLETON
 * @see #SCOPE_PROTOTYPE
 */</span>
<span class="token keyword">void</span> <span class="token function">setScope</span><span class="token punctuation">(</span><span class="token class-name">String</span> scope<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">/**
 * Return whether this bean should be lazily initialized, i.e. not
 * eagerly instantiated on startup. Only applicable to a singleton bean.
 */</span>
<span class="token keyword">boolean</span> <span class="token function">isLazyInit</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">/**
 * Set whether this bean should be lazily initialized.
 * &lt;p&gt;If {@code false}, the bean will get instantiated on startup by bean
 * factories that perform eager initialization of singletons.
 */</span>
<span class="token keyword">void</span> <span class="token function">setLazyInit</span><span class="token punctuation">(</span><span class="token keyword">boolean</span> lazyInit<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">/**
 * Return the bean names that this bean depends on.
 */</span>
<span class="token class-name">String</span><span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token function">getDependsOn</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">/**
 * Set the names of the beans that this bean depends on being initialized.
 * The bean factory will guarantee that these beans get initialized first.
 */</span>
<span class="token keyword">void</span> <span class="token function">setDependsOn</span><span class="token punctuation">(</span><span class="token class-name">String</span><span class="token punctuation">[</span><span class="token punctuation">]</span> dependsOn<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">/**
 * Return whether this bean is a candidate for getting autowired into some other bean.
 */</span>
<span class="token keyword">boolean</span> <span class="token function">isAutowireCandidate</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">/**
 * Set whether this bean is a candidate for getting autowired into some other bean.
 */</span>
<span class="token keyword">void</span> <span class="token function">setAutowireCandidate</span><span class="token punctuation">(</span><span class="token keyword">boolean</span> autowireCandidate<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">/**
 * Return whether this bean is a primary autowire candidate.
 * If this value is true for exactly one bean among multiple
 * matching candidates, it will serve as a tie-breaker.
 */</span>
<span class="token keyword">boolean</span> <span class="token function">isPrimary</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">/**
 * Set whether this bean is a primary autowire candidate.
 * &lt;p&gt;If this value is true for exactly one bean among multiple
 * matching candidates, it will serve as a tie-breaker.
 */</span>
<span class="token keyword">void</span> <span class="token function">setPrimary</span><span class="token punctuation">(</span><span class="token keyword">boolean</span> primary<span class="token punctuation">)</span><span class="token punctuation">;</span>


<span class="token comment">/**
 * Return the constructor argument values for this bean.
 * &lt;p&gt;The returned instance can be modified during bean factory post-processing.
 * @return the ConstructorArgumentValues object (never {@code null})
 */</span>
<span class="token class-name">ConstructorArgumentValues</span> <span class="token function">getConstructorArgumentValues</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">/**
 * Return the property values to be applied to a new instance of the bean.
 * &lt;p&gt;The returned instance can be modified during bean factory post-processing.
 * @return the MutablePropertyValues object (never {@code null})
 */</span>
<span class="token class-name">MutablePropertyValues</span> <span class="token function">getPropertyValues</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>


<span class="token comment">/**
 * Return whether this a &lt;b&gt;Singleton&lt;/b&gt;, with a single, shared instance
 * returned on all calls.
 * @see #SCOPE_SINGLETON
 */</span>
<span class="token keyword">boolean</span> <span class="token function">isSingleton</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">/**
 * Return whether this a &lt;b&gt;Prototype&lt;/b&gt;, with an independent instance
 * returned for each call.
 * @see #SCOPE_PROTOTYPE
 */</span>
<span class="token keyword">boolean</span> <span class="token function">isPrototype</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">/**
 * Return whether this bean is "abstract", that is, not meant to be instantiated.
 */</span>
<span class="token keyword">boolean</span> <span class="token function">isAbstract</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">/**
 * Get the role hint for this {@code BeanDefinition}. The role hint
 * provides tools with an indication of the importance of a particular
 * {@code BeanDefinition}.
 * @see #ROLE_APPLICATION
 * @see #ROLE_INFRASTRUCTURE
 * @see #ROLE_SUPPORT
 */</span>
<span class="token keyword">int</span> <span class="token function">getRole</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">/**
 * Return a human-readable description of this bean definition.
 */</span>
<span class="token class-name">String</span> <span class="token function">getDescription</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">/**
 * Return a description of the resource that this bean definition
 * came from (for the purpose of showing context in case of errors).
 */</span>
<span class="token class-name">String</span> <span class="token function">getResourceDescription</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">/**
 * Return the originating BeanDefinition, or {@code null} if none.
 * Allows for retrieving the decorated bean definition, if any.
 * &lt;p&gt;Note that this method returns the immediate originator. Iterate through the
 * originator chain to find the original BeanDefinition as defined by the user.
 */</span>
<span class="token class-name">BeanDefinition</span> <span class="token function">getOriginatingBeanDefinition</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

}

大家仔细看看,是不是其实和我们定义的 class 差不多呢,主要都是一些 get/set 方法。里面的字段呢,下一讲我们详细讲解一下,会结合一些融会贯通的地方。

bean definition 接口的实现有哪些#

然后我们看看这个接口有哪些实现吧?

可以看到,这里有两个是我标红了,因为他们特殊,特殊在他们不属于 spring-beans 包,而是在 spring-context 包里。后边遇到了我们再单说,这里存疑。

再来看看这个接口的继承图:

可以获取注解信息的子接口 AnnotatedBeanDefinition#

我们看到,这个接口有一个子接口,是AnnotatedBeanDefinition。这个接口定义如下:

Copy
/** * Extended {@link org.springframework.beans.factory.config.BeanDefinition} * interface that exposes {@link org.springframework.core.type.AnnotationMetadata} * about its bean class - without requiring the class to be loaded yet. * 这个接口扩展了 BeanDefinition,可以获得 bean definition 中的 bean class 上的注解元数据。 * 举个例子,假设我们用 @controller 标注了某个类,那这里就能获取到 @controller 这个注解里面的信息 * * @author Juergen Hoeller * @since 2.5 * @see AnnotatedGenericBeanDefinition * @see org.springframework.core.type.AnnotationMetadata */ public interface AnnotatedBeanDefinition extends BeanDefinition {
<span class="token comment">/**
 * Obtain the annotation metadata (as well as basic class metadata)
 * for this bean definition's bean class.
 * @return the annotation metadata object (never {@code null})
 */</span>
<span class="token class-name">AnnotationMetadata</span> <span class="token function">getMetadata</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

}

可以想一想有什么用,这个接口能取到 bean definition 中对应 bean class 上标注的注解元数据。

比如下面的 controller 举例:

Copy
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface Controller {
<span class="token comment">/**
 * The value may indicate a suggestion for a logical component name,
 * to be turned into a Spring bean in case of an autodetected component.
 * @return the suggested component name, if any
 */</span>
<span class="token class-name">String</span> <span class="token function">value</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">default</span> <span class="token string">""</span><span class="token punctuation">;</span>

}

那这个AnnotatedBeanDefinition就能取到 controller 中的 value 字段的值。

我这里也写了个简单的例子,如下:

Copy
@Component("testService") public class HelloWorldService {

}

Copy
@Autowired private ApplicationContext applicationContext; @Override public void run(String... args) { DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) applicationContext.getAutowireCapableBeanFactory(); // 获取 bean definition,然后获取其注解元数据 AnnotatedBeanDefinition annotatedBeanDefinition = (AnnotatedBeanDefinition) beanFactory.getBeanDefinition("testService"); AnnotationMetadata metadata = annotatedBeanDefinition.getMetadata(); Map<String, Object> annotationAttributes = metadata.getAnnotationAttributes("org.springframework.stereotype.Component"); log.info("annotationAttributes:{}",annotationAttributes); }

我这边打印出来就是:

信息: annotationAttributes:

代码在

https://gitee.com/ckl111/spring-boot-first-version-learn/tree/master/all-demo-in-spring-learning/spring-bootstrap-bean-definition-demo

接口下的实现类#

仔细看了两个接口 AnnotatedBeanDefinitionBeanDefinition,其实实现类都是差不多那几个。

基本上org.springframework.beans.factory.support.AbstractBeanDefinition充当了基本的实现,基本上,该实现的方法都实现了,除了一个:

Copy
/** * Clone this bean definition. * To be implemented by concrete subclasses. * @return the cloned bean definition object */ public abstract AbstractBeanDefinition cloneBeanDefinition();

赶紧这个方法,对我们分析也没多大帮助,暂时跳过即可。

再看看org.springframework.beans.factory.support.GenericBeanDefinition,感觉很重要,我们看看:

Copy
public class GenericBeanDefinition extends AbstractBeanDefinition {
<span class="token keyword">private</span> <span class="token class-name">String</span> parentName<span class="token punctuation">;</span>


<span class="token comment">/**
 * 这里有点意思,类似于builder模式,先生成一个实例,再自己各种set方法设置相关属性
 * 
 * Create a new GenericBeanDefinition, to be configured through its bean
 * properties and configuration methods.
 * @see #setBeanClass
 * @see #setBeanClassName
 * @see #setScope
 * @see #setAutowireMode
 * @see #setDependencyCheck
 * @see #setConstructorArgumentValues
 * @see #setPropertyValues
 */</span>
<span class="token keyword">public</span> <span class="token class-name">GenericBeanDefinition</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
	<span class="token keyword">super</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>

<span class="token annotation punctuation">@Override</span>
<span class="token keyword">public</span> <span class="token class-name">AbstractBeanDefinition</span> <span class="token function">cloneBeanDefinition</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
	<span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">GenericBeanDefinition</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

}

ok,都这么简单的话,就再看两个,spring beans 包中的org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition:

Copy
public class AnnotatedGenericBeanDefinition extends GenericBeanDefinition implements AnnotatedBeanDefinition {
<span class="token keyword">private</span> <span class="token keyword">final</span> <span class="token class-name">AnnotationMetadata</span> metadata<span class="token punctuation">;</span>


<span class="token comment">/**
 * Create a new AnnotatedGenericBeanDefinition for the given bean class.
 * @param beanClass the loaded bean class
 */</span>
<span class="token keyword">public</span> <span class="token class-name">AnnotatedGenericBeanDefinition</span><span class="token punctuation">(</span><span class="token class-name">Class</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token operator">?</span><span class="token punctuation">&gt;</span></span> beanClass<span class="token punctuation">)</span> <span class="token punctuation">{</span>
	<span class="token function">setBeanClass</span><span class="token punctuation">(</span>beanClass<span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token keyword">this</span><span class="token punctuation">.</span>metadata <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">StandardAnnotationMetadata</span><span class="token punctuation">(</span>beanClass<span class="token punctuation">,</span> <span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token comment">/**
 * Create a new AnnotatedGenericBeanDefinition for the given annotation metadata,
 * allowing for ASM-based processing and avoidance of early loading of the bean class.
 * Note that this constructor is functionally equivalent to
 * {@link org.springframework.context.annotation.ScannedGenericBeanDefinition
 * ScannedGenericBeanDefinition}, however the semantics of the latter indicate that
 * a bean was discovered specifically via component-scanning as opposed to other
 * means.
 * @param metadata the annotation metadata for the bean class in question
 * @since 3.1.1
 */</span>
<span class="token keyword">public</span> <span class="token class-name">AnnotatedGenericBeanDefinition</span><span class="token punctuation">(</span><span class="token class-name">AnnotationMetadata</span> metadata<span class="token punctuation">)</span> <span class="token punctuation">{</span>
	<span class="token class-name">Assert</span><span class="token punctuation">.</span><span class="token function">notNull</span><span class="token punctuation">(</span>metadata<span class="token punctuation">,</span> <span class="token string">"AnnotationMetadata must not be null"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token function">setBeanClassName</span><span class="token punctuation">(</span>metadata<span class="token punctuation">.</span><span class="token function">getClassName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token keyword">this</span><span class="token punctuation">.</span>metadata <span class="token operator">=</span> metadata<span class="token punctuation">;</span>
<span class="token punctuation">}</span>


<span class="token keyword">public</span> <span class="token keyword">final</span> <span class="token class-name">AnnotationMetadata</span> <span class="token function">getMetadata</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
	 <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">.</span>metadata<span class="token punctuation">;</span>
<span class="token punctuation">}</span>

}

很简单,就是多了获取 bean class 的注解的功能。

再看这个呢,

Copy
/** * Extension of the {@link GenericBeanDefinition} * class, based on an ASM ClassReader, with support for annotation metadata exposed * through the {@link AnnotatedBeanDefinition} interface. * * <p>This class does <i>not</i> load the bean {@code Class} early. * It rather retrieves all relevant metadata from the ".class" file itself, * parsed with the ASM ClassReader. It is functionally equivalent to * {@link AnnotatedGenericBeanDefinition#AnnotatedGenericBeanDefinition(AnnotationMetadata)} * but distinguishes by type beans that have been <em>scanned</em> vs those that have * been otherwise registered or detected by other means. * * @author Juergen Hoeller * @author Chris Beams * @since 2.5 * @see #getMetadata()* @see #getBeanClassName() * @see org.springframework.core.type.classreading.MetadataReaderFactory * @see AnnotatedGenericBeanDefinition */ @SuppressWarnings("serial") public class ScannedGenericBeanDefinition extends GenericBeanDefinition implements AnnotatedBeanDefinition {
<span class="token keyword">private</span> <span class="token keyword">final</span> <span class="token class-name">AnnotationMetadata</span> metadata<span class="token punctuation">;</span>


<span class="token comment">/**
 * Create a new ScannedGenericBeanDefinition for the class that the
 * given MetadataReader describes.
 * @param metadataReader the MetadataReader for the scanned target class
 */</span>
<span class="token keyword">public</span> <span class="token class-name">ScannedGenericBeanDefinition</span><span class="token punctuation">(</span><span class="token class-name">MetadataReader</span> metadataReader<span class="token punctuation">)</span> <span class="token punctuation">{</span>
	<span class="token class-name">Assert</span><span class="token punctuation">.</span><span class="token function">notNull</span><span class="token punctuation">(</span>metadataReader<span class="token punctuation">,</span> <span class="token string">"MetadataReader must not be null"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token keyword">this</span><span class="token punctuation">.</span>metadata <span class="token operator">=</span> metadataReader<span class="token punctuation">.</span><span class="token function">getAnnotationMetadata</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token function">setBeanClassName</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>metadata<span class="token punctuation">.</span><span class="token function">getClassName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>


<span class="token keyword">public</span> <span class="token keyword">final</span> <span class="token class-name">AnnotationMetadata</span> <span class="token function">getMetadata</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
	<span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">.</span>metadata<span class="token punctuation">;</span>
<span class="token punctuation">}</span>

}

我一开始,一眼看过去,感觉眼花了,差不多啊,但这个是在 spring context 包里,然后,可以看上面的注释,说是使用 asm 去获取注解信息。所以,这个和上面那个的差别是:

org.springframework.context.annotation.ScannedGenericBeanDefinition 位于 spring-context,使用 asm

org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition 位于 spring-beans,使用反射

再看看org.springframework.beans.factory.support.RootBeanDefinition(位于 spring-beans),这个类下面只有一个子类,位于 spring-context 的

org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.ConfigurationClassBeanDefinition

这两个类,一看就比较特别,能看出来,和 @configuration 注解有莫大关系,这个我们放后面讲。

总结#

本篇就先到这里,留了一些问题,放到后面(有些我也要查一下,哈哈)。下一讲继续。