Spring IoC与DI:解耦实战指南

📅 2026/7/19 14:21:31
Spring IoC与DI:解耦实战指南
一、IoC DI入门1.1 再次认识Spring我们知道Spring是一个开源框架使用Spring让我们的开发更加简单。用一句更具体的话来说Spring是包含了众多工具方法的IoC容器容器中装的对象1.2 什么是IoCIoC 是Spring的核心思想我们在上一节已经使用过RestController当时我们提到加上这个注解就是把这个对象交给Spring管理Spring框架启动时会加载该类把对象交给Spring管理这就是IoC思想。IoCInversion of Control [控制反转]也就是说Spring是一个“控制反转”的容器所谓控制反转就是控制权发生了反转。获得依赖对象的过程被反转了当需要某个对象时传统的开发模式需要自己new一个对象而现在只需要把创建对象的任务交给容器在程序中注入依赖Dependency InjectionDI就可以了。这个容器称为IoC容器Spring是一个IoC容器所以有时Spring也被称为Spring容器1.2.1 通过实例来认识IoC造汽车传统代码public class CarExample { public static void main(String[] args) { Car carnew Car(); car.run(); } static class Car{ public Framework framework; public Car(){ frameworknew Framework(); System.out.print(Car init...); } public void run(){ System.out.println(Car run...); } } static class Framework { public Bottom bottom; public Framework() { bottom new Bottom(); System.out.println(Framework init...); } } static class Bottom { public Tire tire; public Bottom(){ this.tirenew Tire(); System.out.println(Bottom init...); } } static class Tire { public int size; public Tire(){ this.size16; System.out.println(轮胎尺寸是size); } } }这个代码看似没问题但实际可维护性很低当我们想要给Tire的size传参时会发现从Bottom-Framework-Car都需要经行修改public class CarExample { public static void main(String[] args) { Car carnew Car(9); car.run(); } static class Car{ public Framework framework; public Car(int size){ frameworknew Framework(size); System.out.print(Car init...); } public void run(){ System.out.println(Car run...); } } static class Framework { public Bottom bottom; public Framework(int size) { bottom new Bottom(size); System.out.println(Framework init...); } } static class Bottom { public Tire tire; public Bottom(int size){ this.tirenew Tire(size); System.out.println(Bottom init...); } } static class Tire { public int size; public Tire(int size){ this.sizesize; System.out.println(轮胎尺寸是size); } } }由此我们不难看出当最底层的代码改动后整个调用链上的所有代码都需要修改但当我们按照IoC的思想来调整一下代码public class CarExample { public static void main(String[] args) { Tire tirenew Tire(20); Bottom bottomnew Bottom(tire); Framework frameworknew Framework(bottom); Car carnew Car(framework); car.run(); } static class Car{ public Framework framework; public Car(Framework framework){ this.frameworkframework; System.out.print(Car init...); } public void run(){ System.out.println(Car run...); } } static class Framework { public Bottom bottom; public Framework(Bottom bottom) { this.bottom bottom; System.out.println(Framework init...); } } static class Bottom { public Tire tire; public Bottom(Tire tire){ this.tiretire; System.out.println(Bottom init...); } } static class Tire { public int size; public Tire(int size){ this.sizesize; System.out.println(轮胎尺寸是size); } } }经过以上调整无论底层类如何变化整个调用链是不用做任何改变的完成了代码的解耦。1.2.2 IoC优势传统代码创建顺序Car--Framework--Bottom--TireIoC代码创建顺序Tire--Bottom--Framework--Car我们发现了⼀个规律通用程序的实现代码类的创建顺序是反的传统代码是 Car 控制并创建了FrameworkFramework 创建并创建了 Bottom依次往下而改进之后的控制权发生的反转不再是使用方法创建并控制依赖对象了而是把依赖对象注入将当前对象中依赖对象的控制权不再由当前类控制了。这样的话, 即使依赖类发生任何改变当前类都是不受影响的这就是典型的控制反转也就是 IoC 的实现思想。这部分代码就是IoC容器做的工作IoC容器的优点资源不由使用资源的双方管理而由不使用资源的第三方管理这可以带来很多好处。资源集中管理实现资源的可配置和易管理。降低了使用资源双方的依赖程度也就是我们说的耦合度。1.3 什么是DIDI:Dependency Injection依赖注入在容器运行期间动态的为应用程序提供运行时所依赖的资源称之为依赖注入。程序运行时需要某个资源此时容器就为其提供这个资源。依赖注入DI和控制反转IoC是从不同的角度描述同一件事情依赖注入是从应用程序的角度来描述就是通过引入IoC容器利用依赖关系注入的方式实现对象之间的解耦以上箭头部分就在执行注入IoC是一种思想也是“目标”而思想只是一种指导原则最终还要有可行的落地方案而DI就属于具体的实现。也可以说DI是IoC的一种实现。如我今天想奖励自己吃顿好的那么“吃顿好的”就是我的思想IoC但是我最终决定吃臭粉还是臭榴莲这就是具体实现DI二、IoC DI使用既然Spring是一个IoC控制反转容器那么它就具备存、取两个功能。Spring容器管理的主要是对象这些对象我们称为“Bean”我们把这些对象交由Spring管理由Spring来负责对象的创建和销毁我们程序只需要告诉Spring哪些需要存以及如何从Spring中取出对象核心步骤Service层及Dao层的实现类交给Spring管理使用注解Component在Controller层和Service层注入运行时依赖对象使用注解Autowired2.1 完整示例图书管理系统下面我们通过一个完整的图书管理系统示例演示如何使用Component和Autowired注解实现 IoC 和 DI。2.1.1 实体类定义首先定义图书实体类public class BookInfo { private Integer id; private String bookName; private String author; private Integer count; private BigDecimal price; private String publish; private Integer status; private String statusCN; // 省略getter和setter方法 // 实际开发中应使用Lombok或手动生成 }2.1.2 Dao层实现创建BookDao类使用Repository注解将其交给 Spring 管理import org.springframework.stereotype.Repository; import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; import java.util.Random; Repository public class BookDao { /** * 模拟数据获取图书信息 * return 图书列表 */ public Listlt;BookInfogt; mockData() { Listlt;BookInfogt; books new ArrayListlt;gt;(); for (int i 0; i lt; 5; i) { BookInfo book new BookInfo(); book.setId(i); book.setBookName(书籍 i); book.setAuthor(作者 i); book.setCount(i * 5 3); book.setPrice(new BigDecimal(new Random().nextInt(100))); book.setPublish(出版社 i); book.setStatus(1); books.add(book); } return books; } }2.1.3 Service层实现创建BookService类使用Service注解将其交给 Spring 管理并通过Autowired注入BookDaoimport org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; Service public class BookService { Autowired private BookDao bookDao; /** 获取图书列表并处理状态显示 return 处理后的图书列表 */ public Listlt;BookInfogt; getBookList() { Listlt;BookInfogt; books bookDao.mockData(); for (BookInfo book : books) { if (book.getStatus() 1) { book.setStatusCN(可借阅); } else { book.setStatusCN(不可借阅); } } return books; } }2.1.4 Controller层实现创建BookController类使用RestController注解并通过Autowired注入BookServiceimport org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.List; RestController RequestMapping(/book) public class BookController { Autowired private BookService bookService; RequestMapping(/getList) public Listlt;BookInfogt; getList() { // 通过注入的bookService获取数据 Listlt;BookInfogt; books bookService.getBookList(); return books; } }2.1.5 启动类配置创建 Spring Boot 启动类import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; SpringBootApplication public class LibraryApplication { public static void main(String[] args) { SpringApplication.run(LibraryApplication.class, args); } }2.1.6 运行与测试启动应用程序后访问http://localhost:8080/book/getList即可获取图书列表数据。2.2 示例解析通过这个完整的示例我们可以看到 IoC 和 DI 在实际项目中的应用Bean 存储BookDao使用Repository注解表示这是数据访问层组件BookService使用Service注解表示这是业务逻辑层组件BookController使用RestController注解表示这是控制层组件依赖注入BookService中通过Autowired注入了BookDaoBookController中通过Autowired注入了BookService控制反转所有对象的创建和管理都由 Spring 容器负责各层之间通过接口依赖而不是直接创建对象当需要修改实现时只需修改对应的 Bean 配置不影响其他层2.3 与传统方式的对比如果没有使用 Spring IoC 和 DI传统的代码可能是这样的// 传统方式手动创建对象 public class TraditionalBookController { public Listlt;BookInfogt; getList() { // 需要手动创建所有依赖对象 BookDao bookDao new BookDao(); BookService bookService new BookService(bookDao); return bookService.getBookList(); } } // Service 也需要手动创建 Dao public class TraditionalBookService { private BookDao bookDao; public TraditionalBookService() { this.bookDao new BookDao(); // 硬编码依赖 } }传统方式的问题对象创建逻辑分散在各个类中修改依赖时需要修改多处代码难以进行单元测试无法轻松替换依赖代码耦合度高维护困难使用 Spring IoC/DI 的优势对象创建集中管理依赖关系通过配置声明而非硬编码便于单元测试可以通过 Mock 替换依赖代码解耦各层职责清晰支持灵活的配置和扩展通过这个完整的示例可以更直观地理解 IoC 和 DI 在实际项目中的应用价值。Spring 容器就像是一个对象工厂负责创建和管理所有 Bean 对象而开发者只需要通过注解声明依赖关系Spring 就会自动完成对象的装配和注入。三、Bean的存储在前面的讲解中要把某个对象交给IoC容器管理需要在类上加入Component注解而Spring框架为了更好的服务web应用程序提供了更丰富的注解类注解Controller、Service、Repository、Component、Configuration方法注解BeanBean命名约定开发人员不需要为bean指定名称如果没有显式的提供名称Spring容器将为该bean生成唯一的名称命名方式beanName默认为类名的小驼峰写法如类名UserController, Bean的名称为: userController类名AccountService, Bean的名称为: accountService如果类名前两位均为大写beanName则为类名本身如类名UController, Bean的名称为: UController类名AManager, Bean的名称为: AManager四、小结本来准备在这把bean的几个注解一起写了。但是考虑到这样的话文章太长了所以就放去下一章了。现在已经九点了我才点了臭粉其实晚上我也吃晚饭了。但是这个臭粉太好吃了柳妹妹螺狮粉它家辣椒不辣能吃辣的直接上变态辣就行一定记得拿到手汤到进去闷一会再吃最好从出餐到进嘴隔30min这样更有味道更好吃哦。