热门标签 | 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源码大门。



推荐阅读
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社区 版权所有