Java Spring IoC&DI :探索Java Spring中控制反转和依赖注入的威力,增强灵活性和可维护性

简介: Java Spring IoC&DI :探索Java Spring中控制反转和依赖注入的威力,增强灵活性和可维护性

前提小知识:高内聚低耦合

我们一下要学习的内容都是为了实现⾼内聚低耦合来进行的

软件设计原则:⾼内聚低耦合.

⾼内聚指的是:⼀个模块中各个元素之间的联系的紧密程度,如果各个元素(语句、程序段)之间的联

系程度越⾼,则内聚性越⾼,即"⾼内聚"。

低耦合指的是:软件中各个层、模块之间的依赖关联程序越低越好。修改⼀处代码,其他模块的代码

改动越少越好.

⾼内聚低耦合⽭盾吗?

不⽭盾,⾼内聚指的是⼀个模块中各个元素之间的联系的紧密程度,低耦合指的是各个模块之间的紧

密程度

这就好⽐⼀个企业,包含很多部⻔,各个部⻔之间的关联关系要尽可能的⼩,⼀个部⻔发⽣问题,要尽

可能对降低对其他部⻔的影响,就是耦合.但是部⻔内部员⼯关系要尽量紧密,遇到问题⼀起解决,克

服.这叫做内聚.

⽐如邻⾥邻居,楼上漏⽔,楼下遭殃,就是耦合.家庭⼀个成员⽣病,其他成员帮忙照顾,就叫内聚.

⼀个家庭内部的关系越紧密越好,⼀个家庭尽可能少的影响另⼀个家庭,就是低耦合.

简单来说:就是在未来的工作中写出的代码尽量集中在一个路径中,但是每个程序与其它程序的关联度很低,以至于一个文件出错了并不影响其它程序的正常运行.


一. IOC

1.1 什么是IOC?

IoC是Spring的核⼼思想,也是常⻅的⾯试题,那什么是IoC呢?

IoC:InversionofControl(控制反转),也就是说Spring是⼀个"控制反转"的容器.

什么是控制反转呢?也就是控制权反转.什么的控制权发⽣了反转?获得依赖对象的过程被反转了

也就是说,当需要某个对象时,传统开发模式中需要⾃⼰通过new创建对象,现在不需要再进⾏创

建,把创建对象的任务交给容器,程序中只需要依赖注⼊(DependencyInjection,DI)就可以了.

我们⽤⼀句更具体的话来概括Spring,那就是:Spring是包含了众多⼯具⽅法的IoC容器

简单点来说:就是通过IOC的内置注解将被注解的内容放到Spring的大池子里,方便后续的调用(这样子应该很好理解)

1.2 IOC的实现

举个栗子:比如传统的造车过程是一级级的调用,如下:

以上程序的问题是:当最底层代码改动之后,整个调⽤链上的所有代码都需要修改.程序的耦合度⾮常⾼(修改⼀处代码,影响其他处的代码修改)

经过IOC实现后的流程图如下:

而IOC的目标就是为了降低耦合度,对于上面的例子来说就是将汽车每个部件的生产都交给第三方来处理,不让他们之间有联系,也就降低了耦合度

我们发现了⼀个规律,通⽤程序的实现代码,类的创建顺序是反的,传统代码是Car控制并创建了

Framework,Framework创建并创建了Bottom,依次往下,⽽改进之后的控制权发⽣的反转,不再

是使⽤⽅对象创建并控制依赖对象了,⽽是把依赖对象注⼊将当前对象中,依赖对象的控制权不再由

当前类控制了.

这样的话,即使依赖类发⽣任何改变,当前类都是不受影响的,这就是典型的控制反转,也就是IoC的

实现思想。

1.3 IOC容器的优点

资源不由使⽤资源的双⽅管理,⽽由不使⽤资源的第三⽅管理,这可以带来很多好处。第⼀,资源集

中管理,实现资源的可配置和易管理。第⼆,降低了使⽤资源双⽅的依赖程度,也就是我们说的耦合

度。

  1. 资源集中管理:IoC容器会帮我们管理⼀些资源(对象等),我们需要使⽤时,只需要从IoC容器中去取
    就可以了
  2. 我们在创建实例的时候不需要了解其中的细节,降低了使⽤资源双⽅的依赖程度,也就是耦合度.
    Spring就是⼀种IoC容器,帮助我们来做了这些资源管理.

1.4 IOC的存储

在之前的⼊⻔案例中,要把某个对象交给IOC容器管理,需要在类上添加⼀个注解: @Component

⽽Spring框架为了更好的服务web应⽤程序,提供了更丰富的注解.

共有两类注解类型可以实现:

  1. 类注解:@Controller、@Service、@Repository、@Component、@Configuration.
  2. ⽅法注解:@Bean.
    接下来我们分别来看
@Controller(控制器存储)

使⽤@Controller存储bean的代码如下所⽰:

@Controller // 将对象存储到 Spring 中
  public class UserController {
    public void sayHi(){
      System.out.println("hi,UserController...");
    }
  }
@Service(服务存储)

使⽤@Service存储bean的代码如下所⽰:

@Service// 将对象存储到 Spring 中
  public class UserService {
    public void sayHi(String name) {
      System.out.println("Hi," + name);
    }
  }
@Repository(仓库存储)

使⽤ @Repository 存储bean的代码如下所⽰:

@Repository
  public class UserRepository {
    public void sayHi() {
      System.out.println("Hi, UserRepository~");
    }
  }
@Component(组件存储)

使⽤@Component存储bean的代码如下所⽰:

@Component
  public class UserComponent {
    public void sayHi() {
      System.out.println("Hi, UserComponent~");
    }
  }
@Configuration(配置存储)

使⽤@Configuration存储bean的代码如下所⽰:

@Configuration
  public class UserConfiguration {
    public void sayHi() {
      System.out.println("Hi,UserConfiguration~");
    }
  }
@Bean (方法注解)

类注解是添加到某个类上的同时⽅法注解要配合类注解使⽤,但是存在两个问题:

  1. 使⽤外部包⾥的类,没办法添加类注解
  2. ⼀个类,需要多个对象,⽐如多个数据源
    这种场景,我们就需要使⽤⽅法注解 @Bean
    我们先来看看⽅法注解如何使⽤:
    ⽅法注解要配合类注解使⽤
@Component
public class BeanConfig {
  @Bean
  public User user(){
    User user = new User();
    user.setName("zhangsan");
    user.setAge(18);
    return user;
  }
}

1.5 IOC注解总结概括

  • @Controller:控制层,接收请求,对请求进⾏处理,并进⾏响应.
  • @Servie:业务逻辑层,处理具体的业务逻辑.
  • @Repository:数据访问层,也称为持久层.负责数据访问操作
  • @Configuration:配置层.处理项⽬中的⼀些配置信息.
  • @Component:其它4大类注解的父类
  • @Bean:方法注解,将一个方法交给Spring管理,同时要与类注解一起搭配使用

其实这些注解⾥⾯都有⼀个注解 @Component ,说明它们本⾝就是属于 @Component 的"⼦类".@Component 是⼀个元注解,也就是说可以注解其他类注解,如 @Controller , @Service ,

@Repository 等.这些注解被称为 @Component 的衍⽣注解.

@Controller , @Service 和 @Repository ⽤于更具体的⽤例(分别在控制层,业务逻辑层,持

久化层),在开发过程中,如果你要在业务逻辑层使⽤ @Component 或@Service,显然@Service是更

好的选择


二. DI

2.1 什么是DI?

DI:DependencyInjection(依赖注⼊)

容器在运⾏期间,动态的为应⽤程序提供运⾏时所依赖的资源,称之为依赖注⼊。

程序运⾏时需要某个资源,此时容器就为其提供这个资源.

从这点来看,依赖注⼊(DI)和控制反转(IoC)是从不同的⻆度的描述的同⼀件事情,就是指通过

引⼊IoC容器,利⽤依赖关系注⼊的⽅式,实现对象之间的解耦。

上述代码中,是通过构造函数的⽅式,把依赖对象注⼊到需要使⽤的对象中的

可以说,DI是IoC的⼀种实现

2.2 DI的方法使用

依赖注⼊是⼀个过程,是指IoC容器在创建Bean时,去提供运⾏时所依赖的资源,⽽资源指的就是对象.

在上⾯程序案例中,我们使⽤了 @Autowired 这个注解,完成了依赖注⼊的操作.

简单来说,就是把对象取出来放到某个类的属性中.

在⼀些⽂章中,依赖注⼊也被称之为"对象注⼊",“属性装配”,具体含义需要结合⽂章的上下⽂来理解

关于依赖注⼊,Spring也给我们提供了三种⽅式:

  1. 属性注⼊(FieldInjection)
  2. 构造⽅法注⼊(ConstructorInjection)
  3. Setter注⼊(SetterInjection)
属性注入

属性注⼊是使⽤ @Autowired 实现的,将Service类注⼊到Controller类中.

Service类的实现代码如下:

import org.springframework.stereotype.Service;
@Service
public class UserService {
    public void sayHi() {
      System.out.println("Hi,UserService");
    }
}
构造方法注入

构造⽅法注⼊是在类的构造⽅法中实现注⼊,如下代码所⽰:

@Controller
public class UserController2 {
  //注⼊⽅法2: 构造⽅法
  private UserService userService;
  @Autowired
  
  public UserController2(UserService userService) {
    this.userService = userService;
  }
  public void sayHi(){
    System.out.println("hi,UserController2...");
    userService.sayHi();
  }
}

注意事项:如果类只有⼀个构造⽅法,那么@Autowired注解可以省略;如果类中有多个构造⽅法,

那么需要添加上@Autowired来明确指定到底使⽤哪个构造⽅法。

Setter注入

Setter注⼊和属性的Setter⽅法实现类似,只不过在设置set⽅法的时候需要加上@Autowired注

解,如下代码所⽰:

@Controller
public class UserController3 {
  //注⼊⽅法3: Setter⽅法注⼊
  private UserService userService;
  @Autowired
  public void setUserService(UserService userService) {
    this.userService = userService;
  }
  
  
  public void sayHi(){
    System.out.println("hi,UserController3...");
    userService.sayHi();
  }
}
三种注入优缺点分析
  1. 属性注⼊

优点:简洁,使⽤⽅便;

缺点:

  • 只能⽤于IoC容器,如果是⾮IoC容器不可⽤,并且只有在使⽤的时候才会出现NPE(空指针异常)
  • 不能注⼊⼀个Final修饰的属性
  1. 构造函数注⼊

优点:

  • 可以注⼊final修饰的属性
  • 注⼊的对象不会被修改
  • 依赖对象在使⽤前⼀定会被完全初始化,因为依赖是在类的构造⽅法中执⾏的,⽽构造⽅法 是在类加载阶段就会执⾏的⽅法.
  • 通⽤性好,构造⽅法是JDK⽀持的,所以更换任何框架,他都是适⽤的

缺点:

注⼊多个对象时,代码会⽐较繁琐

  1. Setter注⼊

优点:⽅便在类实例之后,重新对该对象进⾏配置或者注⼊

缺点:

  • 不能注⼊⼀个Final修饰的属性
  • 注⼊对象可能会被改变,因为setter⽅法可能会被多次调⽤,就有被修改的⻛险.

2.3 @Autowired存在的问题

当同⼀类型存在多个bean时,使⽤@Autowired会存在问题

报错的原因是,⾮唯⼀的Bean对象。

如何解决上述问题呢?Spring提供了以下⼏种解决⽅案:

• @Primary

• @Qualifier

• @Resource

@Primary

使⽤@Primary注解:当存在多个相同类型的Bean注⼊时,加上@Primary注解,来确定默认的实现.

@Component
public class BeanConfig {
  @Primary //指定该bean为默认bean的实现
  @Bean("u1")
  public User user1(){
    User user = new User();
    user.setName("zhangsan");
    user.setAge(18);
    return user;
  }
  
  @Bean
  public User user2() {
    User user = new User();
    user.setName("lisi");
    user.setAge(19);
    return user;
  }
}
@Qualifier

使⽤@Qualifier注解:指定当前要注⼊的bean对象。在@Qualifier的value属性中,指定注⼊的bean

的名称。

• @Qualifier注解不能单独使⽤,必须配合@Autowired使⽤

@Controller
public class UserController {
  @Qualifier("user2") //指定bean名称
  @Autowired
  private User user;
  
  public void sayHi(){
    System.out.println("hi,UserController...");
    System.out.println(user);
  }
}
@Resource

使⽤@Resource注解:是按照bean的名称进⾏注⼊。通过name属性指定要注⼊的bean的名称。

@Controller
public class UserController {
  @Resource(name = "user2")
  private User user;
  
  public void sayHi(){
    System.out.println("hi,UserController...");
    System.out.println(user);
  }
}

2.4 @Autowird的注入流程图

常见⾯试题:

@Autowird与@Resource的区别

• @Autowired是spring框架提供的注解,⽽@Resource是JDK提供的注解

• @Autowired默认是按照类型注⼊,⽽@Resource是按照名称注⼊.相⽐于@Autowired来说,

@Resource⽀持更多的参数设置,例如name设置,根据名称获取Bean。


三. 总结

Java Spring框架中的IoC(Inversion of Control,控制反转)和DI(Dependency Injection,依赖注入)是框架的核心概念之一,它们旨在降低组件之间的耦合度,提高代码的灵活性和可维护性。

  • 控制反转(IoC):在传统的程序设计中,应用程序控制程序的流程,即应用程序负责实例化和管理对象之间的依赖关系。而在IoC容器中,控制权被转移到容器,容器负责实例化对象并管理它们之间的依赖关系。这种控制权的转移使得组件之间的耦合度降低,提高了代码的灵活性和可测试性。
  • 依赖注入(DI):依赖注入是IoC的一种实现方式,通过依赖注入,容器会将一个对象所依赖的其他对象注入到该对象中,而不是由对象自己去创建或查找依赖的对象。依赖注入可以通过构造函数、属性或者方法进行注入,使得组件之间的依赖关系更加清晰、可控,同时也方便进行单元测试和替换依赖。

总的来说,IoC和DI通过将控制权交给容器,实现了组件之间的解耦和松耦合,提高了代码的可维护性、可测试性和可扩展性,是Spring框架的核心特性之一。

目录
打赏
0
0
1
0
15
分享
相关文章
SaaS云计算技术的智慧工地源码,基于Java+Spring Cloud框架开发
智慧工地源码基于微服务+Java+Spring Cloud +UniApp +MySql架构,利用传感器、监控摄像头、AI、大数据等技术,实现施工现场的实时监测、数据分析与智能决策。平台涵盖人员、车辆、视频监控、施工质量、设备、环境和能耗管理七大维度,提供可视化管理、智能化报警、移动智能办公及分布计算存储等功能,全面提升工地的安全性、效率和质量。
Java 也能快速搭建 AI 应用?一文带你玩转 Spring AI 可观测性
Java 也能快速搭建 AI 应用?一文带你玩转 Spring AI 可观测性
Java 也能快速搭建 AI 应用?一文带你玩转 Spring AI 可观测性
Java 也能快速搭建 AI 应用?一文带你玩转 Spring AI 可观测性
Java也能快速搭建AI应用?一文带你玩转Spring AI可落地性
Java语言凭借其成熟的生态与解决方案,特别是通过 Spring AI 框架,正迅速成为 AI 应用开发的新选择。本文将探讨如何利用 Spring AI Alibaba 构建在线聊天 AI 应用,并实现对其性能的全面可观测性。
java spring 项目若依框架启动失败,启动不了服务提示端口8080占用escription: Web server failed to start. Port 8080 was already in use. Action: Identify and stop the process that’s listening on port 8080 or configure this application to listen on another port-优雅草卓伊凡解决方案
java spring 项目若依框架启动失败,启动不了服务提示端口8080占用escription: Web server failed to start. Port 8080 was already in use. Action: Identify and stop the process that’s listening on port 8080 or configure this application to listen on another port-优雅草卓伊凡解决方案
66 7
|
2月前
|
使用Java和Spring Data构建数据访问层
本文介绍了如何使用 Java 和 Spring Data 构建数据访问层的完整过程。通过创建实体类、存储库接口、服务类和控制器类,实现了对数据库的基本操作。这种方法不仅简化了数据访问层的开发,还提高了代码的可维护性和可读性。通过合理使用 Spring Data 提供的功能,可以大幅提升开发效率。
75 21
建筑施工一体化信息管理平台源码,支持微服务架构,采用Java、Spring Cloud、Vue等技术开发。
智慧工地云平台是专为建筑施工领域打造的一体化信息管理平台,利用大数据、云计算、物联网等技术,实现施工区域各系统数据汇总与可视化管理。平台涵盖人员、设备、物料、环境等关键因素的实时监控与数据分析,提供远程指挥、决策支持等功能,提升工作效率,促进产业信息化发展。系统由PC端、APP移动端及项目、监管、数据屏三大平台组成,支持微服务架构,采用Java、Spring Cloud、Vue等技术开发。
111 7
|
2月前
|
Java Spring Boot监听事件和处理事件
通过上述步骤,我们可以在Java Spring Boot应用中实现事件的发布和监听。事件驱动模型可以帮助我们实现组件间的松耦合,提升系统的可维护性和可扩展性。无论是处理业务逻辑还是系统事件,Spring Boot的事件机制都提供了强大的支持和灵活性。希望本文能为您的开发工作提供实用的指导和帮助。
113 15
【Spring】IoC和DI,控制反转,Bean对象的获取方式
IoC,DI,控制反转容器,Bean的基本常识,类注解@Controller,获取Bean对象的常用三种方式
Spring Boot 入门:简化 Java Web 开发的强大工具
Spring Boot 是一个开源的 Java 基础框架,用于创建独立、生产级别的基于Spring框架的应用程序。它旨在简化Spring应用的初始搭建以及开发过程。
121 7
Spring Boot 入门:简化 Java Web 开发的强大工具
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等