认识 Spring IOC 及其应用

简介: 在 Spring 框架中,DI 的具体实现类是称为容器,我们通过容器生成一个个小齿轮(Bean),从创建、实例化、销毁等等都交由容器去实现,进一步解耦合调用者与被调用者的代码,并且,去除了大量重复和无意义的代码,方便测试,利于功能复用。Spring 框架为了让用户更为简单的配置 Bean,使用了注解的形式进行配置,而无需去使用麻烦的 XML 进行一一对象配置。

👏 Hi! 我是 Yumuing,一个技术的敲钟人

👨‍💻 每天分享技术文章,永远做技术的朝拜者

📚 欢迎关注我的博客:Yumuing's blog

我们都知道,在使用Spring 框架时,常常遇到 Bean 概念,它其实是描述 Java 的软件组件模型,简单说,就是一个个组成一个可运行机器的小齿轮。而在 Spring 中存在一个专门管理这些齿轮的工厂,它是 IOC( Inversion of Control (IoC:控制反转)理论的实现,称为 DI(Dependency Injection:依赖注入),其中,IOC 是设计思想,DI 是实现方式,是同一个概念的不同角度描述。

在 Spring 框架中,DI 的具体实现类是称为容器,我们通过容器生成一个个小齿轮(Bean),从创建、实例化、销毁等等都交由容器去实现,进一步解耦合调用者与被调用者的代码,并且,去除了大量重复和无意义的代码,方便测试,利于功能复用。Spring 框架为了让用户更为简单的配置 Bean,使用了注解的形式进行配置,而无需去使用麻烦的 XML 进行一一对象配置。

概念性的说明就到这,更重要的是,我们去理解其运行原理,并实际使用它。在不使用 IOC 设计思想时,我们根据下图即可看出,由于某些情况下,以前的 A.class 被弃用了,经过修改变为 B.class ,此时调用 A.class 的对象都得修改代码,以适应变化,以防程序出错。当然,有人会说,那使用抽象接口不就行了,用什么 IOC 呢!其实,我们不能预料未来那些类会发生改变,只能尽可能做好抽象接口处理,总会有遗漏,况且,不是什么类都适合抽象接口,或者,如果每个类都适合并且也做了抽象接口的话,那将出现很多无意义的代码,变得臃肿。最重要的是,即使在存在抽象接口的情况下,你使用新的类,也必须对新的类进行实例化,这部分代码必须在调用者层面去修改,也就意味着调用双方还是得进行修改代码,重新编译源代码,不仅麻烦,还可能编译出错。

无 IOC 情况

当能够使用 IOC 思想之后,就能完美解决,让容器工厂去创建、实例化对象,注入给调用者即可。即使同一个抽象接口下,需要指定哪一个优先注入给调用者也是无需修改调用者代码,除非,还需要使用旧对象的一些方法,其实也可以直接复制代码,也是无需修改调用者代码的,但如果,实在需要旧对象,也可在调用者处修改代码,在获取 Bean 时指定对象。

存在 IOC 情况

在 Spring 中,对象被大致分为四大类,与之相对应的也有四个注解,它们仅仅体现在语义上的不同,方便编写者阅读代码而已。当然,对于对象的属性也有相对应的注入注解,方便自定义对象初始属性。对应的注解说明如下:

对象注入

属性注入

IOC 思想在 Spring 框架中不仅仅体现在对象的创建、实例化上,还能够在对象销毁前自动化地为我们执行某些操作。在测试之前,我们先学会使用容器工厂。

当我们需要使用容器工厂时,就必须使其方法所属类继承 ApplicationContextAware 并实现 setApplicationContext 方法,其中,在 setApplicationContext 方法中必须设置一个属性对象来接受容器工厂类 ApplicationContext 。并且,为了保证与主配置类相同启动环境,test 类也得使用 ContextConfiguration(classes = xxxApplication.class 来配置环境。代码如下:

package top.yumuing.community;

import org.junit.jupiter.api.Test;
import org.springframework.beans.BeansException;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.test.context.ContextConfiguration;

@SpringBootTest
@ContextConfiguration(classes = CommunityApplication.class)
class CommunityApplicationTests implements ApplicationContextAware {
   

    private  ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
   
        this.applicationContext = applicationContext;
    }
}

在方法前一行添加 @PostConstruct 即可 Bean 实例化后第一时间执行该方法,添加 @PreDestroy 即可在 Bean 销毁前最后执行。以上均为 Spring 框架的容器工厂自动执行,无需额外代码。测试类为 TestBeanManagement,代码如下:

package top.yumuing.community.test;

import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import org.springframework.stereotype.Component;

@Component
public class TestBeanManagement {
   
    public TestBeanManagement(){
   
        System.out.println("实例化");
    }

    @PostConstruct
    public void init(){
   
        System.out.println("初始化");
    }

    @PreDestroy
    public void destroy(){
   
        System.out.println("销毁了");
    }
}

测试方法如下:

package top.yumuing.community;

import org.junit.jupiter.api.Test;
import org.springframework.beans.BeansException;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.test.context.ContextConfiguration;

@SpringBootTest
@ContextConfiguration(classes = CommunityApplication.class)
class CommunityApplicationTests implements ApplicationContextAware {
   

    private  ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
   
        this.applicationContext = applicationContext;
    }
    @Test
    public void testBean(){
   
        TestBeanManagement testBeanManagement = applicationContext.getBean(TestBeanManagement.class);
        System.out.println(testBeanManagement);
    }
}

有时,我们使用了某些 jar 包里的对象,需要被注入时,又没办法直接在 jar 里增加注解怎么办?毕竟,jar 包可能加密,或者是需要调用 SDK 中的对象,源代码不可获取。此时,可以采用配置类的方式,通过自行实例化对象,再注册成 Bean,进而通过容器工厂进行调用,无需增加注解在 jar 包里。这种方法是我们主动去获取 Bean ,还是显得较为麻烦,后续有说明,在 Spring 框架下,正确的依赖注入方式,主动获取的方法代码如下:

TestConfig 为配置 jar 包内的 SimpleDateFormat 注册成 Bean 的类,CommunityApplicationTests 为调用该注册为 Bean 的 SimpleDateFormat 类。当然,实际项目目录会按业务、数据库、控制三级平级创建service、dao、controller三个目录,为了方便演示,简化为 test 目录,目录结构如下:

目录结构

TestConfig.java 代码如下:

package top.yumuing.community.test;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.text.SimpleDateFormat;

@Configuration
public class TestConfig {
   

    @Bean
    public SimpleDateFormat simpleDateFormat(){
   
        return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    }

}

CommunityApplicationTests.java 代码如下:

package top.yumuing.community;

import org.junit.jupiter.api.Test;
import org.springframework.beans.BeansException;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.test.context.ContextConfiguration;

import java.text.SimpleDateFormat;
import java.util.Date;

@SpringBootTest
@ContextConfiguration(classes = CommunityApplication.class)
class CommunityApplicationTests implements ApplicationContextAware {
   

    private  ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
   
        this.applicationContext = applicationContext;
    }

    @Test
    public void testBeanConfig(){
   
        SimpleDateFormat simpleDateFormat = applicationContext.getBean("simpleDateFormat",SimpleDateFormat.class);
        System.out.println(simpleDateFormat.format(new Date()));
    }

}

当然,这是我们利用容器工厂的特性进行主动获取 Bean 的方式,明显违背了 IOC 思想,我们将再次在相同的环境下,使用 IOC 思想实现的依赖注入方式解决问题。代码如下:

package top.yumuing.community;

import org.junit.jupiter.api.Test;
import org.springframework.beans.BeansException;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.test.context.ContextConfiguration;

import java.text.SimpleDateFormat;
import java.util.Date;

@SpringBootTest
@ContextConfiguration(classes = CommunityApplication.class)
class CommunityApplicationTests implements ApplicationContextAware {
   

    private  ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
   
        this.applicationContext = applicationContext;
    }
    @Autowired
    public SimpleDateFormat simpleDateFormat;
    @Test
    public void testDI(){
   
        System.out.println(simpleDateFormat.format(new Date()));
    }
}

如果想要指定继承了某个接口的对象,不仅仅需要使用 @Autowired 进行属性注入,还要配合 @Qualifier("Bean 名") 来指定。这次我们模拟实际项目代码,由 controller 调用 service,再由 service 调用 dao 的方式实现。目录结构如下:其中 TestDaoOne 与 TestDaoTwo 继承 TestDao 接口。

屏幕截图_20230201_001318

TestDao 代码如下:

package top.yumuing.community.test;

public interface TestDao {
   
    String select();
}

TestDaoOne 代码如下:

package top.yumuing.community.test;

import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Repository;

@Repository
@Primary
public class TestDaoOne implements TestDao{
   

    @Override
    public String select() {
   
        return "one";
    }
}

TestDaoTwo 代码如下:

package top.yumuing.community.test;

import org.springframework.stereotype.Repository;

@Repository("testDaoTwo")
public class TestDaoTwo implements TestDao{
   

    @Override
    public String select() {
   
        return "two";
    }
}

TestService 代码如下:

package top.yumuing.community.test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

@Service
public class TestService {
   
    @Autowired
    public TestDao testDaoOne;

    public String getDateOne(){
   
        return testDaoOne.select();
    }

    @Autowired
    @Qualifier("testDaoTwo")
    public TestDao testDaoTwo;
    public String getDateTwo(){
   
        return testDaoTwo.select();
    }
}

HelloController 代码如下:

package top.yumuing.community.test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
@RequestMapping("/yumuing")
public class HelloController {
   
    @RequestMapping("/hello")
    @ResponseBody
    public String hello(){
   
        return "hello";
    }


    @Autowired
    public TestService testService;

    @RequestMapping("/dateOne")
    @ResponseBody
    public String oneDate(){
   
        return testService.getDateOne();
    }

    @RequestMapping("/dateTwo")
    @ResponseBody
    public String twoDate(){
   
        return testService.getDateTwo();
    }
}

通过以上的了解,我们就可以简单的知道,何为 IOC ,它又帮助我们减轻了什么,并且该怎么去运用它。除此之外,也简单明白了在Spring 框架中进行测试的方法,以及依赖注入的相关注解。想要获得全部源代码,可见 认识 Spring IOC 及其应用

求点赞转发

目录
相关文章
|
2月前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 实现动态路由和菜单功能,快速搭建前后端分离的应用框架
本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 实现动态路由和菜单功能,快速搭建前后端分离的应用框架。首先,确保开发环境已安装必要的工具,然后创建并配置 Spring Boot 项目,包括添加依赖和配置 Spring Security。接着,创建后端 API 和前端项目,配置动态路由和菜单。最后,运行项目并分享实践心得,包括版本兼容性、安全性、性能调优等方面。
178 1
|
1月前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个具有动态路由和菜单功能的前后端分离应用。
本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个具有动态路由和菜单功能的前后端分离应用。首先,创建并配置 Spring Boot 项目,实现后端 API;然后,使用 Ant Design Pro Vue 创建前端项目,配置动态路由和菜单。通过具体案例,展示了如何快速搭建高效、易维护的项目框架。
115 62
|
20天前
|
XML Java 数据格式
Spring Core核心类库的功能与应用实践分析
【12月更文挑战第1天】大家好,今天我们来聊聊Spring Core这个强大的核心类库。Spring Core作为Spring框架的基础,提供了控制反转(IOC)和依赖注入(DI)等核心功能,以及企业级功能,如JNDI和定时任务等。通过本文,我们将从概述、功能点、背景、业务点、底层原理等多个方面深入剖析Spring Core,并通过多个Java示例展示其应用实践,同时指出对应实践的优缺点。
45 14
|
18天前
|
存储 缓存 Java
Spring面试必问:手写Spring IoC 循环依赖底层源码剖析
在Spring框架中,IoC(Inversion of Control,控制反转)是一个核心概念,它允许容器管理对象的生命周期和依赖关系。然而,在实际应用中,我们可能会遇到对象间的循环依赖问题。本文将深入探讨Spring如何解决IoC中的循环依赖问题,并通过手写源码的方式,让你对其底层原理有一个全新的认识。
38 2
|
18天前
|
XML 前端开发 安全
Spring MVC:深入理解与应用实践
Spring MVC是Spring框架提供的一个用于构建Web应用程序的Model-View-Controller(MVC)实现。它通过分离业务逻辑、数据、显示来组织代码,使得Web应用程序的开发变得更加简洁和高效。本文将从概述、功能点、背景、业务点、底层原理等多个方面深入剖析Spring MVC,并通过多个Java示例展示其应用实践,同时指出对应实践的优缺点。
45 2
|
27天前
|
JSON 安全 算法
Spring Boot 应用如何实现 JWT 认证?
Spring Boot 应用如何实现 JWT 认证?
61 8
|
1月前
|
人工智能 前端开发 Java
基于开源框架Spring AI Alibaba快速构建Java应用
本文旨在帮助开发者快速掌握并应用 Spring AI Alibaba,提升基于 Java 的大模型应用开发效率和安全性。
基于开源框架Spring AI Alibaba快速构建Java应用
|
24天前
|
消息中间件 Java Kafka
Spring Boot 与 Apache Kafka 集成详解:构建高效消息驱动应用
Spring Boot 与 Apache Kafka 集成详解:构建高效消息驱动应用
37 1
|
2月前
|
人工智能 开发框架 Java
总计 30 万奖金,Spring AI Alibaba 应用框架挑战赛开赛
Spring AI Alibaba 应用框架挑战赛邀请广大开发者参与开源项目的共建,助力项目快速发展,掌握 AI 应用开发模式。大赛分为《支持 Spring AI Alibaba 应用可视化调试与追踪本地工具》和《基于 Flow 的 AI 编排机制设计与实现》两个赛道,总计 30 万奖金。
|
1月前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个前后端分离的应用框架,实现动态路由和菜单功能
本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个前后端分离的应用框架,实现动态路由和菜单功能。首先,确保开发环境已安装必要的工具,然后创建并配置 Spring Boot 项目,包括添加依赖和配置 Spring Security。接着,创建后端 API 和前端项目,配置动态路由和菜单。最后,运行项目并分享实践心得,帮助开发者提高开发效率和应用的可维护性。
81 2