五分钟,手撸一个Spring容器!

Spring是我们最常用的开源框架,经过多年发展,Spring已经发展成枝繁叶茂的大树,让我们难以窥其全貌。

这节,我们回归Spring的本质,五分钟手撸一个Spring容器,揭开Spring神秘的面纱!

从什么是IOC开始?

Spring——春天,Java编程世界的春天是由一位音乐家——Rod Johnson带来的。

Rod Johnson先后编写了两本巨著《Expert One-on-One J2EE Design and Development》、《Expert One-on-One J2EE Development without EJB》,拉起了挑战正统Java EE框架EJB的大旗。

8cc1ce776aaa7450e819236e413a3a8e.png
Rod Johnson两大著作-来自百度百科

Rod Johnson不仅是一名旗手,更是开发了Spring这一轻量级框架,像一名勇敢的龙骑兵一样,对EJB发动了冲锋,并最终战胜了EJB,让Spring成为Java EE事实上的标准。

893682954ab3cf16a48d3f782acc1773.png
Spring Logo

Spring的两大内核分别是IOC和AOP,其中最最核心的是IOC。

所谓的IOC(控制反转):就是由容器来负责控制对象的生命周期和对象间的关系。以前是我们想要什么,就自己创建什么,现在是我们需要什么,容器就给我们送来什么。

3a9e00d5845fb19f845902a5de45b9d4.png
引入IOC之前和引入IOC之后

也就是说,控制对象生命周期的不再是引用它的对象,而是容器。对具体对象,以前是它控制其它对象,现在所有对象都被容器控制,所以这就叫控制反转

6b3d32f09a24f5e8e36652e819e62a53.png
控制反转示意图

也许你还听到另外一个概念DI(依赖注入),它指的是容器在实例化对象的时候把它依赖的类注入给它,我们也可以认为,DI是IOC的补充和实现。

工厂和Spring容器

Spring是一个成熟的框架,为了满足扩展性、实现各种功能,所以它的实现如同枝节交错的大树一样,现在让我们把视线从Spring本身移开,来看看一个萌芽版的Spring容器怎么实现。

Spring的IOC本质就是一个大工厂,我们想想一个工厂是怎么运行的呢?

7bcb48d36a138ee4654c40e5862bc21a.png
工厂运行
  • 生产产品:一个工厂最核心的功能就是生产产品。在Spring里,不用Bean自己来实例化,而是交给Spring,应该怎么实现呢?——答案毫无疑问,反射

    那么这个厂子的生产管理是怎么做的?你应该也知道——工厂模式

  • 库存产品:工厂一般都是有库房的,用来库存产品,毕竟生产的产品不能立马就拉走。Spring我们都知道是一个容器,这个容器里存的就是对象,不能每次来取对象,都得现场来反射创建对象,得把创建出的对象存起来。

  • 订单处理:还有最重要的一点,工厂根据什么来提供产品呢?订单。这些订单可能五花八门,有线上签签的、有到工厂签的、还有工厂销售上门签的……最后经过处理,指导工厂的出货。

    在Spring里,也有这样的订单,它就是我们bean的定义和依赖关系,可以是xml形式,也可以是我们最熟悉的注解形式。

那对应我们的萌芽版的Spring容器是什么样的呢?

b151956921b5abdc154396919bdbc685.png
mini版本Spring IOC

订单:Bean定义

Bean可以通过一个配置文件定义,我们会把它解析成一个类型。

a2d3f926fa9320784c590b701c6fea70.png
Bean定义
  • beans.properties

    为了偷懒,这里直接用了最方便解析的properties,用一个<key,value>类型的配置来代表Bean的定义,其中key是beanName,value是class

    userDao:cn.fighter3.bean.UserDao
  • BeanDefinition.java

    bean定义类,配置文件中bean定义对应的实体

    public class BeanDefinition {private String beanName;private Class beanClass;//省略getter、setter  }

获取订单:资源加载

接下订单之后,就要由销售向生产部门交接,让生产部门知道商品的规格、数量之类。

资源加载器,就是来完成这个工作的,由它来完成配置文件中配置的加载。

public class ResourceLoader {public static Map<String, BeanDefinition> getResource() {Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>(16);Properties properties = new Properties();try {InputStream inputStream = ResourceLoader.class.getResourceAsStream("/beans.properties");properties.load(inputStream);Iterator<String> it = properties.stringPropertyNames().iterator();while (it.hasNext()) {String key = it.next();String className = properties.getProperty(key);BeanDefinition beanDefinition = new BeanDefinition();beanDefinition.setBeanName(key);Class clazz = Class.forName(className);beanDefinition.setBeanClass(clazz);beanDefinitionMap.put(key, beanDefinition);}inputStream.close();} catch (IOException | ClassNotFoundException e) {e.printStackTrace();}return beanDefinitionMap;}}

订单分配:Bean注册

对象注册器,这里用于单例bean的缓存,我们大幅简化,默认所有bean都是单例的。可以看到所谓单例注册,也很简单,不过是往HashMap里存对象。

public class BeanRegister {//单例Bean缓存private Map<String, Object> singletonMap = new HashMap<>(32);/*** 获取单例Bean** @param beanName bean名称* @return*/public Object getSingletonBean(String beanName) {return singletonMap.get(beanName);}/*** 注册单例bean** @param beanName* @param bean*/public void registerSingletonBean(String beanName, Object bean) {if (singletonMap.containsKey(beanName)) {return;}singletonMap.put(beanName, bean);}}

生产车间:对象工厂

好了,到了我们最关键的生产部门了,在工厂里,生产产品的是车间,在IOC容器里,生产对象的是BeanFactory。

34758a74be691e5b9fa475dfadde5b43.png
BeanFactory
  • 对象工厂,我们最核心的一个类,在它初始化的时候,创建了bean注册器,完成了资源的加载。

  • 获取bean的时候,先从单例缓存中取,如果没有取到,就创建并注册一个bean

    public class BeanFactory {private Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>();private BeanRegister beanRegister;public BeanFactory() {//创建bean注册器beanRegister = new BeanRegister();//加载资源this.beanDefinitionMap = new ResourceLoader().getResource();}/*** 获取bean** @param beanName bean名称* @return*/public Object getBean(String beanName) {//从bean缓存中取Object bean = beanRegister.getSingletonBean(beanName);if (bean != null) {return bean;}//根据bean定义,创建beanreturn createBean(beanDefinitionMap.get(beanName));}/*** 创建Bean** @param beanDefinition bean定义* @return*/private Object createBean(BeanDefinition beanDefinition) {try {Object bean = beanDefinition.getBeanClass().newInstance();//缓存beanbeanRegister.registerSingletonBean(beanDefinition.getBeanName(), bean);return bean;} catch (InstantiationException | IllegalAccessException e) {e.printStackTrace();}return null;}
    }

生产销售:测试

  • UserDao.java

    我们的Bean类,很简单

    public class UserDao {public void queryUserInfo(){System.out.println("A good man.");}
    }
  • 单元测试

    public class ApiTest {@Testpublic void test_BeanFactory() {//1.创建bean工厂(同时完成了加载资源、创建注册单例bean注册器的操作)BeanFactory beanFactory = new BeanFactory();//2.第一次获取bean(通过反射创建bean,缓存bean)UserDao userDao1 = (UserDao) beanFactory.getBean("userDao");userDao1.queryUserInfo();//3.第二次获取bean(从缓存中获取bean)UserDao userDao2 = (UserDao) beanFactory.getBean("userDao");userDao2.queryUserInfo();}
    }
  • 运行结果

    A good man.
    A good man.

至此,我们一个萌芽版的Spring容器就完成了。

考虑一下,它有哪些不足呢?是否还可以抽象、扩展、解耦……

细细想想这些东西,你是不是对真正的Spring IOC容器为何如此复杂,有所理解了呢?




参考:

  • [1]. 《Spring揭秘》

  • [2].小傅哥 《手撸Spring》

  • [3].《精通Spring4.X企业应用开发实战》

ebcbbdb99319125ebef4cb9abece5ffb.gif

往期推荐

1a164c27c3a6a4d662e77c45ec80f95e.png

Spring 夺命 35 问!


d95fe119c94440c84d895a31f5269ec3.png

Spring Boot 优雅配置多数据源


9af59c79029912bf5b9cdaae8b4fc525.png

IDEA 版 Postman 面世了,功能真心强大!


28eddde523e4a5cd100d59b3aad2fbbf.gif

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/544162.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

一步一步搭建客服系统 (7) 多人共享的电子白板、画板

多人共享、同时操作的电子白板&#xff0c;让不同的参入者以不同的颜色来画画&#xff1b;可以保存当前room的内容&#xff0c;以让后来者可以直接加载所有内容。 在github上找到一个用html5 canvas实现的一个电子白板的例子&#xff1a; https://github.com/kblcuk/canvas-whi…

Spring Cloud OpenFeign 的 5 个优化小技巧!

作者 | 磊哥来源 | Java中文社群&#xff08;ID&#xff1a;javacn666&#xff09;转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09;OpenFeign 是 Spring 官方推出的一种声明式服务调用和负载均衡组件。它的出现就是为了替代已经进入停更维护状态的 Feign&am…

java的equals方法_Java Vector equals()方法与示例

java的equals方法向量类的equals()方法 (Vector Class equals() method) equals() method is available in java.util package. equals()方法在java.util包中可用。 equals() method is used to check whether this Vector is the same or equals to the given object (ob) or …

推荐几个好文章

1 cocos2dx各种行动 http://www.cnblogs.com/linux-ios/archive/2013/04/06/3001946.html 2 cocos2dx自己定义曲线 http://blog.csdn.net/ufolr/article/details/7447773 3 lua中载入cocostudio动画&#xff0c;触发帧事件&#xff08;非常全&#xff0c;非常具体。还有源代码…

treeset java_Java TreeSet first()方法与示例

treeset javaTreeSet类的first()方法 (TreeSet Class first() method) first() method is available in java.util package. first()方法在java.util包中可用。 first() method is used to retrieve the first lowest element in this TreeSet. first()方法用于检索此TreeSet中…

Java常用类:7000字一次性帮你总结好啦!

来源&#xff1a;cnblogs.com/lwtyyds/p/15678152.html常用类概述内部类内部类的分类&#xff1a;1.成员内部类&#xff08;非静态内部类&#xff09;2.局部内部类4.匿名内部类Object类Object类常用方法&#xff1a;1.equals方法2.hashCode方法3.toString方法4.finzlize方法包装…

CentOS6.4系统启动失败故障排查

转&#xff1a;http://www.centoscn.com/CentosBug/osbug/2014/1028/4011.html 操作系统启动失败如下图报错&#xff1a; 故障现象&#xff1a; 从图中可以看到&#xff0c;操作系统启动的过程中&#xff0c;fsck在执行文件系统检测时出现了错误&#xff0c;并且是在检查/dev/m…

hashmap clone_Java HashMap clone()方法与示例

hashmap cloneHashMap类clone()方法 (HashMap Class clone() method) clone() method is available in java.util package. clone()方法在java.util包中可用。 clone() method is used to get a cloned copy of this HashMap instance or in other words, we can say it return…

Linux内存管理--物理内存分配【转】

转自&#xff1a;http://blog.csdn.net/myarrow/article/details/8682819 1. First Fit分配器 First Fit分配器是最基本的内存分配器&#xff0c;它使用bitmap而不是空闲块列表来表示内存。在bitmap中&#xff0c;如果page对应位为1&#xff0c;则表示此page已经被分配&#xf…

JDK的一个Bug,监听文件变更要小心了

背景 在某些业务场景下&#xff0c;我们需要自己实现文件内容变更监听的功能&#xff0c;比如&#xff1a;监听某个文件是否发生变更&#xff0c;当变更时重新加载文件的内容。看似比较简单的一个功能&#xff0c;但如果在某些JDK版本下&#xff0c;可能会出现意想不到的Bug。本…

hashset java_Java HashSet clear()方法与示例

hashset javaHashSet类的clear()方法 (HashSet Class clear() method) clear() method is available in java.util package. clear()方法在java.util包中可用。 clear() method is used to clear all of the element exists from this HashSet. clear()方法用于清除此HashSet中…

推荐 17 个压箱底的常用类库

前言在java的庞大体系中&#xff0c;其实有很多不错的小工具&#xff0c;也就是我们平常说的&#xff1a;轮子。如果在我们的日常工作当中&#xff0c;能够将这些轮子用户&#xff0c;再配合一下idea的快捷键&#xff0c;可以极大得提升我们的开发效率。今天我决定把一些压箱底…

02、django中的上下文

2019独角兽企业重金招聘Python工程师标准>>> 1、譬如设置网站的名称,setting中设置变量&#xff1a; # setting.py SITE_NAME "我的小站"2、在view中写函数将该变量转换成字典,做返回值 from django.conf import settings def site_key(request):# 这里使…

enumset.allof_Java EnumSet allOf()方法与示例

enumset.allofEnumSet类的allOf()方法 (EnumSet Class allOf() method) allOf() method is available in java.util package. allOf()方法在java.util包中可用。 allOf() method is used to return Enumset that has all of the elements of the given element type (ele_ty). …

实战:10 种实现延迟任务的方法,附代码!

作者 | 磊哥来源 | Java中文社群&#xff08;ID&#xff1a;javacn666&#xff09;转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09;这篇文章的诞生要感谢一位读者&#xff0c;是他让这篇优秀的文章有了和大家见面的机会&#xff0c;重点是优秀文章&#xff…

netsh命令

C:\Windows\System32>netsh interface ipv6 show address levelverbose 地址 ::1 参数---------------------------------------------------------接口 Luid : Loopback Pseudo-Interface 1作用域 ID : 0.0有效生存时间 : infinite首选生存时间 : infiniteDAD 状态 : 首选项…

面渣逆袭:Redis连环五十二问!三万字+八十图详解!

基础1.说说什么是Redis?Redis图标Redis是一种基于键值对&#xff08;key-value&#xff09;的NoSQL数据库。比一般键值对数据库强大的地方&#xff0c;Redis中的value支持string&#xff08;字符串&#xff09;、hash&#xff08;哈希&#xff09;、 list&#xff08;列表&…

hdu 4864 Task(贪婪啊)

主题链接&#xff1a;http://acm.hdu.edu.cn/showproblem.php?pid4864 Task Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 1346 Accepted Submission(s): 336Problem DescriptionToday the company has m ta…

svg学习网站

1、http://www.runoob.com/svg

EasyExcel太方便易用了,强烈推荐!

背景 系统中经常要导出大量的数据&#xff0c;格式基本上都是Excel&#xff0c;然而每次导表都是对系统内存的一次挑战。在Java领域&#xff0c;生成或解析Excel的框架比较有名的当属Apache的poi和jxl了。但使用它们&#xff0c;会面临着严重的内存损耗问题。如果系统的并发量还…