【Java教程】Day22-02 Spring框架:IoC容器——原理

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

1. IoC的定义

IoC(Inversion of Control)即控制反转。简单来说,IoC指的是将对象创建与对象间的依赖关系的控制权从程序本身转交给容器管理。为了理解IoC的概念,我们首先来看一个常见的Java应用组件是如何协作的。

2. 传统的组件协作方式

我们假设有一个在线书店应用,BookService 需要访问数据库来获取书籍信息。为了实现这一点,BookService 类会直接创建数据库连接的实例:


 

 

javapublic 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;        }    }}

 

UserService 类也是如此,它也需要连接数据库:


 

 

javapublic 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;        }    }}

 

接下来,在 CartServlet 和 HistoryServlet 中,我们还需要实例化 BookService 和 UserService


 

 

javapublic 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);        ...    }}

 

上述代码的问题是,所有的组件都通过 new 操作符在内部创建,导致以下几个问题:

  1. 高耦合:每个组件都需要了解如何实例化其他组件(例如,BookService 和 UserService 都需要实例化 DataSource)。

  2. 难以共享组件:如果多个组件需要使用同一个 DataSource,那么它们必须相互协作来管理它的生命周期,这增加了复杂度。

  3. 资源管理困难:当有多个组件共享一个资源时(如 DataSource),如何确保它们都能正确地释放资源?

  4. 测试困难:测试某个组件时,必须依赖于真实的外部环境(如数据库),这使得测试变得复杂。

3. IoC的核心问题:谁来负责创建和管理组件?

IoC的主要目的是解决这些问题:谁负责创建组件,谁负责管理组件之间的依赖关系,以及如何销毁它们。

4. 传统的方式 vs IoC方式

在传统的应用程序中,控制权在应用程序本身。比如,在 CartServlet 中,它负责创建 BookService 和 UserService,而这些组件又依赖于 DataSource。这种方式的缺点是,每个组件都需要自己知道如何创建其他依赖的组件。

而在IoC模式下,控制权发生了反转——从应用程序本身转移到容器中。容器负责创建和管理所有组件,并且将依赖关系注入到这些组件中。

5. 依赖注入:IoC的核心思想

在IoC模式中,组件的依赖不再由组件自己创建,而是由外部容器通过“注入”的方式提供。具体来说,**依赖注入(DI,Dependency Injection)**就是指容器负责将一个组件所依赖的其他组件注入到该组件中,而不是由组件自己去创建这些依赖。

6. 示例:BookService中的依赖注入

为了演示这一点,假设我们将 BookService 中的 DataSource 的创建过程从 BookService 中移除,而是通过 setDataSource() 方法来注入 DataSource


 
javapublic class BookService {    private DataSource dataSource;    public void setDataSource(DataSource dataSource) {        this.dataSource = dataSource;    }}

 

通过这种方式,BookService 不再关心如何创建 DataSource,它只需要使用容器已经创建好的 DataSource 实例。

这种改变带来了如下好处:

  1. 降低了组件的耦合度BookService 不需要了解 DataSource 的创建过程。

  2. 便于共享组件:多个组件可以通过依赖注入共享同一个 DataSource 实例。

  3. 便于测试:可以通过注入虚拟的 DataSource(如内存数据库)来进行测试,而不必依赖真实的数据库。

7. IoC容器的配置

为了让容器知道如何创建和配置这些组件,我们需要提供一种方式告诉容器哪些组件需要实例化以及它们之间的依赖关系。Spring的IoC容器通过XML配置文件来实现这一点。例如,以下是一个XML配置示例:


 
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文件中,我们配置了三个组件:DataSourceBookService 和 UserService,并将 dataSource 注入到 BookService 和 UserService 中。

8. 依赖注入的方式

Spring的IoC容器支持两种主要的依赖注入方式:

  1. 属性注入(Setter Injection)
    使用 set 方法注入依赖,如上例所示,setDataSource() 方法将 DataSource 注入到 BookService 中。

  2. 构造方法注入(Constructor Injection)
    依赖通过构造方法传递。以下是通过构造方法注入的示例:


 

 

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

 

Spring同时支持属性注入和构造方法注入,并允许两者混合使用。

9. 无侵入的设计

Spring IoC容器是一个无侵入容器,这意味着应用程序的组件不需要实现Spring的特定接口,或者组件本身不知道它们是如何在Spring容器中运行的。无侵入设计带来了以下好处:

  1. 灵活性:组件可以既在Spring IoC容器中运行,也可以在不使用容器的情况下运行。

  2. 测试的便捷性:因为组件不依赖于Spring容器,所以它们可以单独进行测试,避免了对Spring容器的强依赖。

10. 小结

Spring的IoC容器通过依赖注入解决了传统Java程序中组件创建和依赖管理的痛点。它将对象创建与对象使用解耦,让开发者可以更加专注于业务逻辑的实现。同时,Spring的无侵入设计提高了系统的灵活性,使得应用程序能够在不依赖于Spring容器的情况下正常运行,极大地提升了开发效率和可维护性。

在下一章节,我们将深入探讨Spring容器如何管理对象生命周期和作用域,以及如何配置和使用Spring的不同类型容器。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值