热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

javav.5起点,逐行阅读Spring5.X源码(一)BeanDefinition,起点

本篇博客你讲学到:1.如何理解BeanDefinition2.准备环境3.BeanDefinition接口讲解4.BeanDefinition的类继承关系5.IOC的

本篇博客你讲学到:

1. 如何理解BeanDefinition

2. 准备环境

3. BeanDefinition接口讲解

4. BeanDefinition的类继承关系

5. IOC的引出

6. BeanFactory工厂的引出

7. 后置处理器的引出

8. spring扩展性初探

1、为什么从BeanDefinition讲起?

spring源码太庞大了,精通spring源码确实不容易,讲懂更难。学习spring源码是件浩大的工程。很多读者从spring容器启动开始逐行debug源码进行分析,刚开始很容易理解,但是当你逐层debug源码深处时,就会感慨“身在此山中,不识真面目”。笔者在刚开始的时候也经历过这种痛苦,精读几遍之后笔者觉着spring源码不能从头读起,我们需要先搞懂spring中的基础组件及组件之间的关系,这就好比组装电脑,你得先了解CPU作用、内存的作用、主板的作用、硬盘的作用,然后你才知道如何讲他们组装到一起,从头阅读源码就跟你让一个学金融专业的学生组装电脑一样,效果可想而知。

既然BeanDefinition是个接口,那spring中肯定有他的实现类对不对,好,是时候看一下BeanDefinition的类继承图了。在这里笔者跟大家说一个问题,笔者发现很多人读源码的时候拿来一个类就读或者debug源码跟读,也不管这个类跟其他类的关系,读完后感觉很混乱,甚至吐槽spring源码写的毫无章法,拜托,spring源码是典型的面向接口编程,严格遵循开闭原则、依赖倒置原则和迪米特法则(软件设计7大基本原则,大家自行百度啦),是spring源码写的差还是你的水平差?spring每一个模块都有一个完整的类继承关系图,不然spring被业界称赞的高扩展性谈何而来?所以我们必须将每个模块的类继承关系了然于胸。初学,我们也不可能将继承体系中的每个类都搞懂,把这个继承图下载下来存到桌面,在以后的源码阅读中这个继承关系会被你一一攻破,学完你也就掌握了,而且不会忘,更能提高你的编程水平,读完spring你会发现的编程风格潜移默化的被spring影响了!对吧。

BeanDefinition的继承关系:

beanDefinition.setScope("singleton");

beanDefinition.setDescription("手动注入");

beanDefinition.setAbstract(false);

//将beanDefinition注册到spring容器中

context.registerBeanDefinition("interService",beanDefinition);

//加载或者刷新当前的配置信息

context.refresh();

BeanDefinition interServiceBeanDefinition = context.getBeanDefinition("interService");

System.out.println("——————InterService的附加属性如下:");

System.out.println("父类"+interServiceBeanDefinition.getParentName());

System.out.println("描述"+interServiceBeanDefinition.getDescription());

System.out.println("InterService在spring的名称"+interServiceBeanDefinition.getBeanClassName());

System.out.println("实例范围"+interServiceBeanDefinition.getScope());

System.out.println("是否是懒加载"+interServiceBeanDefinition.isLazyInit());

System.out.println("是否是抽象类"+interServiceBeanDefinition.isAbstract());

System.out.println("——————等等等等,读者自行编写");

}

}

其实笔者很喜欢这种代码方式完成spring配置工作,这样能让我们更深入的了解和应用spring,不过这种方式的缺点也很明显-繁琐易出错,spring为了简化我们的工作提供了xml配置方式,直到spring5.x注解方式的稳定成熟,spring全家桶得到了飞速的发展。但通过这个例子读者可以加深对BeanDefinition的理解。

IOC的引出

看上面这么一行代码:

context.registerBeanDefinition("interService",beanDefinition);

这行代码的意思是将我们手动封装的beanDefinition注册到容器中,同时给这个beanDefinition起了个名字“interService”,spring内部生成beanDefinitino时会默认起一个名字,改名字的规则就是业务类名字首字母小写。

那生成的BeanDefinition保存在哪里呢?既然我们是通过上面的方法将BeanDefinition注册到容器中,肯丢是在这个方法底层实现了保存,我们点进去看:

@Override

public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)

throws BeanDefinitionStoreException {

// 将beanDefinition保存到spring容器中

this.beanFactory.registerBeanDefinition(beanName, beanDefinition);

}

继续跟进registerBeanDefinition方法,找到下面这行代码:

.....以上代码省略,以后详解

this.beanDefinitionMap.put(beanName, beanDefinition);

顾名思义,beanDefinitionMap就是一个Map呀!具体是啥map,我们ctrl+鼠标左键单击找到beanDefinitionMap定义处:

/** Map of bean definition objects, keyed by bean name. */

private final Map beanDefinitionMap &#61; new ConcurrentHashMap<>(256);

还需要我解释这行代码吗&#xff1f;有人说需要&#xff0c;我不懂ConcurrentHashMap&#xff0c;好吧&#xff0c;这是java并发包java.util.concurrent下的集合类&#xff0c;它就是一个Map&#xff0c;但是支持多线程并发访问&#xff0c;为啥使用ConcurrentHashMap而不是用HashMap&#xff0c;嗨&#xff0c;建议你好好补下java高并发知识(后续我会写一个java高并发编程底层原理&#xff0c;让你吊打面试官&#xff0c;欢迎大家关注)。总之&#xff0c;这就是beanDefinition存储的容器&#xff0c;这行代码所在的类名叫DefaultListableBeanFactory&#xff0c;它是bean的工厂&#xff0c;spring中所有的对象或者说bean都存在这个bean工厂中&#xff0c;业界叫它IOC&#xff0c;很多书或者视频都会讲IOC&#xff0c;相信读者也知道IOC是容器&#xff0c;但它就是一堆Map集合而已&#xff0c;beanDefinitionMap 知识众多Map中的一个而已&#xff0c;以后我会将其他的map容器&#xff0c;今天你只需要只到这么一个存放BeanDefinition的容器即可。

这下你搞懂IOC了吧&#xff01; 全体起立&#xff01;

有读者问&#xff0c;那DefaultListableBeanFactory这个bean工厂啥时候创建的&#xff0c;我说以后再讲&#xff01;想必大家都想知道&#xff0c;那就了解一下吧&#xff01;看代码&#xff1a;

AnnotationConfigApplicationContext context &#61; new AnnotationConfigApplicationContext();

这是我们创建spring上下文对象&#xff0c;AnnotationConfigApplicationContext 类有一个父类&#xff0c;AnnotationConfigApplicationContext的无参 构造函数执行时会默认调用父类无参构造函数(java基础知识)&#xff0c;AnnotationConfigApplicationContext 的父类如下&#xff1a;

public class AnnotationConfigApplicationContext extends GenericApplicationContext implements AnnotationConfigRegistry {

也就是GenericApplicationContext &#xff0c;我们看一下他的构造函数&#xff1a;

public GenericApplicationContext() {

//初始化一个BeanFactory

this.beanFactory &#61; new DefaultListableBeanFactory();

}

我不说话&#xff0c;静静的看着屏幕前的你思考的样子&#xff01; 也就是说&#xff0c;spring启动的时候就创建好了这个bean工厂&#xff01;

咦&#xff1f;不是要将BeanDefinitino的继承关系码&#xff1f;怎么跑偏了&#xff1f;这就是spring的特点&#xff0c;太庞大了&#xff0c;没有孤立的知识点&#xff01;这也是很多读者阅读spring源码时读着读着就蒙圈的原因。

后置处理器的引出

上文我们通过手动将InterService封装成了一个BeanDefinition然后注册(说好听了叫注册&#xff0c;起始就是map.put)到了容器中&#xff0c;我说了现在我们没有这么用的了&#xff0c;都是spring自动帮我们完成扫描注册&#xff0c;在哪完成的扫描注册&#xff1f;回到下面这几行代码&#xff1a;

AnnotationConfigApplicationContext context &#61; new AnnotationConfigApplicationContext();

//注册配置类

context.register(Config.class);

//加载或者刷新当前的配置信息

context.refresh();

context.refresh()方法&#xff0c;完成了spring的启动、扫描、解析、实例化等一系列过程&#xff0c;这个方法完成的功能太多了&#xff0c;我们的扫描注册也是在这里完成的&#xff0c;进入到这个方法&#xff0c;找到这么一行代码&#xff1a;

...以上省略

invokeBeanFactoryPostProcessors(beanFactory);

...以下省略

翻译一下名字&#xff0c;执行bean工厂的后置处理器&#xff0c;这行代码完成了扫描与注册&#xff0c;我不带大家分析里面的代码&#xff0c;你只需要知道他的作用就行&#xff0c;这行代码执行完成后&#xff0c;我们只是把业务类InterService封装成了BeanDefinition而已&#xff0c;业务类InterService并没有实例化&#xff0c;在业务类InterService实例化之前我们能不能从beanDefinition中将InterService偷梁换柱呢&#xff1f;或者说&#xff0c;我们能通过BeanDefinition来构建bean&#xff0c;那我们能不能修改bean呢&#xff1f;那必须的&#xff01;

通过后置处理器完成&#xff0c;什么是后置处理器&#xff1f;可以把它理解成回调&#xff0c;我扫描注册成功后回调后置处理器&#xff01;BeanDefinition讲完后紧接着就讲后置处理器。我们添加一个后置处理器&#xff1a;

/**

* 扫描注册成功完成后&#xff0c;spring自动调用后置处理器MyBeanFactoryPostProcessor的postProcessBeanFactory方法

*/

&#64;Component

public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

&#64;Override

public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)

throws BeansException {

//通过bean工厂拿到业务类InterService的beanDefinition

GenericBeanDefinition beanDefinition &#61;

(GenericBeanDefinition) beanFactory.getBeanDefinition("interService");

System.out.println("扫描注册成功完成后&#xff0c;spring自动调用次方法");

System.out.println(beanDefinition.getDescription());

}

}

spring扫描注册完成后&#xff0c;会自动调用MyBeanFactoryPostProcessor的postProcessBeanFactory方法&#xff0c;这个方法给你传递了一个ConfigurableListableBeanFactory类型的bean工厂&#xff0c;ConfigurableListableBeanFactory是一个接口&#xff0c;上文spring启动实例化的DefaultListableBeanFactory工厂是它的实现类。天啊&#xff0c;竟然把bean工厂给你了&#xff0c;相当于敌人把军火库暴露在你面前&#xff0c;你岂不是想干嘛就干嘛&#xff01;上述代码我们通过bean工厂拿到了业务类InterService的beanDefinition&#xff0c;我都拿到你的beanDefinition了&#xff0c;那么我不但可以get到你的信息&#xff0c;我也可以set你的信息从而改变你的行为来影响你后续的实例化。我们来编写另一个业务类&#xff1a;

public class User {

private int age &#61;31;

private String name&#61;"myname";

}

spring启动时也会把这个业务类扫描&#xff0c;接下来&#xff0c;看好了&#xff0c;我在bean工厂中偷梁换柱&#xff0c;在beanDefinition中将你的InterService业务类替换掉&#xff1a;

/**

* 扫描注册成功完成后&#xff0c;spring自动调用后置处理器MyBeanFactoryPostProcessor的postProcessBeanFactory方法

*/

&#64;Component

public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

&#64;Override

public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)

throws BeansException {

GenericBeanDefinition beanDefinition &#61;

(GenericBeanDefinition) beanFactory.getBeanDefinition("interService");

System.out.println(beanDefinition.getBeanClassName());

System.out.println("开始偷梁换柱");

beanDefinition.setBeanClass(User.class);

}

}

测试一下&#xff1a;

public class Test {

public static void main(String[] args) {

AnnotationConfigApplicationContext context &#61; new AnnotationConfigApplicationContext();

//注册配置类

context.register(Config.class);

context.refresh();

System.out.println("更改后的业务类&#xff1a;"&#43;context.getBeanDefinition("interService").getBeanClassName());

}

}

打印结果&#xff1a;

//我们尝试获取InterService实例

context.getBean(InterService.class);

}

}

打印结果报错&#xff0c;因为InterService不存在spring当中了&#xff1a;

getBeanDefinition方法返回的BeanDefinition类型&#xff0c;为什么强转成GenericBeanDefinition&#xff0c;起始BeanDefinition接口中并没有setBeanClass这个方法&#xff0c;GenericBeanDefinition是他的实现&#xff0c;提供更丰富的功能。不同的BeanDefinition实现具有不同的作用。

下一篇我们详细讲一下不同BeanDefinition的作用&#xff0c;BeanDefinition学精通后你基本迈入了spring源码大门。



推荐阅读
  • Tomcat/Jetty为何选择扩展线程池而不是使用JDK原生线程池?
    本文探讨了Tomcat和Jetty选择扩展线程池而不是使用JDK原生线程池的原因。通过比较IO密集型任务和CPU密集型任务的特点,解释了为何Tomcat和Jetty需要扩展线程池来提高并发度和任务处理速度。同时,介绍了JDK原生线程池的工作流程。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • 标题: ... [详细]
  • Java在运行已编译完成的类时,是通过java虚拟机来装载和执行的,java虚拟机通过操作系统命令JAVA_HOMEbinjava–option来启 ... [详细]
  • Oracle优化新常态的五大禁止及其性能隐患
    本文介绍了Oracle优化新常态中的五大禁止措施,包括禁止外键、禁止视图、禁止触发器、禁止存储过程和禁止JOB,并分析了这些禁止措施可能带来的性能隐患。文章还讨论了这些禁止措施在C/S架构和B/S架构中的不同应用情况,并提出了解决方案。 ... [详细]
  • Spring常用注解(绝对经典),全靠这份Java知识点PDF大全
    本文介绍了Spring常用注解和注入bean的注解,包括@Bean、@Autowired、@Inject等,同时提供了一个Java知识点PDF大全的资源链接。其中详细介绍了ColorFactoryBean的使用,以及@Autowired和@Inject的区别和用法。此外,还提到了@Required属性的配置和使用。 ... [详细]
  • 云原生边缘计算之KubeEdge简介及功能特点
    本文介绍了云原生边缘计算中的KubeEdge系统,该系统是一个开源系统,用于将容器化应用程序编排功能扩展到Edge的主机。它基于Kubernetes构建,并为网络应用程序提供基础架构支持。同时,KubeEdge具有离线模式、基于Kubernetes的节点、群集、应用程序和设备管理、资源优化等特点。此外,KubeEdge还支持跨平台工作,在私有、公共和混合云中都可以运行。同时,KubeEdge还提供数据管理和数据分析管道引擎的支持。最后,本文还介绍了KubeEdge系统生成证书的方法。 ... [详细]
  • 本文介绍了Hyperledger Fabric外部链码构建与运行的相关知识,包括在Hyperledger Fabric 2.0版本之前链码构建和运行的困难性,外部构建模式的实现原理以及外部构建和运行API的使用方法。通过本文的介绍,读者可以了解到如何利用外部构建和运行的方式来实现链码的构建和运行,并且不再受限于特定的语言和部署环境。 ... [详细]
  • 基于事件驱动的并发编程及其消息通信机制的同步与异步、阻塞与非阻塞、IO模型的分类
    本文介绍了基于事件驱动的并发编程中的消息通信机制,包括同步和异步的概念及其区别,阻塞和非阻塞的状态,以及IO模型的分类。同步阻塞IO、同步非阻塞IO、异步阻塞IO和异步非阻塞IO等不同的IO模型被详细解释。这些概念和模型对于理解并发编程中的消息通信和IO操作具有重要意义。 ... [详细]
  • 本文介绍了Web学习历程记录中关于Tomcat的基本概念和配置。首先解释了Web静态Web资源和动态Web资源的概念,以及C/S架构和B/S架构的区别。然后介绍了常见的Web服务器,包括Weblogic、WebSphere和Tomcat。接着详细讲解了Tomcat的虚拟主机、web应用和虚拟路径映射的概念和配置过程。最后简要介绍了http协议的作用。本文内容详实,适合初学者了解Tomcat的基础知识。 ... [详细]
  • 在springmvc框架中,前台ajax调用方法,对图片批量下载,如何弹出提示保存位置选框?Controller方法 ... [详细]
  • 2018深入java目标计划及学习内容
    本文介绍了作者在2018年的深入java目标计划,包括学习计划和工作中要用到的内容。作者计划学习的内容包括kafka、zookeeper、hbase、hdoop、spark、elasticsearch、solr、spring cloud、mysql、mybatis等。其中,作者对jvm的学习有一定了解,并计划通读《jvm》一书。此外,作者还提到了《HotSpot实战》和《高性能MySQL》等书籍。 ... [详细]
  • SpringMVC接收请求参数的方式总结
    本文总结了在SpringMVC开发中处理控制器参数的各种方式,包括处理使用@RequestParam注解的参数、MultipartFile类型参数和Simple类型参数的RequestParamMethodArgumentResolver,处理@RequestBody注解的参数的RequestResponseBodyMethodProcessor,以及PathVariableMapMethodArgumentResol等子类。 ... [详细]
  • SpringBoot整合SpringSecurity+JWT实现单点登录
    SpringBoot整合SpringSecurity+JWT实现单点登录,Go语言社区,Golang程序员人脉社 ... [详细]
  • 本文介绍了H5游戏性能优化和调试技巧,包括从问题表象出发进行优化、排除外部问题导致的卡顿、帧率设定、减少drawcall的方法、UI优化和图集渲染等八个理念。对于游戏程序员来说,解决游戏性能问题是一个关键的任务,本文提供了一些有用的参考价值。摘要长度为183字。 ... [详细]
author-avatar
火立者
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有