首页 > 其他分享 >Ioc原理

Ioc原理

时间:2023-03-14 14:32:10浏览次数:32  
标签:BookService Ioc DataSource UserService 组件 原理 IoC dataSource

Spring提供的容器又称为IoC容器,什么是IoC?

IoC全称Inversion of Control,直译为控制反转。那么何谓IoC?在理解IoC之前,我们先看看通常的Java组件是如何协作的。

我们假定一个在线书店,通过​​BookService​​获取书籍:

public class BookService {
private HikariConfig config = new HikariConfig();
private DataSource dataSource = new HikariDataSource(config);

public Book getBook(long bookId) {
try (Connection conn = dataSource.getConnection()) {
...
return book;
}
}
}

为了从数据库查询书籍,​​BookService​​持有一个​​DataSource​​。为了实例化一个​​HikariDataSource​​,又不得不实例化一个​​HikariConfig​​。

现在,我们继续编写​​UserService​​获取用户:

public class UserService {
private HikariConfig config = new HikariConfig();
private DataSource dataSource = new HikariDataSource(config);

public User getUser(long userId) {
try (Connection conn = dataSource.getConnection()) {
...
return user;
}
}
}

因为​​UserService​​也需要访问数据库,因此,我们不得不也实例化一个​​HikariDataSource​​。

在处理用户购买的​​CartServlet​​中,我们需要实例化​​UserService​​和​​BookService​​:

public class CartServlet extends HttpServlet {
private BookService bookService = new BookService();
private UserService userService = new UserService();

protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
long currentUserId = getFromCookie(req);
User currentUser = userService.getUser(currentUserId);
Book book = bookService.getBook(req.getParameter("bookId"));
cartService.addToCart(currentUser, book);
...
}
}

类似的,在购买历史​​HistoryServlet​​中,也需要实例化​​UserService​​和​​BookService​​:

public class HistoryServlet extends HttpServlet {
private BookService bookService = new BookService();
private UserService userService = new UserService();
}

上述每个组件都采用了一种简单的通过​​new​​创建实例并持有的方式。仔细观察,会发现以下缺点:

  1. 实例化一个组件其实很难,例如,BookServiceUserService要创建HikariDataSource,实际上需要读取配置,才能先实例化HikariConfig,再实例化HikariDataSource
  2. 没有必要让BookServiceUserService分别创建DataSource实例,完全可以共享同一个DataSource,但谁负责创建DataSource,谁负责获取其他组件已经创建的DataSource,不好处理。类似的,CartServletHistoryServlet也应当共享BookService实例和UserService实例,但也不好处理。
  3. 很多组件需要销毁以便释放资源,例如DataSource,但如果该组件被多个组件共享,如何确保它的使用方都已经全部被销毁?
  4. 随着更多的组件被引入,例如,书籍评论,需要共享的组件写起来会更困难,这些组件的依赖关系会越来越复杂。
  5. 测试某个组件,例如BookService,是复杂的,因为必须要在真实的数据库环境下执行。

从上面的例子可以看出,如果一个系统有大量的组件,其生命周期和相互之间的依赖关系如果由组件自身来维护,不但大大增加了系统的复杂度,而且会导致组件之间极为紧密的耦合,继而给测试和维护带来了极大的困难。

因此,核心问题是:

  1. 谁负责创建组件?
  2. 谁负责根据依赖关系组装组件?
  3. 销毁时,如何按依赖顺序正确销毁?

解决这一问题的核心方案就是IoC。

传统的应用程序中,控制权在程序本身,程序的控制流程完全由开发者控制,例如:

​CartServlet​​创建了​​BookService​​,在创建​​BookService​​的过程中,又创建了​​DataSource​​组件。这种模式的缺点是,一个组件如果要使用另一个组件,必须先知道如何正确地创建它。

在IoC模式下,控制权发生了反转,即从应用程序转移到了IoC容器,所有组件不再由应用程序自己创建和配置,而是由IoC容器负责,这样,应用程序只需要直接使用已经创建好并且配置好的组件。为了能让组件在IoC容器中被“装配”出来,需要某种“注入”机制,例如,​​BookService​​自己并不会创建​​DataSource​​,而是等待外部通过​​setDataSource()​​方法来注入一个​​DataSource​​:

public class BookService {
private DataSource dataSource;

public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
}

不直接​​new​​一个​​DataSource​​,而是注入一个​​DataSource​​,这个小小的改动虽然简单,却带来了一系列好处:

  1. ​BookService​​不再关心如何创建​​DataSource​​,因此,不必编写读取数据库配置之类的代码;
  2. ​DataSource​​实例被注入到​​BookService​​,同样也可以注入到​​UserService​​,因此,共享一个组件非常简单;
  3. 测试​​BookService​​更容易,因为注入的是​​DataSource​​,可以使用内存数据库,而不是真实的MySQL配置。

因此,IoC又称为依赖注入(DI:Dependency Injection),它解决了一个最主要的问题:将组件的创建+配置与组件的使用相分离,并且,由IoC容器负责管理组件的生命周期。

因为IoC容器要负责实例化所有的组件,因此,有必要告诉容器如何创建组件,以及各组件的依赖关系。一种最简单的配置是通过XML文件来实现,例如:

<beans>
<bean id="dataSource" class="HikariDataSource" />
<bean id="bookService" class="BookService">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="userService" class="UserService">
<property name="dataSource" ref="dataSource" />
</bean>
</beans>

上述XML配置文件指示IoC容器创建3个JavaBean组件,并把id为​​dataSource​​的组件通过属性​​dataSource​​(即调用​​setDataSource()​​方法)注入到另外两个组件中。

在Spring的IoC容器中,我们把所有组件统称为JavaBean,即配置一个组件就是配置一个Bean。

依赖注入方式

我们从上面的代码可以看到,依赖注入可以通过​​set()​​方法实现。但依赖注入也可以通过构造方法实现。

很多Java类都具有带参数的构造方法,如果我们把​​BookService​​改造为通过构造方法注入,那么实现代码如下:

public class BookService {
private DataSource dataSource;

public BookService(DataSource dataSource) {
this.dataSource = dataSource;
}
}

Spring的IoC容器同时支持属性注入和构造方法注入,并允许混合使用。

无侵入容器

在设计上,Spring的IoC容器是一个高度可扩展的无侵入容器。所谓无侵入,是指应用程序的组件无需实现Spring的特定接口,或者说,组件根本不知道自己在Spring的容器中运行。这种无侵入的设计有以下好处:

  1. 应用程序组件既可以在Spring的IoC容器中运行,也可以自己编写代码自行组装配置;
  2. 测试的时候并不依赖Spring容器,可单独进行测试,大大提高了开发效率。

标签:BookService,Ioc,DataSource,UserService,组件,原理,IoC,dataSource
From: https://blog.51cto.com/u_15692628/6120412

相关文章

  • 索引原理与优化
    下面这条SQL,你怎么通过索引来提高查询效率呢?select*fromorderwherestatus=1 orderbycreate_timeasc更优的方式是建立一个status和create_time组合索引,这......
  • 为什么要使用线程池?创建线程池的参数有哪些?线程池的原理是什么?
    为什么要使用线程池?线程池有一下优点:线程在是非常宝贵的资源,使用线程池可以重复使用线程,避免频繁的创建和销毁线程所带来的系统损耗。可以根据系统的具体情况调整线程......
  • Webpack底层原理及核心概念实际应用(一篇封神系列)
    Webpack是一个流行的打包工具,用于构建大型前端项目。它在前端社区中广泛使用,并且已成为前端开发中必不可少的工具之一。本篇文章将对Webpack的工作原理进行深入讲解并附带代......
  • chatPDF原理学习
    一.很火的chatPDF.分析原因,想一下能和一本书沟通,这本身就是一件神奇的事情,再者,它能帮助你通过聊天的方式学习到其中的内容,调动了学习的乐趣.除了装X外,乐趣......
  • Mybatis的工作原理
    mybatis的工作原理mybatis基本工作原理封装sql->调用JDBC操作数据库->返回数据封装JDBCQ:JDBC是什么?Java中规范客户端程序如何访问数据库的应用程序接口J。驱动......
  • kubernetes集成GPU原理
    这里以NvidiaGPU设备如何在Kubernetes中管理调度为例研究,工作流程分为以下两个方面:如何在容器中使用GPUKubernetes如何调度GPU容器中使用GPU想要在容器中的应用可......
  • FastJson 反序列化漏洞原理分析
    Fastjson简介fastjson框架:https://github.com/alibaba/fastjsonfastjson-jndi:https://github.com/earayu/fastjson_jndi_pocfastjson是阿里巴巴开发的java语言编写的......
  • 知识图谱-TransE模型原理
    1.TransE模型介绍1.1TransE模型引入知识图谱补全任务的前提任务是知识表示学习,在知识表示学习中,最为经典的模型就是TransE模型,TransE模型的核心作用就是将知识图谱中的三......
  • 数据库查询语句执行原理
    当程序猿输入一条查询语句的时候,具体MySQL数据库是如何处理的呢?如何使其能达到更快更好的查询呢?接下来这篇文章将为你解释这个原理。1.基本路线​ 首先假定我们使用了......
  • java202302java学习笔记第十一天-方法的基本内存原理2
     ......