工厂和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. C#12新功能有哪些?

    前言 作为.NET 8发布会的一部分,微软于11月14日发布了C#12的新功能,这也是目前.NET的最新版本.正如之前公布的那样,最显著的改进包括了集合表达式.主构造函数.任何类型的别名以及lambd ...

  2. jmeter测试计划中的“独立运行每个线程组”Demo演示

    一:jmeter的运行顺序 测试计划-->线程组 其次执行顺序为:配置元件.前置处理器.定时器.取样器.后置处理器.断言.监听器 当一个测试计划中有多个线程组,当多个线程组都是是执行状态时,就会 ...

  3. StackGres 数据库平台工程,使用 Citus + Patroni 创建生产级高可用分布式 PostgreSQL 分片集群

    系列 StackGres, 可私有部署的云原生数据库平台工程 StackGres 数据库平台工程功能介绍与快速上手 StackGres 1.6 数据库平台工程集群配置管理(K8S Pods/Postg ...

  4. Ubuntu 22.04 LTS 安装lnmp

    Ubuntu 22.04 LTS 安装最新稳定版本nginx.mysql5.7和php7.2 全部apt-get安装,就是快,迅速.前提是需要在有网络环境的情况下哈!! 操作系统版本:Ubuntu 2 ...

  5. Mybatis-Flex核心功能之@Column

    1.是什么? MyBatis-Flex 提供了 @Column 用来对字段进行更多的配置 public @interface Column { /** * 字段名称 */ String value() ...

  6. MybatisPlus入门到进阶

    1.创建一个SpringBoot项目 2.导入相关依赖 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xs ...

  7. leetcode:354 俄罗斯套娃信封问题(LIS)

    解题思路: 根据题意,不难发现组合的元素,他们的长宽都是单调递增的,因此可以转化为最长上升子序列问题. 首先按照长度从小到大对信封进行排序,长度相同,按照宽度从大到小进行排序.因为当长度相同,因为可能 ...

  8. DC-3

    DC-3 前言:这个DC系列去年就做完了,但是因为那时候visualbox老崩搞得头大,一直漏了DC-3没做.现在重新搞好了来完结这个系列 扫存活的主机,显示只开了80 扫了一下目录,看了几个没有什么 ...

  9. Windows手工入侵排查思路

    文章来源公众号:Bypass Windows系统被入侵后,通常会导致系统资源占用过高.异常端口和进程.可疑的账号或文件等,给业务系统带来不稳定等诸多问题.一些病毒木马会随着计算机启动而启动并获取一定的 ...

  10. DVWA File Upload(文件上传)全等级

    File Upload(文件上传) 目录: File Upload(文件上传) 一句话木马的构成 1. Low 1.上传一句话木马1.php 2.中国蚁剑 2.Medium 3. High 4.Imp ...