从0到1实现IOC

news2024/11/18 5:47:38

一、什么是 IOC

我们先来看看spring官网对IOC的定义:

IoC is also known as dependency injection (DI). It is a process whereby objects define their dependencies, that is, the other objects they work with, only through constructor arguments, arguments to a factory method, or properties that are set on the object instance after it is constructed or returned from a factory method. The container then injects those dependencies when it creates the bean. This process is fundamentally the inverse, hence the name Inversion of Control (IoC), of the bean itself controlling the instantiation or location of its dependencies by using direct construction of classes, or a mechanism such as the Service Locator pattern.

就不逐句翻译了,主要意思就是,IOC也被称为依赖注入,它是一个容器创建bean时将依赖对象注入的过程,而这些依赖对象在该bean中仅仅通过构造参数、工厂方法的参数或属性来指定。

控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低代码之间的耦合度,提高代码的可重用性和可维护性。通过控制反转,对象在被创建的时候,由一个容器将其所依赖的对象的引用传递给它,即我们常说的,依赖被注入到对象中。要自己设计实现一个IOC,首先要理解IOC的作用, 最基础且必要功能有两个:a、存储对象, b、有注解注入的功能;

二、为什么需要 IOC

在传统java项目中对象是通过new 获得的,并需要自己维护对象的生命周期,在一个大型项目中,这个将是繁杂冗余浪费机器资源的,如图(1) ,IOC就是来解决该问题的,由IOC容器来维护类的生命周期和类之间的引用,如图(2),对象的关系图也即变成图(3) 的效果,看着是否清爽多了,对人是这样,对机器也是这样

                                                                      (图1)

 

                                                   (图2)

                                                               (图3)

三、怎么实现IOC

Java语言允许通过程序化的方式间接对Class进行操作,Class文件由类装载器装载后,在JVM中将形成一份描述Class结构的元信息对象,通过该元信息对象可以获知Class的结构信息:如构造函数、属性和方法等。Java允许用户借由这个Class相关的元信息对象间接调用Class对象的功能,这就为使用程序化方式操作Class对象开辟了途径。下面我们着手实现一个IOC容器,先参照下最经典的Spring容器。

 

 

上图是Spring如何工作的高级视图,业务类与配置元数据相结合,这样在创建和初始化ApplicationContext(spring 容器)之后,就有了一个完全配置和可执行的系统或应用程序,Spring IOC的实现官网等都有比较详细的介绍了,这里就不做赘述。

在这里,参照spring容器视图,为了更好的理解和实现IOC我们从基础0代码出发,一步一步详细实现一个简单的通过注解的方式实现IOC容器功能, 一个IOC容器中最核心的当属容器接口,比如我们定义为:Container。那么这个容器里应该有什么呢?即需要具备什么功能呢?要能存储对象,其次包括括获取、注册、删除对象的方法,所以可以通过一个map来实现容器的核心。

1、容器接口

public interface Container {

/**
* 根据Class获取Bean
* @param clazz
* @return
*/
public <T> T getBean(Class<T> clazz);

/**
* 根据名称获取Bean
* @param name
* @return
*/
public <T> T getBeanByName(String name);

/**
* 注册一个Bean到容器中
* @param object
*/
public Object registerBean(Object bean);

/**
* 注册一个Class到容器中
* @param clazz
*/
public Object registerBean(Class<?> clazz);

/**
* 注册一个带名称的Bean到容器中
* @param name
* @param bean
*/
public Object registerBean(String name, Object bean);

/**
* 删除一个bean
* @param clazz
*/
public void remove(Class<?> clazz);

/**
* 根据名称删除一个bean
* @param name
*/
public void removeByName(String name);

/**
* @return 返回所有bean对象名称
*/
public Set<String> getBeanNames();

/**

* 初始化装配
*/
public void initWired();

}



2、容器实现
 

这里将所有Bean的名称存储在 beanKeys 这个map中,将所有的对象存储在 beans 中,用 beanKeys 维护名称和对象的关系。

在装配的时候步骤如下

public class ContainerImpl implements Container {


   /**
    * 保存所有bean对象,格式为com.test.TestA TestA@516
    */
   private Map<String, Object> beans;

   /**
    * 存储注入属性名和对象的全名称,格式testA  com.test.TestA
    */
   private Map<String, String> beanKeys;


   public ContainerImpl() {
      this.beans = new ConcurrentHashMap<String, Object>();
      this.beanKeys = new ConcurrentHashMap<String, String>();
   }

   /**
    * 根据类获取指定的对象
    * @param clazz
    * @param <T>
    * @return
    */
   @Override
   public <T> T getBean(Class<T> clazz) {
      String name = clazz.getName();
      Object object = beans.get(name);
      if(null != object){
         return (T) object;
      }
      return null;
   }

   /**
    * 获取指定的对象
    * @param name 对象名称
    * @param <T>
    * @return
    */
   @Override
   public <T> T getBeanByName(String name) {
      String className = beanKeys.get(name);
      Object obj = beans.get(className);
      if(null != obj){
         return (T) obj;
      }
      return null;
   }

   /**
    * 通过对象的方式进行注册
    * @param bean
    * @return
    */
   @Override
   public Object registerBean(Object bean) {
      String name = bean.getClass().getName();
      String lastName = name.substring(name.lastIndexOf(".")+1);
      lastName = StringUtils.uncapitalize(lastName);
      beanKeys.put(lastName, name);
      beans.put(name, bean);
      return bean;
   }

   /**
    * 通过类方式进行注册
    * @param clazz
    * @return
    */
   @Override
   public Object registerBean(Class<?> clazz) {
      String name = clazz.getName();
      String lastName = name.substring(name.lastIndexOf(".")+1);
      lastName = StringUtils.uncapitalize(lastName);
      beanKeys.put(lastName, name);
      Object bean = null;
      try {
         bean = ReflectUtil.newInstance(clazz);
         beans.put(name, bean);
      }catch (Exception e) {
         return null;
      }
      return bean;
   }

   /**
    * 通过指定对象名称方式进行注册
    * @param name
    * @param bean
    * @return
    */
   @Override
   public Object registerBean(String name, Object bean) {
      String className = bean.getClass().getName();
      beanKeys.put(name, className);
      beans.put(className, bean);
      return bean;
   }

   /**
    * 获取对象名称集合
    * @return
    */
   @Override
   public Set<String> getBeanNames() {
      return beanKeys.keySet();
   }

   /**
    * 根据类卸载一个对象
    * @param clazz
    */
   @Override
   public void remove(Class<?> clazz) {
      String className = clazz.getName();
      if(null != className && !className.equals("")){
         beanKeys.remove(className);
         beans.remove(className);
      }
   }

   /**
    * 指定卸载某个对象
    * @param name
    */
   @Override
   public void removeByName(String name) {
      String className = beanKeys.get(name);
      if(null != className && !className.equals("")){
         beanKeys.remove(name);
         beans.remove(className);
      }
   }

   /**
    * 将注册的bean通过循环的方式注入的容器中,后续通过容器进行使用各个bean对象
    */
   @Override
   public void initWired() {
      Iterator<Entry<String, Object>> it = beans.entrySet().iterator();
      while (it.hasNext()) {
         Map.Entry<String, Object> entry = (Map.Entry<String, Object>) it.next();
         Object object = entry.getValue();
         injection(object);
      }
   }

   /**
    * 本例子是展示了,通过注解的方式进行注入
    * @param object
    */
   public void injection(Object object) {

      try {
         // 所有字段
         Field[] fields = object.getClass().getDeclaredFields();
         for (Field field : fields) {
            // 需要注入的字段,找到注解的对象(@autowired注解)
//          Autowired autoWired = field.getAnnotation(Autowired.class);
            // 需要注入的字段,找到注解的对象(@Resource注解)
            Resource autoWired = field.getAnnotation(Resource.class);
            if (null != autoWired) {

               // 要注入的字段
               Object autoWiredField = null;

               String name = field.getName();

               String className = beanKeys.get(name);
               if (StringUtils.isNotBlank(className)) {
                  autoWiredField = beans.get(className);
               }

               if (autoWiredField == null) {
                  throw new RuntimeException("Unable to load " + field.getType().getCanonicalName());
               }

               boolean accessible = field.isAccessible();
               field.setAccessible(true);
               field.set(object, autoWiredField);
               field.setAccessible(accessible);
            }
         }
      } catch (Exception e) {
         e.printStackTrace();
      }
   }
}


3、测试类

public class IocTest {

private static Container container = new ExampleContainer();

public static void baseTest(){

// 将TestB注册到容器中(通过类名方式)
container.registerBean(TestB.class);

// 将TestA注册到容器中(通过对象方式)
TestA a = new TestA();
container.registerBean(a);

// 进行初始化(依赖注入)
container.initWired();

// 获取bean
TestB test = container.getBean(TestB.class);

// 执行对应bean里的方法
test.doTest();
}

public static void main(String[] args) {
baseTest();
}
}

4、测试BEAN

public class TestA {
   
   public void work(){
      System.out.println("TestA executes,implment IOC...");
   }
}
public class TestB {

   /**
    * TestB中注入TestA
    */
   @Autowired
   private TestA testA;

   public void doTest (){
      System.out.println("testB executes...");
      testA.work();
   }

}

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

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

相关文章

阿里工程师手打的MySQL学习笔记,轻松拿捏MySQL

我们都知道阿里经历过几次重大的技术变革&#xff0c;其中就包括放弃Oracle和Hadoop&#xff0c;全面拥抱MySQL。 讲道理其实靠OracleHadoop也能撑一撑&#xff0c;为啥偏得变。当然肯定不是因为阿里爸爸没钱&#xff0c;而是即便再花个几千万还是不能彻底解决问题&#xff0c…

压力测试遭遇大量TIME_WITE之后(这样解决)

前言&#xff1a;http协议是互联网中最常使用的应用层协议&#xff0c;它的绝大多数实现是基于TCP协议的。 目录 一 问题描述 二 问题跟踪 三 跟进分析 四 解决方法 一、问题描述 某天&#xff0c;在对一个提供http接口的后台服务进行压力测试过程中&#xff0c;我们设定了…

科班出身又如何?这类人连外包都不要...

在软件测试这个领域&#xff0c;多数人对于外包公司是有戴有色眼镜看待的&#xff0c;外包测试员往往会处于一个比较尴尬的局面。主要是由于雇主公司比较核心或者底层的东西是不会让外包人员作的。外包人员一般做的都是“边角料”。而这些活往往对于技术要求不高&#xff0c;所…

python接口自动化 —— 接口测试工具介绍(详解)

简介 “工欲善其事必先利其器”&#xff0c;通过前边几篇文章的介绍&#xff0c;大家大致对接口有了进一步的认识。那么接下来让我们看看接口测试的工具有哪些。 目前&#xff0c;市场上有很多支持接口测试的工具。利用工具进行接口测试&#xff0c;能够提供测试效率。例如&…

运维小白必学篇之基础篇第十集:系统启动流程实验

系统启动流程实验 实验作业&#xff1a; 1、现在有一台服务器因为长时间不使用&#xff0c;管理员密码已经丢失&#xff0c;现在想要启动该服务器&#xff0c;如何操作 第一步&#xff1a;开启系统&#xff0c;在GRUB界面按E进行编辑 在linux16行中centos/swap后添加 rd.break参…

【AI】InsCode AI 创作助手 --使用心得

CSDN AI写作助手上线了&#xff01;InsCode AI 创作助手不仅能够帮助用户高效创作文章&#xff0c;而且能够作为对话式AI回答你想知道的问题。成倍提高生产力&#xff01;欢迎大家使用新功能后分享自己的使用心得与建议&#xff01; 文章目录 一、你平时会使用这类AI工具吗&am…

navicat与SQLyog的区别

在之前的学习中由于先学的SQL Server&#xff0c;后来才学的MySQL&#xff0c;导致我刚学习的时候冥冥之中感觉到那有点不对劲&#xff0c;但是又说不出来。通过进行深入的学习解除到了Navicat Premium和SQLyog这两个工具&#xff0c;才让我明白了MySQL与之前学习的内容是有所出…

影响代理ip纯净度的原因及目标网站如何识别代理ip

网络上代理ip很多&#xff0c;但真正可以为我们所用的大部分都是付费ip&#xff0c;那为什么免费ip不能为我们所用呢&#xff1f;下面我们就纯净度和目标网站是如何识别代理ip来分析一下。 一、纯净度 ip纯净度是什么意思呢&#xff1f;简单一点开始就是指使用这个ip的人少&…

教你 5 分钟快速部署开源网关

最近在研究开源网关&#xff0c;找了一圈&#xff0c;发现这个叫 Apinto 的开源网关符合我的需求&#xff0c;下面我将演示如何部署这样一个开源网关。 Apinto功能架构图 开始部署 部署资源 设备推荐配置设备数量部署对象4核8G&#xff0c;250G磁盘空间&#xff0c;2.5GHz1控制…

SQL-基础

SQL-小基础 1 SQL简介 英文&#xff1a;Structured Query Language&#xff0c;简称 SQL结构化查询语言&#xff0c;一门操作关系型数据库的编程语言定义操作所有关系型数据库的统一标准对于同一个需求&#xff0c;每一种数据库操作的方式可能会存在一些不一样的地方&#xff…

RPC接口测试技术-Tcp 协议的接口测试

【摘要】 首先明确 Tcp 的概念&#xff0c;针对 Tcp 协议进行接口测试&#xff0c;是指基于 Tcp 协议的上层协议比如 Http &#xff0c;串口&#xff0c;网口&#xff0c; Socket 等。这些协议与 Http 测试方法类似&#xff08;具体查看接口自动化测试章节&#xff09;&#xf…

Ingress Controller高可用部署

Ingress-controller 高可用解说 Ingress Controller 是集群流量的接入层&#xff0c;对它做高可用非常重要&#xff0c;可以基于 keepalive 实现 nginx-ingress-controller 高可用&#xff0c;具体实现如下&#xff1a; Ingress-controller 根据 Deployment nodeSeletorpod 反…

揭秘广告投放的9大关键环节,了解真相让你成为广告投放高手!

正式开始本章的内容之前&#xff0c;先来简单复习一下上一章的主要内容&#xff1a; 核心要点1&#xff1a;广告投放的意义主要有三点&#xff1a;传播品牌、宣传产品、促成转化&#xff1b; 核心要点2&#xff1a;广告投放的主要流程有这样 9 个阶段&#xff1a; 本章我们以…

OpenCV(图像处理)-基于Python-图像的基本变换-平移-翻转-仿射变换-透视变换

1. 概述2. 接口介绍resize()flip()rotate()仿射变换warpAffine()getRotationMatrix2D()-变换矩阵1getAffineTransform()-变换矩阵2 透视变换warpPerspective()getPerspectiveTransform() 1. 概述 为了方便开发人员的操作&#xff0c;OpenCV还提供了一些图像变换的API&#xff…

Qt 去除标题栏不同方法不同平台差异探究

Qt 版本&#xff1a;Qt 6.5.0 Windows 11 当窗体为QWidget时 setWindowFlags(Qt::FramelessWindowHint);// 窗口不能缩放setWindowFlags(Qt::CustomizeWindowHint);// 窗口支持缩放&#xff0c;且窗体四角为圆角CustomizeWindowHintFramelessWindowHint 当窗体为QMainWindow时…

什么是第三方付费模式?用“尤伯罗斯模式”让你的商品由别人买单

什么是第三方付费模式&#xff1f;用“尤伯罗斯模式”让你的商品由别人买单 微三云营销策划胡总监给大家介绍一下&#xff0c;什么是第三方付费模式&#xff1f; 当同质化产品日趋严重的时候&#xff0c;改变客户接受产品价值及服务的模式创新就是商业模式的创新&#xff0c;以…

计算机硬件(2)

1.4 计算机硬件 电脑硬件是计算机系统中最基本的部分&#xff0c;可理解成看得见、摸得着的实物。一台电脑是由许多的零部件组成&#xff0c;只有这些零部件组合在一起协调工作&#xff0c;才能称之为电脑。一般计算机的硬件可以简单分为以下部件&#xff1a; 主机&#xff08…

Nginx location匹配优先级 与 Rewrite

目录 前言locationlocation 常用的匹配规则location 优先级实例演示 rewrite前言rewrite跳转实现rewrite 执行顺序语法格式rewrite全局变量实例演示 前言 从功能看 rewrite 和 location 似乎有点像&#xff0c;都能实现跳转&#xff0c;主要区别在于 rewrite 是在同一个域名内…

抛弃传统网络?SDN协议、标准、接口对比分析

随着网络规模的不断扩大和复杂性的增加&#xff0c;传统的网络架构已经难以满足日益增长的网络需求。SDN&#xff08;Software Defined Networking&#xff09;技术的出现&#xff0c;为网络的管理和控制带来了革命性的变化。SDN的核心思想是将网络的控制和管理从传统的分散式硬…

STL-string-2

Iterators Capacity resize void resize (size_t n);void resize (size_t n, char c); Resize string 将字符串的大小调整为n个字符的长度。 如果n小于当前字符串长度&#xff0c;则当前值将缩短为其第一个n字符&#xff0c;删除第n个字符之后的字符。 如果n大于当前字符串长…