从零开始手写 spring ioc 框架,深入学习 spring 源码
IoC
Ioc 是一款 spring ioc 核心功能简化实现版本,便于学习和理解原理。
创作目的
使用 spring 很长时间,对于 spring 使用非常频繁,实际上对于源码一直没有静下心来学习过。
但是 spring 源码存在一个问题,那就是过于抽象,导致学习起来成本上升。
所以本项目由渐入深,只实现 spring 的核心功能,便于自己和他人学习 spring 的核心原理。
spring 的核心
Spring 的核心就是 spring-beans,后面的一切 spring-boot,spring-cloud 都是建立在这个地基之上。
当别人问你 spring 的时候,希望你可以谈谈自己对于 spring ioc 自己更深层的见解,而不是网上人云亦云的几句话。
什么是 IOC
控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。
其中最常见的方式叫做依赖注入(Dependency Injection,简称DI)。
通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体,将其所依赖的对象的引用传递给它。
也可以说,依赖被注入到对象中。
为什么需要 IOC
IoC 是解耦的一种方法。
我们知道Java 是一门面向对象的语言,在 Java 中 Everything is Object,我们的程序就是由若干对象组成的。
当我们的项目越来越大,合作的开发者越来越多的时候,我们的类就会越来越多,类与类之间的引用就会成指数级的增长。
这样的工程简直就是灾难,如果我们引入 Ioc 框架。
由框架来维护类的生命周期和类之间的引用。
我们的系统就会变成这样:
这个时候我们发现,我们类之间的关系都由 IoC 框架负责维护类,同时将类注入到需要的类中。
也就是类的使用者只负责使用,而不负责维护。
把专业的事情交给专业的框架来完成,大大的减少开发的复杂度。
快速开始
maven 引入
<dependency>
<groupId>com.github.houbb</groupId>
<artifactId>ioc</artifactId>
<version>0.1.11</version>
</dependency>
测试准备
全部测试代码,见 test 模块。
- Apple.java
public class Apple {
public void color() {
System.out.println("Apple color: red. ");
}
}
- apple.json
类似于 xml 的配置,我们暂时使用 json 进行配置验证。
[
{"name":"apple","className":"com.github.houbb.ioc.test.service.Apple"}
]
执行测试
- 测试
BeanFactory beanFactory = new JsonApplicationContext("apple.json");
Apple apple = (Apple) beanFactory.getBean("apple");
apple.color();
- 日志
Apple color: red.
spring 基本实现流程
说明
spring-beans 一切都是围绕 bean 展开的。
BeanFactory 负责对 bean 进行生命周期的相关管理,本节展示第一小节的简单实现流程。
spring 核心流程
Spring IoC 主要是以下几个步骤。
初始化 IoC 容器。
读取配置文件。
将配置文件转换为容器识别对的数据结构(这个数据结构在Spring中叫做 BeanDefinition)
利用数据结构依次实例化相应的对象
注入对象之间的依赖关系
BeanDefinition 的抽象
BeanDefinition 是 spring 对 java bean 属性的一个抽象,经过这一层抽象,配置文件可以是 xml/json/properties/yaml 等任意一种,甚至包括注解扫包。
为 spring 的拓展带来极大的灵活性。
本框架考虑到实现的简单性,初步只实现了 json 和基于注解扫包两种方式。
后期如果有时间可以考虑添加 xml 的实现,其实更多是 xml 的解析工作量,核心流程已经全部实现。
实现源码节选
BeanDefinition 相关
包含了对于 java bean 的基本信息抽象。
- BeanDefinition.java
其默认实现为 DefaultBeanDefinition.java
,就是对接口实现的最基本的 java POJO
/**
* 对象定义属性
* @author binbin.hou
* @since 0.0.1
*/
public interface BeanDefinition {
/**
* 名称
* @return 名称
* @since 0.0.1
*/
String getName();
/**
* 设置名称
* @param name 名称
* @since 0.0.1
*/
void setName(final String name);
/**
* 类名称
* @return 类名称
*/
String getClassName();
/**
* 设置类名称
* @param className 类名称
* @since 0.0.1
*/
void setClassName(final String className);
}
BeanFactory 核心管理相关
- BeanFactory.java
/**
* bean 工厂接口
* @author binbin.hou
* @since 0.0.1
*/
public interface BeanFactory {
/**
* 根据名称获取对应的实例信息
* @param beanName bean 名称
* @return 对象信息
* @since 0.0.1
*/
Object getBean(final String beanName);
/**
* 获取指定类型的实现
* @param beanName 属性名称
* @param tClass 类型
* @param <T> 泛型
* @return 结果
* @since 0.0.1
*/
<T> T getBean(final String beanName, final Class<T> tClass);
}
- DefaultBeanFactory.java
为接口最基础的实现,源码如下:
/**
* bean 工厂接口
* @author binbin.hou
* @since 0.0.1
*/
public class DefaultBeanFactory implements BeanFactory {
/**
* 对象信息 map
* @since 0.0.1
*/
private Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
/**
* 对象 map
* @since 0.0.1
*/
private Map<String, Object> beanMap = new ConcurrentHashMap<>();
/**
* 注册对象定义信息
* @since 0.0.1
*/
protected void registerBeanDefinition(final String beanName, final BeanDefinition beanDefinition) {
// 这里可以添加监听器
this.beanDefinitionMap.put(beanName, beanDefinition);
}
@Override
public Object getBean(String beanName) {
Object bean = beanMap.get(beanName);
if(ObjectUtil.isNotNull(bean)) {
// 这里直接返回的是单例,如果用户指定为多例,则每次都需要新建。
return bean;
}
// 获取对应配置信息
BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
if(ObjectUtil.isNull(beanDefinition)) {
throw new IocRuntimeException(beanName + " not exists in bean define.");
}
// 直接根据
Object newBean = createBean(beanDefinition);
// 这里可以添加对应的监听器
beanMap.put(beanName, newBean);
return newBean;
}
/**
* 根据对象定义信息创建对象
* @param beanDefinition 对象定义信息
* @return 创建的对象信息
* @since 0.0.1
*/
private Object createBean(final BeanDefinition beanDefinition) {
String className = beanDefinition.getClassName();
Class clazz = ClassUtils.getClass(className);
return ClassUtils.newInstance(clazz);
}
@Override
@SuppressWarnings("unchecked")
public <T> T getBean(String beanName, Class<T> tClass) {
Object object = getBean(beanName);
return (T)object;
}
}
其中 ClassUtils 是基于 class 的反射工具类,详情见 ClassUtils.java
JsonApplicationContext
基于 json 配置文件实现的基本实现,使用方式见开始种的例子代码。
- JsonApplicationContext.java
/**
* JSON 应用上下文
* @author binbin.hou
* @since 0.0.1
*/
public class JsonApplicationContext extends DefaultBeanFactory {
/**
* 文件名称
* @since 0.0.1
*/
private final String fileName;
public JsonApplicationContext(String fileName) {
this.fileName = fileName;
// 初始化配置
this.init();
}
/**
* 初始化配置相关信息
*
* <pre>
* new TypeReference<List<BeanDefinition>>(){}
* </pre>
*
* 读取文件:https://blog.csdn.net/feeltouch/article/details/83796764
* @since 0.0.1
*/
private void init() {
InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName);
final String jsonConfig = FileUtil.getFileContent(is);
List<DefaultBeanDefinition> beanDefinitions = JsonBs.deserializeArray(jsonConfig, DefaultBeanDefinition.class);
if(CollectionUtil.isNotEmpty(beanDefinitions)) {
for (BeanDefinition beanDefinition : beanDefinitions) {
super.registerBeanDefinition(beanDefinition.getName(), beanDefinition);
}
}
}
}
小结
至此,一个最基本的 spring ioc 就基本实现了。
如果你想继续学习,可以分别参考以下代码分支。
分支说明
v0.0.6-构造器和 factoryMethod 新建对象
v0.0.8-Aware 监听器及 PostProcessor
v0.1.1-@Configuration-java 代码配置
v0.1.3-@Lazy-@Scope-java 对象属性配置
v0.1.5-@Bean 参数构造以及 @Description
v0.1.9-Environment 和 @Profile 实现
v0.1.10-Property 配置文件相关和 @Value/@PropertyResource 实现
v0.1.11-@ComponentScan 文件包扫描支持
拓展阅读
从零开始手写 spring ioc 框架,深入学习 spring 源码的更多相关文章
- 从零开始手写 dubbo rpc 框架
rpc rpc 是基于 netty 实现的 java rpc 框架,类似于 dubbo. 主要用于个人学习,由渐入深,理解 rpc 的底层实现原理. 前言 工作至今,接触 rpc 框架已经有很长时间. ...
- java 从零开始手写 RPC (04) -序列化
序列化 java 从零开始手写 RPC (01) 基于 socket 实现 java 从零开始手写 RPC (02)-netty4 实现客户端和服务端 java 从零开始手写 RPC (03) 如何实 ...
- java 从零开始手写 RPC (05) reflect 反射实现通用调用之服务端
通用调用 java 从零开始手写 RPC (01) 基于 socket 实现 java 从零开始手写 RPC (02)-netty4 实现客户端和服务端 java 从零开始手写 RPC (03) 如何 ...
- 剖析手写Vue,你也可以手写一个MVVM框架
剖析手写Vue,你也可以手写一个MVVM框架# 邮箱:563995050@qq.com github: https://github.com/xiaoqiuxiong 作者:肖秋雄(eddy) 温馨提 ...
- java 从零开始手写 RPC (03) 如何实现客户端调用服务端?
说明 java 从零开始手写 RPC (01) 基于 socket 实现 java 从零开始手写 RPC (02)-netty4 实现客户端和服务端 写完了客户端和服务端,那么如何实现客户端和服务端的 ...
- java 从零开始手写 RPC (07)-timeout 超时处理
<过时不候> 最漫长的莫过于等待 我们不可能永远等一个人 就像请求 永远等待响应 超时处理 java 从零开始手写 RPC (01) 基于 socket 实现 java 从零开始手写 RP ...
- 手写开源ORM框架介绍
手写开源ORM框架介绍 简介 前段时间利用空闲时间,参照mybatis的基本思路手写了一个ORM框架.一直没有时间去补充相应的文档,现在正好抽时间去整理下.通过思路历程和代码注释,一方面重温下知识,另 ...
- 闭关修炼180天--手写持久层框架(mybatis简易版)
闭关修炼180天--手写持久层框架(mybatis简易版) 抛砖引玉 首先先看一段传统的JDBC编码的代码实现: //传统的JDBC实现 public static void main(String[ ...
- 手牵手,从零学习Vue源码 系列二(变化侦测篇)
系列文章: 手牵手,从零学习Vue源码 系列一(前言-目录篇) 手牵手,从零学习Vue源码 系列二(变化侦测篇) 陆续更新中... 预计八月中旬更新完毕. 1 概述 Vue最大的特点之一就是数据驱动视 ...
随机推荐
- 2017.12.21 学习vue的新得
温故而知新,这句话说的真的有道理.每次回顾vue总会学到不一样的知识点,我就在想,我第一遍到底看了什么? 废话不多说,简要记录今天的所得: 1.v-if 与 v-show 同:都是条件渲染 异:渲染的 ...
- GO基础之List
一.List定义 概述1.list是一种非连续存储的容器,由多个节点组成,节点通过一些变量记录彼此之间的关系.list有多种实现方法,如单向链表.双向链表等.2.Go语言中list的实现原理是双向链表 ...
- 项目spring boot 写es hbase 运行内存溢出
本地项目运行正常.服务器上运行内存溢出. 项目内部同时做插入oracle,es,hbase 经过测试发现 同时插入es,hbase是服务器上就会出现内存溢出 如果只插入oracle+es 或oracl ...
- Flask:项目的准备工作
1.创建虚拟环境 (1)打开Windows命令窗口,输入命令(先确保电脑上安装了Anaconda): conda create -n FlaskPath python=3.5.2 FlaskPath为 ...
- 超融合与传统IT架构对比:成本价格优势有哪些
之前文章中,我们已经介绍了超融合给用户 IT 基础架构带来的各个方面的价值,其中成本只是超融合架构的优势之一.但很多用户还是会非常关心这个话题,希望能有更具体的了解,所以本文整理超融合和传统 FC S ...
- CentOS生产环境无网络安装percona-xtrabackup2.4【RPM安装教程】
Percona XtraBackup 8.0不支持对在MySQL 8.0之前的版本,Percona Server for MySQL或 Percona XtraDB Cluster中创建的数据库进行备 ...
- 转:XSS和CSRF原理及防范
原文地址:http://www.freebuf.com/articles/web/39234.html 随着Web2.0.社交网络.微博等等一系列新型的互联网产品的诞生,基于Web环境的互联网应用越来 ...
- oop面向对象知识总结 静态成员和友元
第十一章 静态成员和友元 11.1 静态成员 1.C++类当中的静态数据成员仍借用保留字static,但是与之前的静态全局变量,静态局部变量以及静态函数没有关系. 2.静态数据成员不占用具体对象的数据 ...
- 一个帮助理清思路的神奇工具--debug
今天在回顾复习之前的知识的时候,对嵌套循环犯了迷糊,于是我使用了debug这个方法,不仅让我弄清楚嵌套循环输出结果是如何得来,也让我明白了具体流程. 在这里,IDE我使用的是PyCharm,自然用它自 ...
- 『题解』Codeforces9D How many trees?
更好的阅读体验 Portal Portal1: Codeforces Portal2: Luogu Description In one very old text file there was wr ...