learn.lianglianglee.com/文章/Spring中眼花缭乱的BeanDefinition.md.html
2022-09-06 22:30:37 +08:00

846 lines
43 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<!-- saved from url=(0046)https://kaiiiz.github.io/hexo-theme-book-demo/ -->
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no">
<link rel="icon" href="/static/favicon.png">
<title>Spring中眼花缭乱的BeanDefinition.md.html</title>
<!-- Spectre.css framework -->
<link rel="stylesheet" href="/static/index.css">
<!-- theme css & js -->
<meta name="generator" content="Hexo 4.2.0">
</head>
<body>
<div class="book-container">
<div class="book-sidebar">
<div class="book-brand">
<a href="/">
<img src="/static/favicon.png">
<span>技术文章摘抄</span>
</a>
</div>
<div class="book-menu uncollapsible">
<ul class="uncollapsible">
<li><a href="/" class="current-tab">首页</a></li>
</ul>
<ul class="uncollapsible">
<li><a href="../">上一级</a></li>
</ul>
<ul class="uncollapsible">
<li>
<a href="/文章/AQS 万字图文全面解析.md.html">AQS 万字图文全面解析</a>
</li>
<li>
<a href="/文章/Docker 镜像构建原理及源码分析.md.html">Docker 镜像构建原理及源码分析</a>
</li>
<li>
<a href="/文章/ElasticSearch 小白从入门到精通.md.html">ElasticSearch 小白从入门到精通</a>
</li>
<li>
<a href="/文章/JVM CPU Profiler技术原理及源码深度解析.md.html">JVM CPU Profiler技术原理及源码深度解析</a>
</li>
<li>
<a href="/文章/JVM 垃圾收集器.md.html">JVM 垃圾收集器</a>
</li>
<li>
<a href="/文章/JVM 面试的 30 个知识点.md.html">JVM 面试的 30 个知识点</a>
</li>
<li>
<a href="/文章/Java IO 体系、线程模型大总结.md.html">Java IO 体系、线程模型大总结</a>
</li>
<li>
<a href="/文章/Java NIO浅析.md.html">Java NIO浅析</a>
</li>
<li>
<a href="/文章/Java 面试题集锦(网络篇).md.html">Java 面试题集锦(网络篇)</a>
</li>
<li>
<a href="/文章/Java-直接内存 DirectMemory 详解.md.html">Java-直接内存 DirectMemory 详解</a>
</li>
<li>
<a href="/文章/Java中9种常见的CMS GC问题分析与解决.md.html">Java中9种常见的CMS GC问题分析与解决</a>
</li>
<li>
<a href="/文章/Java中9种常见的CMS GC问题分析与解决.md.html">Java中9种常见的CMS GC问题分析与解决</a>
</li>
<li>
<a href="/文章/Java中的SPI.md.html">Java中的SPI</a>
</li>
<li>
<a href="/文章/Java中的ThreadLocal.md.html">Java中的ThreadLocal</a>
</li>
<li>
<a href="/文章/Java线程池实现原理及其在美团业务中的实践.md.html">Java线程池实现原理及其在美团业务中的实践</a>
</li>
<li>
<a href="/文章/Java魔法类Unsafe应用解析.md.html">Java魔法类Unsafe应用解析</a>
</li>
<li>
<a href="/文章/Kafka 源码阅读笔记.md.html">Kafka 源码阅读笔记</a>
</li>
<li>
<a href="/文章/Kafka、ActiveMQ、RabbitMQ、RocketMQ 区别以及高可用原理.md.html">Kafka、ActiveMQ、RabbitMQ、RocketMQ 区别以及高可用原理</a>
</li>
<li>
<a href="/文章/MySQL · 引擎特性 · InnoDB Buffer Pool.md.html">MySQL · 引擎特性 · InnoDB Buffer Pool</a>
</li>
<li>
<a href="/文章/MySQL · 引擎特性 · InnoDB IO子系统.md.html">MySQL · 引擎特性 · InnoDB IO子系统</a>
</li>
<li>
<a href="/文章/MySQL · 引擎特性 · InnoDB 事务系统.md.html">MySQL · 引擎特性 · InnoDB 事务系统</a>
</li>
<li>
<a href="/文章/MySQL · 引擎特性 · InnoDB 同步机制.md.html">MySQL · 引擎特性 · InnoDB 同步机制</a>
</li>
<li>
<a href="/文章/MySQL · 引擎特性 · InnoDB 数据页解析.md.html">MySQL · 引擎特性 · InnoDB 数据页解析</a>
</li>
<li>
<a href="/文章/MySQL · 引擎特性 · InnoDB崩溃恢复.md.html">MySQL · 引擎特性 · InnoDB崩溃恢复</a>
</li>
<li>
<a href="/文章/MySQL · 引擎特性 · 临时表那些事儿.md.html">MySQL · 引擎特性 · 临时表那些事儿</a>
</li>
<li>
<a href="/文章/MySQL 主从复制 半同步复制.md.html">MySQL 主从复制 半同步复制</a>
</li>
<li>
<a href="/文章/MySQL 主从复制 基于GTID复制.md.html">MySQL 主从复制 基于GTID复制</a>
</li>
<li>
<a href="/文章/MySQL 主从复制.md.html">MySQL 主从复制</a>
</li>
<li>
<a href="/文章/MySQL 事务日志(redo log和undo log).md.html">MySQL 事务日志(redo log和undo log)</a>
</li>
<li>
<a href="/文章/MySQL 亿级别数据迁移实战代码分享.md.html">MySQL 亿级别数据迁移实战代码分享</a>
</li>
<li>
<a href="/文章/MySQL 从一条数据说起-InnoDB行存储数据结构.md.html">MySQL 从一条数据说起-InnoDB行存储数据结构</a>
</li>
<li>
<a href="/文章/MySQL 地基基础:事务和锁的面纱.md.html">MySQL 地基基础:事务和锁的面纱</a>
</li>
<li>
<a href="/文章/MySQL 地基基础:数据字典.md.html">MySQL 地基基础:数据字典</a>
</li>
<li>
<a href="/文章/MySQL 地基基础:数据库字符集.md.html">MySQL 地基基础:数据库字符集</a>
</li>
<li>
<a href="/文章/MySQL 性能优化:碎片整理.md.html">MySQL 性能优化:碎片整理</a>
</li>
<li>
<a href="/文章/MySQL 故障诊断:一个 ALTER TALBE 执行了很久,你慌不慌?.md.html">MySQL 故障诊断:一个 ALTER TALBE 执行了很久,你慌不慌?</a>
</li>
<li>
<a href="/文章/MySQL 故障诊断:如何在日志中轻松定位大事务.md.html">MySQL 故障诊断:如何在日志中轻松定位大事务</a>
</li>
<li>
<a href="/文章/MySQL 故障诊断:教你快速定位加锁的 SQL.md.html">MySQL 故障诊断:教你快速定位加锁的 SQL</a>
</li>
<li>
<a href="/文章/MySQL 日志详解.md.html">MySQL 日志详解</a>
</li>
<li>
<a href="/文章/MySQL 的半同步是什么?.md.html">MySQL 的半同步是什么?</a>
</li>
<li>
<a href="/文章/MySQL中的事务和MVCC.md.html">MySQL中的事务和MVCC</a>
</li>
<li>
<a href="/文章/MySQL事务_事务隔离级别详解.md.html">MySQL事务_事务隔离级别详解</a>
</li>
<li>
<a href="/文章/MySQL优化优化 select count().md.html">MySQL优化优化 select count()</a>
</li>
<li>
<a href="/文章/MySQL共享锁、排他锁、悲观锁、乐观锁.md.html">MySQL共享锁、排他锁、悲观锁、乐观锁</a>
</li>
<li>
<a href="/文章/MySQL的MVCC多版本并发控制.md.html">MySQL的MVCC多版本并发控制</a>
</li>
<li>
<a href="/文章/QingStor 对象存储架构设计及最佳实践.md.html">QingStor 对象存储架构设计及最佳实践</a>
</li>
<li>
<a href="/文章/RocketMQ 面试题集锦.md.html">RocketMQ 面试题集锦</a>
</li>
<li>
<a href="/文章/SnowFlake 雪花算法生成分布式 ID.md.html">SnowFlake 雪花算法生成分布式 ID</a>
</li>
<li>
<a href="/文章/Spring Boot 2.x 结合 k8s 实现分布式微服务架构.md.html">Spring Boot 2.x 结合 k8s 实现分布式微服务架构</a>
</li>
<li>
<a href="/文章/Spring Boot 教程:如何开发一个 starter.md.html">Spring Boot 教程:如何开发一个 starter</a>
</li>
<li>
<a href="/文章/Spring MVC 原理.md.html">Spring MVC 原理</a>
</li>
<li>
<a href="/文章/Spring MyBatis和Spring整合的奥秘.md.html">Spring MyBatis和Spring整合的奥秘</a>
</li>
<li>
<a href="/文章/Spring 帮助你更好的理解Spring循环依赖.md.html">Spring 帮助你更好的理解Spring循环依赖</a>
</li>
<li>
<a href="/文章/Spring 循环依赖及解决方式.md.html">Spring 循环依赖及解决方式</a>
</li>
<li>
<a class="current-tab" href="/文章/Spring中眼花缭乱的BeanDefinition.md.html">Spring中眼花缭乱的BeanDefinition</a>
</li>
<li>
<a href="/文章/Vert.x 基础入门.md.html">Vert.x 基础入门</a>
</li>
<li>
<a href="/文章/eBay 的 Elasticsearch 性能调优实践.md.html">eBay 的 Elasticsearch 性能调优实践</a>
</li>
<li>
<a href="/文章/不可不说的Java“锁”事.md.html">不可不说的Java“锁”事</a>
</li>
<li>
<a href="/文章/互联网并发限流实战.md.html">互联网并发限流实战</a>
</li>
<li>
<a href="/文章/从ReentrantLock的实现看AQS的原理及应用.md.html">从ReentrantLock的实现看AQS的原理及应用</a>
</li>
<li>
<a href="/文章/从SpringCloud开始聊微服务架构.md.html">从SpringCloud开始聊微服务架构</a>
</li>
<li>
<a href="/文章/全面了解 JDK 线程池实现原理.md.html">全面了解 JDK 线程池实现原理</a>
</li>
<li>
<a href="/文章/分布式一致性理论与算法.md.html">分布式一致性理论与算法</a>
</li>
<li>
<a href="/文章/分布式一致性算法 Raft.md.html">分布式一致性算法 Raft</a>
</li>
<li>
<a href="/文章/分布式唯一 ID 解析.md.html">分布式唯一 ID 解析</a>
</li>
<li>
<a href="/文章/分布式链路追踪:集群管理设计.md.html">分布式链路追踪:集群管理设计</a>
</li>
<li>
<a href="/文章/动态代理种类及原理,你知道多少?.md.html">动态代理种类及原理,你知道多少?</a>
</li>
<li>
<a href="/文章/响应式架构与 RxJava 在有赞零售的实践.md.html">响应式架构与 RxJava 在有赞零售的实践</a>
</li>
<li>
<a href="/文章/大数据算法——布隆过滤器.md.html">大数据算法——布隆过滤器</a>
</li>
<li>
<a href="/文章/如何优雅地记录操作日志?.md.html">如何优雅地记录操作日志?</a>
</li>
<li>
<a href="/文章/如何设计一个亿级消息量的 IM 系统.md.html">如何设计一个亿级消息量的 IM 系统</a>
</li>
<li>
<a href="/文章/异步网络模型.md.html">异步网络模型</a>
</li>
<li>
<a href="/文章/当我们在讨论CQRS时我们在讨论些神马.md.html">当我们在讨论CQRS时我们在讨论些神马</a>
</li>
<li>
<a href="/文章/彻底理解 MySQL 的索引机制.md.html">彻底理解 MySQL 的索引机制</a>
</li>
<li>
<a href="/文章/最全的 116 道 Redis 面试题解答.md.html">最全的 116 道 Redis 面试题解答</a>
</li>
<li>
<a href="/文章/有赞权限系统(SAM).md.html">有赞权限系统(SAM)</a>
</li>
<li>
<a href="/文章/有赞零售中台建设方法的探索与实践.md.html">有赞零售中台建设方法的探索与实践</a>
</li>
<li>
<a href="/文章/服务注册与发现原理剖析Eureka、Zookeeper、Nacos.md.html">服务注册与发现原理剖析Eureka、Zookeeper、Nacos</a>
</li>
<li>
<a href="/文章/深入浅出Cache.md.html">深入浅出Cache</a>
</li>
<li>
<a href="/文章/深入理解 MySQL 底层实现.md.html">深入理解 MySQL 底层实现</a>
</li>
<li>
<a href="/文章/漫画讲解 git rebase VS git merge.md.html">漫画讲解 git rebase VS git merge</a>
</li>
<li>
<a href="/文章/生成浏览器唯一稳定 ID 的探索.md.html">生成浏览器唯一稳定 ID 的探索</a>
</li>
<li>
<a href="/文章/缓存 如何保证缓存与数据库的双写一致性?.md.html">缓存 如何保证缓存与数据库的双写一致性?</a>
</li>
<li>
<a href="/文章/网易严选怎么做全链路监控的?.md.html">网易严选怎么做全链路监控的?</a>
</li>
<li>
<a href="/文章/美团万亿级 KV 存储架构与实践.md.html">美团万亿级 KV 存储架构与实践</a>
</li>
<li>
<a href="/文章/美团点评Kubernetes集群管理实践.md.html">美团点评Kubernetes集群管理实践</a>
</li>
<li>
<a href="/文章/美团百亿规模API网关服务Shepherd的设计与实现.md.html">美团百亿规模API网关服务Shepherd的设计与实现</a>
</li>
<li>
<a href="/文章/解读《阿里巴巴 Java 开发手册》背后的思考.md.html">解读《阿里巴巴 Java 开发手册》背后的思考</a>
</li>
<li>
<a href="/文章/认识 MySQL 和 Redis 的数据一致性问题.md.html">认识 MySQL 和 Redis 的数据一致性问题</a>
</li>
<li>
<a href="/文章/进阶Dockerfile 高阶使用指南及镜像优化.md.html">进阶Dockerfile 高阶使用指南及镜像优化</a>
</li>
<li>
<a href="/文章/铁总在用的高性能分布式缓存计算框架 Geode.md.html">铁总在用的高性能分布式缓存计算框架 Geode</a>
</li>
<li>
<a href="/文章/阿里云PolarDB及其共享存储PolarFS技术实现分析.md.html">阿里云PolarDB及其共享存储PolarFS技术实现分析</a>
</li>
<li>
<a href="/文章/阿里云PolarDB及其共享存储PolarFS技术实现分析.md.html">阿里云PolarDB及其共享存储PolarFS技术实现分析</a>
</li>
<li>
<a href="/文章/面试最常被问的 Java 后端题.md.html">面试最常被问的 Java 后端题</a>
</li>
<li>
<a href="/文章/领域驱动设计在互联网业务开发中的实践.md.html">领域驱动设计在互联网业务开发中的实践</a>
</li>
<li>
<a href="/文章/领域驱动设计的菱形对称架构.md.html">领域驱动设计的菱形对称架构</a>
</li>
<li>
<a href="/文章/高效构建 Docker 镜像的最佳实践.md.html">高效构建 Docker 镜像的最佳实践</a>
</li>
</ul>
</div>
</div>
<div class="sidebar-toggle" onclick="sidebar_toggle()" onmouseover="add_inner()" onmouseleave="remove_inner()">
<div class="sidebar-toggle-inner"></div>
</div>
<script>
function add_inner() {
let inner = document.querySelector('.sidebar-toggle-inner')
inner.classList.add('show')
}
function remove_inner() {
let inner = document.querySelector('.sidebar-toggle-inner')
inner.classList.remove('show')
}
function sidebar_toggle() {
let sidebar_toggle = document.querySelector('.sidebar-toggle')
let sidebar = document.querySelector('.book-sidebar')
let content = document.querySelector('.off-canvas-content')
if (sidebar_toggle.classList.contains('extend')) { // show
sidebar_toggle.classList.remove('extend')
sidebar.classList.remove('hide')
content.classList.remove('extend')
} else { // hide
sidebar_toggle.classList.add('extend')
sidebar.classList.add('hide')
content.classList.add('extend')
}
}
function open_sidebar() {
let sidebar = document.querySelector('.book-sidebar')
let overlay = document.querySelector('.off-canvas-overlay')
sidebar.classList.add('show')
overlay.classList.add('show')
}
function hide_canvas() {
let sidebar = document.querySelector('.book-sidebar')
let overlay = document.querySelector('.off-canvas-overlay')
sidebar.classList.remove('show')
overlay.classList.remove('show')
}
</script>
<div class="off-canvas-content">
<div class="columns">
<div class="column col-12 col-lg-12">
<div class="book-navbar">
<!-- For Responsive Layout -->
<header class="navbar">
<section class="navbar-section">
<a onclick="open_sidebar()">
<i class="icon icon-menu"></i>
</a>
</section>
</header>
</div>
<div class="book-content" style="max-width: 960px; margin: 0 auto;
overflow-x: auto;
overflow-y: hidden;">
<div class="book-post">
<p id="tip" align="center"></p>
<div><h1>Spring中眼花缭乱的BeanDefinition</h1>
<h4>引入主题</h4>
<p>为什么要读Spring源码有的人为了学习Spring中的先进思想也有的人是为了更好的理解设计模式当然也有很大一部分小伙伴是为了应付面试Spring Bean的生命周期啦Spring AOP的原理啦Spring IoC的原理啦应付面试看几篇博客对照着看看源码应该就没什么问题了但是如果想真正的玩懂Spring需要花的时间真的很多需要你沉下心从最基础的看起今天我们就来看看Spring中的基础——BeanDefinition。</p>
<h4>什么是BeanDefinition</h4>
<p><img src="assets/15100432-b6f3e81ca4e0b5b4.png" alt="png" />
Spring官网中有详细的说明我们来翻译下
SpringIoc容器管理一个Bean或多个Bean这些Bean通过我们提供给容器的配置元数据被创建出来例如在xml中的定义
在容器中这些Bean的定义用BeanDefinition对象来表示包含以下元数据</p>
<ul>
<li>全限定类名, 通常是Bean的实际实现类</li>
<li>Bean行为配置元素它们说明Bean在容器中的行为作用域、生命周期回调等等</li>
<li>Bean执行工作所需要的的其他Bean的引用这些Bean也称为协作者或依赖项</li>
<li>其他配置信息例如管理连接池的bean中限制池的大小或者使用的连接的数量。</li>
</ul>
<p>Spring官网中对BeanDefinition的解释还是很详细的但是不是那么通俗易懂其实BeanDefinition是比较容易解释的BeanDefinition就是用来描述一个Bean或者BeanDefinition就是Bean的定义。</p>
<p>创建一个Java Bean大概是下面这个酱紫
<img src="assets/15100432-87de503f024f272f.png" alt="png" />
我们写的Java文件会编译为Class文件运行程序类加载器会加载Class文件放入JVM的方法区我们就可以愉快的new对象了。</p>
<p>创建一个Spring Bean大概是下面这个酱紫
<img src="assets/15100432-4e677493ee644264.png" alt="png" />
我们写的Java文件会编译为Class文件运行程序类加载器会加载Class文件放入JVM的方法区这一步还是保持不变当然这个也没办法变。。。
下面就是Spring的事情了Spring会解析我们的配置类配置文件假设现在只配置了A解析后Spring会把A的BeanDefinition放到一个map中去随后由一个一个的BeanPostProcessor进行加工最终把经历了完整的Spring生命周期的Bean放入了singleObjects。</p>
<h4>BeanDefinition类图鸟瞰</h4>
<p><img src="assets/15100432-18d3be0ba6b0eda8.png" alt="png" />
大家可以看到Spring中BeanDefinition的类图还是相当复杂的我刚开始读Spring源码的时候觉得BeanDefinition应该是一个特别简单的东西但是后面发觉并不是那么回事。</p>
<p>下面我将对涉及到的类逐个进行解读。</p>
<h4>AttributeAccessor</h4>
<p>AttributeAccessor是一个接口</p>
<pre><code>/**
* Interface defining a generic contract for attaching and accessing metadata
* to/from arbitrary objects.
*
* @author Rob Harrop
* @since 2.0
*/
public interface AttributeAccessor {
void setAttribute(String name, @Nullable Object value);
Object getAttribute(String name);
Object removeAttribute(String name);
boolean hasAttribute(String name);
String[] attributeNames();
}
</code></pre>
<p>我们来看下类上面的注释:接口定义了通用的方法来保存或者读取元数据。既然是接口,那么一定会有实现类,我们先把这个放一边。</p>
<h4>BeanMetadataElement</h4>
<p>BeanMetadataElement也是一个接口里面只定义了一个方法</p>
<pre><code>/**
* Interface to be implemented by bean metadata elements
* that carry a configuration source object.
*
* @author Juergen Hoeller
* @since 2.0
*/
public interface BeanMetadataElement {
@Nullable
Object getSource();
}
</code></pre>
<p>我们还是来看下类上的注释接口提供了一个方法来获取Bean的源对象这个源对象就是源文件怎么样是不是不太好理解没关系我们马上写个代码来看下</p>
<pre><code>@Configuration
@ComponentScan
public class AppConfig {
}
@Service
public class BookService {
}
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
System.out.println(context.getBeanDefinition(&quot;bookService&quot;).getSource());
}
}
file [D:\cycleinject\target\classes\com\codebear\springcycle\BookService.class]
</code></pre>
<p>怎么样,现在理解了把。</p>
<h4>AttributeAccessorSupport</h4>
<p>AttributeAccessorSupport类是一个抽象类实现了AttributeAccessor接口这个AttributeAccessor还记得吧里面定义了通用的方法来保存或者读取元数据的虚方法AttributeAccessorSupport便实现了这个虚方法AttributeAccessorSupport定义了一个map容器元数据就被保存在这个map里面。</p>
<h5>为什么要有这个map</h5>
<p>初次读Spring源码看到这个map的时候觉得有点奇怪元数据不应该是保存在BeanDefinition的beanClass、scope、lazyInit这些字段里面吗这个map不是多次一举吗</p>
<p>后面才知道Spring是为了方便扩展不然BeanDefinition有新的特性就要新增字段这是其一其二如果程序员要扩展Spring而BeanDefinition中定义的字段已经无法满足扩展了呢</p>
<p>那Spring自己有使用这个map吗答案是有的我们来看下Spring在这个map中放了什么数据</p>
<pre><code> public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
BeanDefinition appConfig = context.getBeanDefinition(&quot;appConfig&quot;);
for (String item : appConfig.attributeNames()) {
System.out.println(item + &quot;:&quot; + appConfig.getAttribute(item));
}
}
org.springframework.context.annotation.ConfigurationClassPostProcessor.configurationClass:full
org.springframework.aop.framework.autoproxy.AutoProxyUtils.preserveTargetClass:true
</code></pre>
<p>可以看到Spring在里面放了两个item</p>
<ul>
<li>第一个item保存着这个配置类是否是一个Full配置类关于Full配置类我在先前的博客有简单的介绍过<a href="https://juejin.im/user/3544481219222488/posts">Spring中你可能不知道的事</a></li>
<li>第二个item从名字上就可以知道和AOP相关。</li>
</ul>
<h3>BeanDefinition</h3>
<p>BeanDefinition是一个接口继承了AttributeAccessor、BeanMetadataElement这两个类上面已经介绍过了。</p>
<p>BeanDefinition定义了很多方法比如setBeanClassName、getBeanClassName、setScope、getScope、setLazyInit、isLazyInit等等这些方法一眼就知道是什么意思了这里就不解释了。</p>
<h3>BeanMetadataAttributeAccessor</h3>
<p>BeanMetadataAttributeAccessor继承了AttributeAccessorSupport对保存或者读取元数据的方法进行了进一步的封装。</p>
<h3>AbstractBeanDefinition</h3>
<p>AbstractBeanDefinition是一个抽象类继承了BeanMetadataAttributeAccessor实现了BeanDefinition。</p>
<p>BeanDefinition实现了BeanDefinition定义的大部分虚方法同时定义了很多常量和默认值。</p>
<p>AbstractBeanDefinition有三个子类下面我们来看看这三个子类。</p>
<h4>ChildBeanDefinition</h4>
<p>从Spring2.5开始ChildBeanDefinition已经不再使用取而代之的是GenericBeanDefinition。</p>
<h4>GenericBeanDefinition</h4>
<p>GenericBeanDefinition替代了ChildBeanDefinitionChildBeanDefinition从字面上就可以看出有“子BeanDefinition”的意思难道BeanDefinition还有“父子关系”吗当然有。</p>
<pre><code>public class ChildService {
private int id;
private String name;
public ChildService(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class ParentService {
private int id;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public ParentService(int id, String name) {
this.id = id;
this.name = name;
}
}
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
GenericBeanDefinition parentBeanDefinition = new GenericBeanDefinition();
parentBeanDefinition.setScope(BeanDefinition.SCOPE_SINGLETON);
parentBeanDefinition.setAttribute(&quot;name&quot;, &quot;codebear&quot;);
parentBeanDefinition.setAbstract(true);
parentBeanDefinition.getConstructorArgumentValues().addGenericArgumentValue(1);
parentBeanDefinition.getConstructorArgumentValues().addGenericArgumentValue(&quot;CodeBear&quot;);
GenericBeanDefinition childBeanDefinition = new GenericBeanDefinition();
childBeanDefinition.setParentName(&quot;parent&quot;);
childBeanDefinition.setBeanClass(ChildService.class);
context.registerBeanDefinition(&quot;parent&quot;, parentBeanDefinition);
context.registerBeanDefinition(&quot;child&quot;, childBeanDefinition);
context.refresh();
BeanDefinition child = context.getBeanFactory().getMergedBeanDefinition(&quot;child&quot;);
for (String s : child.attributeNames()) {
System.out.println(s + &quot;:&quot; + child.getAttribute(s));
}
System.out.println(&quot;scope:&quot; + child.getScope());
System.out.println(&quot;-------------------&quot;);
ChildService service = context.getBean(ChildService.class);
System.out.println(service.getName());
System.out.println(service.getId());
}
</code></pre>
<p>运行结果:</p>
<pre><code>name:codebear
scope:singleton
-------------------
CodeBear
1
</code></pre>
<p>来分析下代码:</p>
<ol>
<li>创建了GenericBeanDefinition对象parentBeanDefinition设置为了单例模式设置了Attribute声明了构造方法的两个参数值</li>
<li>创建了GenericBeanDefinition对象childBeanDefinition设置parentName为parentBeanClass为ChildService</li>
<li>注册parentBeanDefinitionbeanName为parentchildBeanDefinitionbeanName为child</li>
<li>刷新容器;</li>
<li>从mergedBeanDefinitions取出了childmergedBeanDefinitions存放的是合并后的BeanDefinition</li>
<li>打印出child的attribute、scope、构造方法的两个参数值。</li>
</ol>
<p>大家可以看到childBeanDefinition继承了parentBeanDefinition。</p>
<p>如果没有父子关系单独作为BeanDefinition也可以用GenericBeanDefinition来表示</p>
<pre><code> AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition();
genericBeanDefinition.setBeanClass(AuthorService.class);
genericBeanDefinition.setScope(BeanDefinition.SCOPE_PROTOTYPE);
context.registerBeanDefinition(&quot;authorService&quot;, genericBeanDefinition);
context.refresh();
BeanDefinition mergedBeanDefinition = context.getBeanFactory().getMergedBeanDefinition(&quot;authorService&quot;);
BeanDefinition beanDefinition = context.getBeanFactory().getMergedBeanDefinition(&quot;authorService&quot;);
System.out.println(mergedBeanDefinition);
System.out.println(beanDefinition);
</code></pre>
<p>运行结果:</p>
<pre><code>Root bean: class [com.codebear.springcycle.AuthorService]; scope=prototype; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null
Root bean: class [com.codebear.springcycle.AuthorService]; scope=prototype; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null
</code></pre>
<p>可以看到当没有父子关系beanDefinition依旧会被保存在mergedBeanDefinitions中只是存储的内容和beanDefinitions中所存储的内容是一模一样的。</p>
<h5>GenericBeanDefinition总结</h5>
<p>GenericBeanDefinition替代了低版本Spring的ChildBeanDefinitionGenericBeanDefinition比ChildBeanDefinition、RootBeanDefinition更加灵活既可以单独作为BeanDefinition也可以作为父BeanDefinition还可以作为子GenericBeanDefinition。</p>
<h4>RootBeanDefinition</h4>
<p>在介绍GenericBeanDefinition的时候写了两段代码。</p>
<p>给第一个代码打上断点观察下mergedBeanDefinitions会发现parentBeanDefinition和
childBeanDefinition在mergedBeanDefinitions都变为了RootBeanDefinition
<img src="assets/15100432-bef9e1704c952ffe.png" alt="png" /></p>
<p>给第二个代码打上断点也观察下mergedBeanDefinitions会发现authorService在mergedBeanDefinitions也变为了RootBeanDefinition
<img src="assets/15100432-d355a3707f7a6a85.png" alt="png" /></p>
<p>可以看到在mergedBeanDefinitions存放的都是RootBeanDefinition。</p>
<p>RootBeanDefinition也可以用来充当父BeanDefinition就像下面的酱紫</p>
<pre><code> public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
RootBeanDefinition genericBeanDefinition = new RootBeanDefinition();
genericBeanDefinition.setBeanClass(ParentService.class);
genericBeanDefinition.setScope(BeanDefinition.SCOPE_PROTOTYPE);
context.registerBeanDefinition(&quot;parent&quot;, genericBeanDefinition);
GenericBeanDefinition rootBeanDefinition = new GenericBeanDefinition();
rootBeanDefinition.setBeanClass(ChildService.class);
rootBeanDefinition.setParentName(&quot;parent&quot;);
context.refresh();
}
</code></pre>
<p>但是RootBeanDefinition不可以充当子BeanDefinition</p>
<pre><code> public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
RootBeanDefinition genericBeanDefinition = new RootBeanDefinition();
genericBeanDefinition.setBeanClass(ParentService.class);
genericBeanDefinition.setScope(BeanDefinition.SCOPE_PROTOTYPE);
context.registerBeanDefinition(&quot;parent&quot;, genericBeanDefinition);
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition();
rootBeanDefinition.setBeanClass(ChildService.class);
rootBeanDefinition.setParentName(&quot;parent&quot;);
context.refresh();
}
</code></pre>
<p>运行结果:</p>
<pre><code>Exception in thread &quot;main&quot; java.lang.IllegalArgumentException: Root bean cannot be changed into a child bean with parent reference
at org.springframework.beans.factory.support.RootBeanDefinition.setParentName(RootBeanDefinition.java:260)
at com.codebear.springcycle.Main.main(Main.java:20)
</code></pre>
<p>抛出了异常。</p>
<p>查询源码:</p>
<pre><code> @Override
public void setParentName(@Nullable String parentName) {
if (parentName != null) {
throw new IllegalArgumentException(&quot;Root bean cannot be changed into a child bean with parent reference&quot;);
}
}
</code></pre>
<p>发现调用RootBeanDefinition的setParentName方法直接抛出了异常。</p>
<h5>RootBeanDefinition总结</h5>
<p>RootBeanDefinition可以作为其他BeanDefinition的父BeanDefinition也可以单独作为BeanDefinition但是不能作为其他BeanDefinition的子BeanDefinition在mergedBeanDefinitions存储的都是RootBeanDefinition。</p>
<h3>ScannedGenericBeanDefinition</h3>
<pre><code>@Configuration
@ComponentScan
public class AppConfig {
}
@Service
public class AuthorService {
}
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
System.out.println(context.getBeanDefinition(&quot;authorService&quot;).getClass());
}
}
</code></pre>
<p>运行结果:</p>
<pre><code>class org.springframework.context.annotation.ScannedGenericBeanDefinition
</code></pre>
<p>通过注解扫描出来的Bean的BeanDefinition用ScannedGenericBeanDefinition来表示。</p>
<h3>AnnotatedGenericBeanDefinition</h3>
<pre><code> public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
System.out.println(context.getBeanDefinition(&quot;appConfig&quot;).getClass());
}
</code></pre>
<p>运行结果:</p>
<pre><code>class org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition
</code></pre>
<p>配置类的BeanDefinition用AnnotatedGenericBeanDefinition来表示。</p>
<h3>ConfigurationClassBeanDefinition</h3>
<pre><code>public class AuthorService {
}
@Configuration
@ComponentScan
public class AppConfig {
@Bean
public AuthorService authorService() {
return null;
}
}
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
System.out.println(context.getBeanDefinition(&quot;authorService&quot;).getClass());
}
</code></pre>
<p>运行结果:</p>
<pre><code> class org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader$ConfigurationClassBeanDefinition
</code></pre>
<p>用@Bean声明的Bean的BeanDefinition用ConfigurationClassBeanDefinition来表示。</p>
<p>是不是完全没想到一个BeanDefinition可以牵涉到这么多的内容这些内容说没用确实没什么用说有用也有用。不明白这些内容阅读Spring源码会比较懵逼为什么会有那么多的BeanDefinition。这个时候你就会卡壳拼命的想弄懂这些BeanDefinition都是用来干嘛的但是网上关于BeanDefinition的博客不算太多比较好的博客就更少了希望此篇文章可以填充这块空白。</p>
</div>
</div>
<div>
<div style="float: left">
<a href="/文章/Spring 循环依赖及解决方式.md.html">上一页</a>
</div>
<div style="float: right">
<a href="/文章/Vert.x 基础入门.md.html">下一页</a>
</div>
</div>
</div>
</div>
</div>
</div>
<a class="off-canvas-overlay" onclick="hide_canvas()"></a>
</div>
<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v652eace1692a40cfa3763df669d7439c1639079717194" integrity="sha512-Gi7xpJR8tSkrpF7aordPZQlW2DLtzUlZcumS8dMQjwDHEnw9I7ZLyiOj/6tZStRBGtGgN6ceN6cMH8z7etPGlw==" data-cf-beacon='{"rayId":"7099802a0ad08b66","version":"2021.12.0","r":1,"token":"1f5d475227ce4f0089a7cff1ab17c0f5","si":100}' crossorigin="anonymous"></script>
</body>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-NPSEEVD756"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag('js', new Date());
gtag('config', 'G-NPSEEVD756');
var path = window.location.pathname
var cookie = getCookie("lastPath");
console.log(path)
if (path.replace("/", "") === "") {
if (cookie.replace("/", "") !== "") {
console.log(cookie)
document.getElementById("tip").innerHTML = "<a href='" + cookie + "'>跳转到上次进度</a>"
}
} else {
setCookie("lastPath", path)
}
function setCookie(cname, cvalue) {
var d = new Date();
d.setTime(d.getTime() + (180 * 24 * 60 * 60 * 1000));
var expires = "expires=" + d.toGMTString();
document.cookie = cname + "=" + cvalue + "; " + expires + ";path = /";
}
function getCookie(cname) {
var name = cname + "=";
var ca = document.cookie.split(';');
for (var i = 0; i < ca.length; i++) {
var c = ca[i].trim();
if (c.indexOf(name) === 0) return c.substring(name.length, c.length);
}
return "";
}
</script>
</html>