当前位置: 首页 > article >正文

Java-27 深入浅出 Spring - 实现简易Ioc-03 在上节的业务下手动实现IoC

点一下关注吧!!!非常感谢!!持续更新!!!

大数据篇正在更新!https://blog.csdn.net/w776341482/category_12713819.html

在这里插入图片描述

目前已经更新到了:

  • MyBatis(已更完)
  • Spring(正在更新…)

在这里插入图片描述

上节进度

上节完成了基本代码的编写,本节我们继续。本节将进行 IoC的实现。

IoC 实现

此时我们需要一个 Bean 的管理容器,当我们需要 new 对象的时候,可以直接从容器中获取出来。但是我们需要在程序启动的时候(当然也可以懒加载)就把这些对象初始化出来,所以我们需要 XML 来告诉容器需要加载什么内容。

IoC(Inversion of Control,控制反转)简介

控制反转(IoC)是一种设计原则,用于实现组件间的解耦,是面向对象编程中非常重要的概念之一。IoC的核心思想是将程序中对对象的控制权从调用方转移到框架或容器中,使得对象之间的依赖关系由容器来管理。

IoC 的特点

解耦

IoC通过将依赖的管理和创建责任交给容器,减少了模块之间的耦合性,增强了系统的可维护性和可扩展性。

动态依赖管理

容器根据配置或注解动态地将依赖注入到对象中,而不需要硬编码的依赖关系。

灵活性

对象的依赖可以在运行时动态修改,只需更改配置即可,不需要修改代码。

IoC 的应用场景

IoC被广泛应用于各种软件开发框架和项目中,以下是几个典型应用:

  • Spring Framework:Spring框架使用IoC容器管理Bean的生命周期和依赖关系。开发者只需定义组件及其依赖,具体的实例化和依赖注入由Spring容器完成。
  • Guice 和 Dagger:这些轻量级DI框架在Java项目中也很流行,提供了简单高效的依赖注入功能。
  • 前端框架(如Angular):Angular中也实现了IoC,通过其依赖注入系统来管理组件和服务之间的依赖关系。

IoC 的优势

  • 增强模块化:通过减少模块之间的直接依赖,促进模块化设计。
  • 提高测试性:依赖注入可以方便地替换依赖对象,从而支持单元测试。
  • 增强灵活性:通过配置或注解动态注入依赖,无需修改代码即可适应变化。
  • 便于维护:解耦使得系统在增加或修改功能时影响最小。

IoC 的限制

  • 学习曲线:初学者需要一定时间理解IoC和DI的概念。
  • 运行时性能开销:IoC容器在运行时解析依赖关系可能会引入一些性能开销。
  • 复杂性:在大型项目中,过度使用IoC可能导致配置和依赖关系变得复杂。

Resources

Resources 目录下

beans.xml

我们先编写一个 XML 来保存我们的 Bean 的信息:

<?xml version="1.0" encoding="UTF-8" ?>
<!-- BeanFactory 类会进行处理这块内容 -->
<beans>
    <!-- WzkConnectionUtils 交给容器管理 -->
    <bean id="wzkConnectionUtils" class="wzk.utils.WzkConnectionUtils"></bean>
    <!-- 依赖了工具类 WzkConnectionUtils -->
    <!-- id 是放入到容器中的名称 -->
    <bean id="wzkAccountDao" class="wzk.dao.impl.JdbcWzkAccountDaoImpl">
        <!-- name是成员变量名字 ref是引用从容器中拿对象 -->
        <property name="WzkConnectionUtils" ref="wzkConnectionUtils"/>
    </bean>
    <!-- 依赖了 WzkAccountDao -->
    <bean id="wzkTransferService" class="wzk.service.impl.WzkTransferServiceImpl">
        <!-- name是成员变量名字 ref是引用从容器中拿对象 -->
        <property name="WzkAccountDao" ref="wzkAccountDao"></property>
    </bean>
</beans>

对应的内容截图如下所示:
在这里插入图片描述
注意:需要将 beans.xml 放置到 resources 目录下。
在这里插入图片描述

Proxy

BeanFactory

public class BeanFactory {

    private BeanFactory() {}

    /**
     * 全局容器 对象都存储到这里
     */
    private static Map<String, Object> map = new HashMap<>();

    static {
        // 对 XML 进行处理,这里可以参考我们之前 手写的MyBatis的部分
        // 静态代码块 来进行初始化
        InputStream resourceAsSteam = BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml");
        SAXReader saxReader = new SAXReader();
        try {
            Document document = saxReader.read(resourceAsSteam);
            Element rootElement = document.getRootElement();
            List<Element> beanList = rootElement.selectNodes("//bean");
            for (Element element : beanList) {
                // 处理每个 Bean 元素
                String id = element.attributeValue("id");
                String clazz = element.attributeValue("class");
                // 反射拿到对象
                Class<?> aClass = Class.forName(clazz);
                Object object = aClass.newInstance();
                // 保存到容器中
                map.put(id, object);
            }

            List<Element> propertyList = rootElement.selectNodes("//property");
            for (Element element : propertyList) {
                // 处理每个依赖关系
                String name = element.attributeValue("name");
                String ref = element.attributeValue("ref");
                // 拿到父级节点 它下边的 property都是它的依赖
                Element parentElement = element.getParent();
                String parentId = parentElement.attributeValue("id");
                // 通过父级的 ID 拿到对象
                Object parentObject = map.get(parentId);
                // 拿到父级的所有方法 Get Set 等等
                Method[] methods = parentObject.getClass().getMethods();
                for (Method method : methods) {
                    // 如果是 set 方法的话 我们调用就可以将它的依赖赋值给父级
                    if (method.getName().equalsIgnoreCase("set" + name)) {
                        method.invoke(parentObject, map.get(ref));
                    }
                }
                // 记得更新容器的内容
                map.put(parentId, parentObject);
            }
            System.out.println("BeanFactory 初始化完毕 map:" + map);
        } catch (Exception e) {
            System.out.println("BeanFactory 初始化失败");
            e.printStackTrace();
        }
    }

    public static Object getBean(String id) {
        return map.get(id);
    }

}

对应的截图如下所示:

在这里插入图片描述

Controller

WzkServlet

我们对 WzkServlet 进行一些修改:

@WebServlet(name="wzkServlet", urlPatterns = "/wzkServlet")
public class WzkServlet extends HttpServlet {

    // ========================== 2 ==========================
    // 由 BeanFactory 处理
    private WzkTransferService wzkTransferService = (WzkTransferService) BeanFactory.getBean("wzkTransferService");
    // =======================================================

    @Override
    protected void doGet(javax.servlet.http.HttpServletRequest req, javax.servlet.http.HttpServletResponse resp) throws javax.servlet.ServletException, IOException {
        System.out.println("=== WzkServlet doGet ===");
        // ======================= 1 =============================
        // 由于没有 Spring 的帮助 我们就需要手动去创建和维护依赖之间的关系
        // 组装 DAO DAO层依赖于 ConnectionUtils 和 DruidUtils
        // JdbcWzkAccountDaoImpl jdbcWzkAccountDaoImpl = new JdbcWzkAccountDaoImpl();
        // jdbcWzkAccountDaoImpl.setConnectionUtils(new WzkConnectionUtils());
        // 组装 Service
        // WzkTransferServiceImpl wzkTransferService = new WzkTransferServiceImpl();
        // wzkTransferService.setWzkAccountDao(jdbcWzkAccountDaoImpl);
        // ======================================================
        // 执行业务逻辑
        try {
            wzkTransferService.transfer("1", "2", 100);
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("=== transfer error ====");
        }
        resp.setContentType("application/json;charset=utf-8");
        resp.getWriter().print("=== WzkServlet doGet ===");
    }

}

WzkServlet(2)

还有一种写法也是类似的,这样的处理方式也可以顺利执行的。

// ========================== 3 ==========================
    // 另一种方式 相同的
    private WzkTransferService wzkTransferService;
    @Override
    public void init() throws ServletException {
        super.init();
        this.wzkTransferService = (WzkTransferService) BeanFactory.getBean("wzkTransferService");
    }
    // ======================================================

测试运行

我们运行代码之后,可以看到控制台如下所示:

BeanFactory 初始化完毕 map:{wzkTransferService=wzk.service.impl.WzkTransferServiceImpl@5dfdd67c, wzkAccountDao=wzk.dao.impl.JdbcWzkAccountDaoImpl@380d9fc0, wzkConnectionUtils=wzk.utils.WzkConnectionUtils@2d150354}
=== WzkServlet doGet ===
Loading class `com.mysql.jdbc.Driver'. This is deprecated. The new driver class is `com.mysql.cj.jdbc.Driver'. The driver is automatically registered via the SPI and manual loading of the driver class is generally unnecessary.
1118, 2024 6:24:24 下午 com.alibaba.druid.pool.DruidDataSource info
信息: {dataSource-1} inited
transfer fromResult: 1 toResult: 1

对应的控制台结束如下所示:
在这里插入图片描述

查看结果

数据库的结果已经变化了,可以看到结果如下:
在这里插入图片描述
当然,刚才的代码打印的内容,也说明我们的代码是正常工作的。
(你可以打断点进行调试)

目前问题

我们虽然已经实现了简易的 IoC,但是对于当前业务来说,我们还需要对事务进行控制,此时需要我们实现一个事务的管理器,将采取和数据库一样,用 ThreadLocal 来进行控制。


http://www.kler.cn/a/443835.html

相关文章:

  • Oracle:数据库的顶尖认证
  • 摩尔信使MThings的逻辑控制功能范例
  • 山景BP1048蓝牙音频任务后台运行设置方法
  • python 曲线拟合,曲线拟合交点
  • 通过阿里云 Milvus 与 PAI 搭建高效的检索增强对话系统
  • Mac 开机 一闪框 mediasharingd
  • 1688所有商品获取API接口详解
  • QAnything源码学习
  • leetcode74:搜索二维矩阵
  • 从 PDF 到 Word:一个简单的 PythonGUI转换器
  • 请给我详细讲解vue.config.js的配置内容
  • React状态管理常见面试题目(二)
  • Vue前端开发-数据缓存
  • K-Means 聚类:数据挖掘的瑞士军刀
  • 将java项目部署到linux
  • Selenium 深度解析:自动化浏览器操作的利器
  • PPT中添加多个图片
  • 解决echarts图宽度自适应问题,设置100%宽度显示100px
  • UDP网络编程套接
  • Java.10--IO流
  • 修改openjdk17 java/lang/String.java 类源码,增加一个native本地方法打印固定字符串功能
  • 图书馆管理系统(一)基于jquery、ajax
  • Linux 显示系统活动进程状态命令 ps 详细介绍
  • 如何有效修复ffmpeg.dll错误:一站式解决方案指南
  • Linux dd 命令详解:工作原理与实用指南(C/C++代码实现)
  • 单节点calico性能优化