【Spring】IoC 和 DI的关系、简单使用,从“硬编码“到“优雅解耦“:IoC与DI的Spring蜕变之旅

1.IoC 和 DI的关系

IoC(Inversion of Control,控制反转)和DI(Dependency Injection,依赖注入)是Spring框架中紧密相关但又有所区别的两个概念。理解它们的联系,可以帮助我们更深刻地掌握Spring的核心设计理念。以下是IoC和DI的联系分析:

  1. IoC是目标,DI是手段
    IoC是一种设计思想,核心是将对象的创建、依赖管理和生命周期控制权从代码本身“反转”给外部容器(比如Spring容器)。它解决的是“谁来控制”的问题,强调控制权的转移。
    DI是实现IoC的具体方式之一。通过依赖注入,容器主动将对象所需的依赖(其他对象或资源)提供给目标对象,而不是由对象自己去创建或查找依赖。换句话说,DI是IoC的一种“落地实践”。

    联系IoC是宏观理念,描述了“反转”的思想,而DI是微观操作,回答了“怎么反转”的问题。在Spring中,IoC通常通过DI来实现,二者相辅相成。

    举例:想象你是个导演(程序),传统方式是你亲自挑选演员(依赖)。IoC让你把选角权交给经纪公司(容器),而DI就是经纪公司把演员送到你面前的具体行动。

  2. 共同目标:解耦
    IoC和DI的最终目的都是降低代码之间的耦合度。

    在没有IoC/DI时,对象A如果需要对象B,会直接new B(),导致A和B紧耦合。一旦B变了,A也要改。

    IoC将B的创建交给容器,A只需要声明“我需要一个B”。DI则通过构造器、Setter等方式把B注入到A中,A不再关心B的来源。

    联系:IoC提供了“解耦”的思想框架,DI是实现这一框架的具体工具,二者共同让代码更灵活、更易维护。

2.IoC & DI的使用

2.1示例1

在Spring中,IoC和DI是密不可分的搭档。IoC负责反转控制权,DI则是实现这一反转的具体手段。让我们通过一个简单的例子,看看它们如何在Spring中工作。

假设我们要开发一个“用户服务”,需要依赖一个“用户数据访问对象(DAO)”来查询数据库。传统方式是这样的:

public class UserService {
    
    
    private UserDao userDao = new UserDaoImpl(); // 自己创建依赖
    public void getUser() {
    
    
        userDao.findUser();
    }
}

这种方式耦合度高,如果UserDaoImpl改了,UserService也得跟着改。现在我们用Spring的IoC和DI重写

  1. 定义Bean和依赖关系(通过Java配置类):

    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    public class AppConfig {
          
          
        @Bean
        public UserDao userDao() {
          
          
            return new UserDaoImpl();
        }
    
        @Bean
        public UserService userService(UserDao userDao) {
          
           // DI注入
            return new UserService(userDao);
        }
    }
    
  2. UserService接受注入:

    public class UserService {
          
          
        private UserDao userDao;
    
        public UserService(UserDao userDao) {
          
           // 通过构造器注入
            this.userDao = userDao;
        }
    
        public void getUser() {
          
          
            userDao.findUser();
        }
    }
    
  3. 启动Spring容器并使用

    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    
    public class Main {
          
          
        public static void main(String[] args) {
          
          
            AnnotationConfigApplicationContext context = 
                new AnnotationConfigApplicationContext(AppConfig.class);
            UserService userService = context.getBean(UserService.class);
            userService.getUser();
        }
    }
    

这里,Spring容器通过IoC接管了UserServiceUserDao的创建,而DI则通过构造器将UserDao注入到UserService中。如果需要换成另一个UserDao实现(比如UserDaoMock),只需改配置类,UserService完全不用动。

2.2示例2

在这里插入图片描述

2.3 示例3

BookController:

import org.example.booksmanagementsystem.book.service.BookService;
import org.example.booksmanagementsystem.book.model.BookInfo;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
@RequestMapping("book")
public class BookController {
    
    
    // 加载数据
    private BookService bookService=new BookService();

    @RequestMapping("getBookList")
    public List<BookInfo> getBookList(){
    
    

        // 获取图书数据
        List<BookInfo> bookInfos = bookService.getBookList(); ;

        //返回图书列表
        return bookInfos;

    }

}

BookService

import org.example.booksmanagementsystem.book.dao.BookDao;
import org.example.booksmanagementsystem.book.model.BookInfo;

import java.util.List;

public class BookService {
    
    
    //加载数据
    private BookDao bookDao = new BookDao();

    //创建虚拟的数据
    public List<BookInfo> getBookList(){
    
    

        // 获取图书数据
        List<BookInfo> bookInfos = bookDao.getBookList();

        // 对图书进行处理
        //设置书的状态
        for(BookInfo bookInfo:bookInfos){
    
    
            if(bookInfo.getStatus()==1){
    
    
                bookInfo.setStatusCN("可借阅");
            }else{
    
    
                bookInfo.setStatusCN("不可借阅");
            }
        }

        return bookInfos;
    }
}

BookDao

import org.example.booksmanagementsystem.book.model.BookInfo;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class BookDao {
    
    
    //创建虚拟的数据
    public List<BookInfo> getBookList(){
    
    
        //创建15条数据
        List<BookInfo> bookInfos = new ArrayList<>(15);
        for (int i=0; i<15; i++){
    
    
            BookInfo bookInfo = new BookInfo();
            bookInfo.setId(i);
            bookInfo.setBookName("图书"+i);
            bookInfo.setAuthor("作者"+i);
            bookInfo.setCount(new Random().nextInt(200));//生成200以下随机数
            bookInfo.setPrice(new BigDecimal(new Random().nextInt(100)));
            bookInfo.setPublish("出版社"+1);
            bookInfo.setStatus(i%5==0?2:1);

            //添加到列表
            bookInfos.add(bookInfo);
        }

        return bookInfos;
    }
}

⽬标: 把BookDao, BookService 交给Spring管理, 完成Controller层, Service层, Dao层的解耦步骤:
1.Service层及Dao层的实现类,交给Spring管理: 使⽤@Component注解;
2.在Controller层 和Service层 注⼊运⾏时依赖的对象: 使⽤@Autowired注解实现;

实现:
1.把BookDao 交给Spring管理, 由Spring来管理对象

@Component //把BookDao交给Spring管理
public class BookDao {
    
    
    //创建虚拟的数据
    public List<BookInfo> getBookList(){
    
    
        //创建15条数据
        List<BookInfo> bookInfos = new ArrayList<>(15);
        for (int i=0; i<15; i++){
    
    
            BookInfo bookInfo = new BookInfo();
            bookInfo.setId(i);
            bookInfo.setBookName("图书"+i);
            bookInfo.setAuthor("作者"+i);
            bookInfo.setCount(new Random().nextInt(200));//生成200以下随机数
            bookInfo.setPrice(new BigDecimal(new Random().nextInt(100)));
            bookInfo.setPublish("出版社"+1);
            bookInfo.setStatus(i%5==0?2:1);

            //添加到列表
            bookInfos.add(bookInfo);
        }

        return bookInfos;
    }
}

2.把BookService 交给Spring管理, 由Spring来管理对象

@Component //把BookService 交给Spring管理
public class BookService {
    
    
    //加载数据
    private BookDao bookDao = new BookDao();

    //创建虚拟的数据
    public List<BookInfo> getBookList(){
    
    

        // 获取图书数据
        List<BookInfo> bookInfos = bookDao.getBookList();

        // 对图书进行处理
        //设置书的状态
        for(BookInfo bookInfo:bookInfos){
    
    
            if(bookInfo.getStatus()==1){
    
    
                bookInfo.setStatusCN("可借阅");
            }else{
    
    
                bookInfo.setStatusCN("不可借阅");
            }
        }

        return bookInfos;
    }
}

3.删除创建BookDao的代码, 从Spring中获取对象

@Component
public class BookService {
    
    
    //加载数据
//    private BookDao bookDao = new BookDao();

    @Autowired  //从容器中注入
    private BookDao bookDao;

    //创建虚拟的数据
    public List<BookInfo> getBookList(){
    
    

        // 获取图书数据
        List<BookInfo> bookInfos = bookDao.getBookList();

        // 对图书进行处理
        //设置书的状态
        for(BookInfo bookInfo:bookInfos){
    
    
            if(bookInfo.getStatus()==1){
    
    
                bookInfo.setStatusCN("可借阅");
            }else{
    
    
                bookInfo.setStatusCN("不可借阅");
            }
        }

        return bookInfos;
    }
}

4.删除创建BookService的代码, 从Spring中获取对象

@RestController
@RequestMapping("book")
public class BookController {
    
    
    // 加载数据
//    private BookService bookService=new BookService();
    
    @Autowired //从容器中注入
    private BookService bookService;

    @RequestMapping("getBookList")
    public List<BookInfo> getBookList(){
    
    

        // 获取图书数据
        List<BookInfo> bookInfos = bookService.getBookList(); ;

        //返回图书列表
        return bookInfos;
    }
}