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

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

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

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

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

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

在Spring里,也有这样的订单,它就是我们bean的定义和依赖关系,可以是xml形式,也可以是我们最熟悉的注解形式。
————————————————
涉及到需要的事物有:

订单:Bean定义

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

userDao = com.example.apidemo.spring.UserDao

BeanConfig.java

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

package com.example.apidemo.spring;

import lombok.Data;

/**
* bean定义类,配置文件中bean定义对应的实体
*/
@Data
public class BeanConfig { private String beanName; private Class beanClass;
//省略getter、setter
}

获取订单:资源加载

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

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

/**
* 获取订单:资源加载
* @return
*/
public class ResourceLoader { public static Map<String, BeanConfig> getResource() {
Map<String, BeanConfig> beanDefinitionMap = new HashMap<>(16);
Properties properties = new Properties();
try {
//配置beans.properties文件: userDao = com.example.apidemo.spring.UserDao
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);
BeanConfig beanConfig = new BeanConfig();
beanConfig.setBeanName(key);
Class clazz = Class.forName(className);
beanConfig.setBeanClass(clazz);
beanDefinitionMap.put(key, beanConfig);
}
inputStream.close();
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
return beanDefinitionMap;
} }

订单分配:Bean注册

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

/**
* 仓库:订单分配:Bean注册
*/
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。

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

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

/**
* 生产车间:对象工厂
* 流程:《生产车间:对象工厂》==执行BeanFactory()初始化方法,ResourceLoader().getResource()去==《获取订单:资源加载》
* 返回一个BeanFactory,里面包含配置的bean实例。 如果第一次调用,就是==《仓库:订单分配:Bean注册》,用BeanRegister去注册一个单例的bean,
* 并且存储到BeanRegister,如果第二次调用,先从缓存beanRegister取,获取不到再注册。
*/
public class BeanFactory { private Map<String, BeanConfig> 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定义,创建bean
return createBean(beanDefinitionMap.get(beanName));
} /**
* 创建Bean
*
* @param beanConfig bean定义
* @return
*/
private Object createBean(BeanConfig beanConfig) {
try {
Object bean = beanConfig.getBeanClass().newInstance();
//缓存bean
beanRegister.registerSingletonBean(beanConfig.getBeanName(), bean);
return bean;
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
}

生产销售:测试

  • UserDao.java

    我们的Bean类,很简单

/**
* 生产销售:测试
*/
public class UserDao {
public void queryUserInfo(){
System.out.println("new A good man.");
}
}

单元测试:

import org.junit.Test;

public class ApiTest {

    /**
* 演示一个简单的spring容器框架
*/
@Test
public 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();
}
}

结果:

五分钟,手撸一个简单的Spring容器的更多相关文章

  1. 深入理解Spring--动手实现一个简单的SpringIOC容器

    接触Spring快半年了,前段时间刚用Spring4+S2H4做完了自己的毕设,但是很明显感觉对Spring尤其是IOC容器的实现原理理解的不到位,说白了,就是仅仅停留在会用的阶段,有一颗想读源码的心 ...

  2. 【RPC】手撸一个简单的RPC框架实现

      涉及技术   序列化.Socket通信.Java动态代理技术,反射机制   角色   1.服务提供者:运行在服务端,是真实的服务实现类   2.服务发布监听者:运行在RPC服务端,1将服务端提供的 ...

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

    大家好,我是老三,Spring是我们最常用的开源框架,经过多年发展,Spring已经发展成枝繁叶茂的大树,让我们难以窥其全貌. 这节,我们回归Spring的本质,五分钟手撸一个Spring容器,揭开S ...

  4. 《Spring 手撸专栏》第 2 章:小试牛刀(让新手能懂),实现一个简单的Bean容器

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 上学时,老师总说:不会你就问,但多数时候都不知道要问什么! 你总会在小傅哥的文章前言 ...

  5. 手撸一个springsecurity,了解一下security原理

    手撸一个springsecurity,了解一下security原理 转载自:www.javaman.cn 手撸一个springsecurity,了解一下security原理 今天手撸一个简易版本的sp ...

  6. 通过 Netty、ZooKeeper 手撸一个 RPC 服务

    说明 项目链接 微服务框架都包括什么? 如何实现 RPC 远程调用? 开源 RPC 框架 限定语言 跨语言 RPC 框架 本地 Docker 搭建 ZooKeeper 下载镜像 启动容器 查看容器日志 ...

  7. 手撸一个SpringBoot-Starter

    1. 简介 通过了解SpringBoot的原理后,我们可以手撸一个spring-boot-starter来加深理解. 1.1 什么是starter spring官网解释 starters是一组方便的依 ...

  8. 使用Java Socket手撸一个http服务器

    原文连接:使用Java Socket手撸一个http服务器 作为一个java后端,提供http服务可以说是基本技能之一了,但是你真的了解http协议么?你知道知道如何手撸一个http服务器么?tomc ...

  9. 【手撸一个ORM】MyOrm的使用说明

    [手撸一个ORM]第一步.约定和实体描述 [手撸一个ORM]第二步.封装实体描述和实体属性描述 [手撸一个ORM]第三步.SQL语句构造器和SqlParameter封装 [手撸一个ORM]第四步.Ex ...

  10. 利用SpringBoot+Logback手写一个简单的链路追踪

    目录 一.实现原理 二.代码实战 三.测试 最近线上排查问题时候,发现请求太多导致日志错综复杂,没办法把用户在一次或多次请求的日志关联在一起,所以就利用SpringBoot+Logback手写了一个简 ...

随机推荐

  1. #ifndef、#define、#ifdef等命令在C#中的应用

    1.#ifndef指令的理解 #ifndef是一个预处理指令,用于判断一个表示符是否被定义.如果该标识符未被定义,则执行条件编译中的代码块.否则,将跳过该代码块.下面是一个简单的粒子. 1 #ifnd ...

  2. 文心一言 VS 讯飞星火 VS chatgpt (149)-- 算法导论12.2 4题

    五.用go语言,证明:如果一棵二叉搜索树中的一个结点有两个孩子,那么它的后继没有左孩子,它的前驱没有右孩子. 灵捷3.5 . 文心一言: 为了证明这个性质,我们首先需要明确二叉搜索树(BST)的定义和 ...

  3. Rong晔大佬教程学习(0):前言

    2023-12-13 在安装了tinyriscv的工具链之后,本想着说去看那个技术文档,但是那个技术文档只是相当于一个"使用手册",而不是技术教程,所以说还是得去补一补计组的知识. ...

  4. JavaScript 文件优化指南

    本文将探讨实用的 JavaScript 文件优化技术.如何处理与 JavaScript 文件相关的性能问题以及帮助优化过程的工具.你将获得提升web应用程序速度的相关知识,从而为你的用户提供无缝体验. ...

  5. 华为防火墙1day?

    背景信息 缺省情况下,FW通过8887端口提供内置的本地Portal认证页面,用户可以主动访问或HTTP重定向至认证页面(https://接口IP地址:8887)进行本地Portal认证. 当企业部署 ...

  6. Java8函数式编程应用

    我们经常提到,Java8是革命性的一个版本,原因就是正式引入了函数式编程,那Java的函数式编程在实际应用中到底有什么用呢?结合实际的应用,我整理出了函数式在Java的几个经典用途. 缓求值 惰性求值 ...

  7. 为什么说数字孪生和GIS高度互补?它们各自从对方那里获得了什么?

    在数字化时代,数字孪生和GIS作为两项重要技术,它们的融合正日益受到人们的关注和认可.数字孪生是将实体世界与数字世界紧密结合的技术,可以创建实时的虚拟副本,对物理系统进行模拟.优化和预测.而GIS则是 ...

  8. Cesium被接入数字孪生系统后会发生怎样的改变?

    众所周知,Cesium凭借其开源免费的特点一直垄断着整个三维GIS的生态系统,但是随着数字孪生技术的发展以及各项新需求的不断涌现,Cesium与数字孪生系统相结合的潜力也逐渐凸显. 一般而言,Cesi ...

  9. Collection和Map的区别

    Collection接口,包含list和set子接口Collection和Map接口之间的主要区别在于:Collection中存储了一组对象,而Map存储关键字/值对.在Map对象中,每一个关键字最多 ...

  10. Git和Github库详细使用教程

    SVN 是集中式或者有中心式版本控制系统,版本库是集中放在中央服务器的; Git 是分布式版本控制系统,那么它就没有中央服务器的,每个人的电脑就是一个完整的版本库,这样,工作的时候就不需要联网了,因为 ...