首页 > 其他分享 >Spring:DI思想的详细图解

Spring:DI思想的详细图解

时间:2024-08-21 09:22:21浏览次数:14  
标签:Autowired Spring DI User bean user 图解 public 注入

什么是DI

DI 就是依赖注入,,简单来讲,其实就是,把 IoC 容器中的 bean 对象取出来直接放到某个类的属性中,不再需要我们自己手的的new对象。例如下面代码,

在 Controller 包中创建了一个 SayHiContreoller 类,在 ServiceDemo 包中创建了一个SayHiService 类,现在,想要在 SayHiController 类中的 sayHi 方法里,调用 SayHiService 类中的sayHi()方法,所以,就需要手动的New一个SayHiService对象,然后再调用sayHi()方法,那么,现在不再手动的new对象了,而使用依赖注入的方式。

1.手动创建对象的方式

在这里插入图片描述

2.使用DI的方式

下图 Ioc 容器在创建 SayHiController 对象时,需要 SayHiService 对象,所以,通过 DI 的方式提供给 SayHiController 运行时所需要的资源(SayHiService对象)

在这里插入图片描述

关于依赖注入,不仅仅只有 @Autowired 一个注解,Spring 给我们提供了三种方式,如下

依赖注入的三种方式

  • 属性注入(Filed Injection)
  • 构造方法注入(Constructor Injection)
  • Setter 注入(Setter Injection)

1.属性注入

“属性注入” 就是使用 @Autowired 注解,也就是上面我们演示的,将SayHiService 类注入到 SayHiController 类中。

SayHiController 类的代码实现:

@RestController
public class SayHiController {
   
    @Autowired
    private SayHiService service;
    
    public void sayHi() {
        service.sayHi();
    }
}

SayHiService 类的代码实现:

@Service
public class SayHiService {
    public void sayHi() {
        System.out.println("hello SayHiService");
    }
}

获取 SayHiController 中的 sayHi()

@SpringBootApplication
public class SpringDemo7Application {

    public static void main(String[] args) {
        //获取Spring上下文对象
        ConfigurableApplicationContext context = SpringApplication.run(SpringDemo7Application.class, args);
        //从上下文中获取到SayHiController对象
        SayHiController bean = (SayHiController)context.getBean(SayHiController.class);
        bean.sayHi();
    }

}

在这里插入图片描述

去掉 @AutoWired 注解,再次运行,就会出现空指针异常

在这里插入图片描述

2.构造方法注入

构造方法注入是在类的构造方法中实现注入的,如下代码:

@RestController
public class SayHiController {

    private SayHiService service;
    @Autowired
    public SayHiController(SayHiService service) {
        this.service = service;
    }

    public void sayHi() {
        service.sayHi();
    }
}

在这里插入图片描述

注意:如果类中只有一个构造方法,@Autowired 注解可以省略,但如果有多个注解,那么就要使用 @Autowired 注解明确的指定使用哪个构造方法,如下代码演示:

3.Setter 注入

Setter 注入和属性的 Setter 方法类似,只不过要在set方法上使用 @Autowired 注解修饰,代码如下:

@RestController
public class SayHiController {

    private SayHiService service;
    @Autowired
    public void setService(SayHiService service) {
        this.service = service;
    }

    public void sayHi() {
        service.sayHi();
    }
}

在这里插入图片描述

同样,如果不加 @Autowired 注解就会报空指针异常

在这里插入图片描述

三种注入的优缺点:

  • 属性注入

    • 优点:简洁,适用方便,直接适用 @Autowired 修饰即可

    • 缺点:

      • 只能用于 IoC 容器,如果是非 Ioc 容器则不可用,并且只有在使用的时候才会出现空指针异常

      • 不能注入一个 final 修饰的属性,如下图:

        在这里插入图片描述

        但是,如果想要注入一个 final 修饰的属性,有一个要求,需要满足以下任意一个条件:

        1,在属性声明时要完成初始化,但是,如果声明时就已经初始化了,就不需要再进行注入了呀,所以这条等于没用

        2,要在构造方法中进行赋值,如果在构造方法中进行赋值的话,直接使用构造函数注入就行了呀,所以这条也没用。

        所以,这条缺点与下面的构造方法注入对比来讲。

  • 构造方法注入

    • 优点:
      • 可以注入 final 修饰的属性,因为它本来就是在构造方法中进行赋值的
      • 注入的对象不会被修改,因为依赖对象在使用前就已经被初始化了
      • 依赖对象在使用前一定会被初始化,因为依赖是在类的构造方法中执行的,而构造方法是在类加载阶段就会被执行的
      • 通用性好,构造方法是JDK支持的,所以更换任何框架,它都是适用的
    • 缺点:
      • 注入多个对象时,代码会比较繁琐
  • Setter 注入

    • 优点:方便在类实例化之后,重新对该对象进行配置或注入
    • 缺点:
      • 不能注入一个 final 修饰的属性
      • 注入对象可能会被改变,因为setter方法可能会给多次调用,就有被修改的风险

@Autowired 存在的问题

使用方法注解对同一个类型创建多个 bean对象时,使用 @Autowired 就会报错,代码如下:

创建一个 User 类

@Data
public class User {
    private String name;
    private int age;
}

创建一个 BeanConfig 类, 使用方法注解在该类中创建两个 User 类型的对象

@Service
public class BeanConfig {
    @Bean
    public User user1() {
        User user = new User();
        user.setName("张三");
        user.setAge(18);
        return user;
    }

    @Bean
    public User user2() {
        User user = new User();
        user.setName("李四");
        user.setAge(20);
        return user;
    }
}

注入Bean对象

@RestController
public class SayHiController {
    @Autowired
    private User user;

    public void sayHi() {
        System.out.println(user);
    }
}

在使用 @Autowired 进行注入时,就会报出以下错误,表示有多个“User”类型的bean

在这里插入图片描述

如果将变量名 user 改成 user1 或者 user2 就可以运行成功

在这里插入图片描述

通过上述的验证,就可以发现,当同样类型存在多个bean对象时,就可能会出错,而 @Autowired 是先根据 bean 名称来获取bean对象,如果匹配到了,则成功,如果没有匹配到,就会根据类型进行匹配,如果匹配到多个,就会报错。但是通常不会修改变量名来获取某个bean,而是通过其它的手段来指定bean的名称。解决方法如下:

解决方案

Spring 为我们提供了以下几种方案:

  • @Primary
  • @Qualifier
  • @Resource

使用 @Primary 注解:当存在多个相同类型的bean对象时,加上 @Primary 注解来确定默认的 bean 对象

@Service
public class BeanConfig {
    
    @Primary //指定bean为默认bean的实现
    @Bean
    public User user1() {
        User user = new User();
        user.setName("张三");
        user.setAge(18);
        return user;
    }

    @Bean
    public User user2() {
        User user = new User();
        user.setName("李四");
        user.setAge(20);
        return user;
    }
}

在这里插入图片描述

使用 @Qualifier 注解:指定当前要注入的bean的名称,在 @Qualifier 的 value 属性上,指定注入bean的名称

注意,@Qualifier 注解不能单独使用,要配合 @Autowired 注解使用,如下图:

@RestController
public class SayHiController {
    
    @Qualifier("user2")
    @Autowired
    private User user;
    public void sayHi() {
        System.out.println(user);
    }
}

在这里插入图片描述

使用 @Resource 注解:按照 bean 的名称进行注入。通过 @Resource 的name属性来指定要注入的bean的名称,代码如下:

@RestController
public class SayHiController {
    
    @Resource(name = "user2")
    private User user;
    public void sayHi() {
        System.out.println(user);
    }
}

在这里插入图片描述

而且,@Resource 注解是属于 JDK 里的,而不是属于 Spring 的,Spring 只是通过其他方式进行了实现,所以,就可以知道在Spring之前就有了注解,如下图:

在这里插入图片描述

@Autowired 和 @Resource 的区别

1.@Autowired 是 Spring 提供的,@Resource 是 JDK 提供的

2.@Autowired 是先根据名称来获取bean对象,如果匹配到了,则成功,如果没有匹配到,就会根据类型进行匹配,如果匹配到多个,检查有没有通过 @Qualifier指定名称,如果没有,则报错,@Resource 是按照名称注入的,相比 @Autowired 来说,@Resource 支持更多的参数设置,例如name设置,根据名称获取Bean

标签:Autowired,Spring,DI,User,bean,user,图解,public,注入
From: https://blog.csdn.net/Shine0115/article/details/141368728

相关文章

  • 开始步入Spring框架的学习
    Spring框架Spring概述Spring是一个广泛使用的Java企业级应用开源开发框架,它旨在简化企业级应用的开发。以下是Spring的详细概述:一、创建背景与目的创建背景:Spring框架的创建是为了解决企业级编程开发中的复杂性,实现敏捷开发。它最初由RodJohnson在2002年提出并随后创建......
  • 浅谈Java Spring Boot
    一、基本介绍SpringBoot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。通过这种方式,SpringBoot致力于在蓬勃发展的快速应用开发领域(rapidapplicatio......
  • 浅谈 Java Spring框架
    一、基本介绍Spring框架是由于软件开发的复杂性而创建的。Spring使用的是基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅仅限于服务器端的开发。从简单性、可测试性和松耦合性角度而言,绝大部分Java应用都可以从Spring中受益。二、核心特性依......
  • 解决Cannot find module ‘@/score/test/index.vue‘ or its corresponding type decl
    {"compilerOptions":{"target":"esnext","module":"esnext","strict":true,"jsx":"preserve","importHelpers":true,"moduleResolu......
  • 利用Spring Boot实现微服务的配置中心
    利用SpringBoot实现微服务的配置中心大家好,我是微赚淘客返利系统3.0的小编,是个冬天不穿秋裤,天冷也要风度的程序猿!在微服务架构中,随着服务数量的增加,集中管理配置信息变得尤为重要。SpringCloudConfig提供了一个配置服务器,用于集中管理微服务的配置信息。本文将介绍如何利用Sp......
  • 构建Spring Boot应用的性能监控与优化
    构建SpringBoot应用的性能监控与优化大家好,我是微赚淘客返利系统3.0的小编,是个冬天不穿秋裤,天冷也要风度的程序猿!性能监控与优化是确保SpringBoot应用高效运行的关键环节。SpringBoot提供了多种机制来监控应用性能,并进行优化。本文将介绍如何构建SpringBoot应用的性能监控与......
  • Spring Boot集成Spring Cloud Netflix组件
    SpringBoot集成SpringCloudNetflix组件大家好,我是微赚淘客返利系统3.0的小编,是个冬天不穿秋裤,天冷也要风度的程序猿!SpringCloud是一个基于SpringBoot的微服务框架,它集成了多种微服务解决方案,包括服务发现、配置管理、消息总线等。Netflix组件是SpringCloud中的重要组成部......
  • 利用Spring Boot实现微服务的链路追踪
    利用SpringBoot实现微服务的链路追踪大家好,我是微赚淘客返利系统3.0的小编,是个冬天不穿秋裤,天冷也要风度的程序猿!在微服务架构中,一个请求可能会经过多个服务节点,链路追踪成为监控和诊断问题的关键技术。SpringBoot结合SpringCloudSleuth和Zipkin或其他追踪系统,可以有效地实......
  • 利用Spring Boot的RestTemplate进行REST客户端开发
    利用SpringBoot的RestTemplate进行REST客户端开发大家好,我是微赚淘客返利系统3.0的小编,是个冬天不穿秋裤,天冷也要风度的程序猿!在微服务架构中,服务之间的通信通常通过RESTAPI来实现。SpringBoot提供了RestTemplate,这是一个用于同步客户端REST请求的类。本文将介绍如何使用Spri......
  • 利用Spring Boot的Spring Security实现细粒度访问控制
    利用SpringBoot的SpringSecurity实现细粒度访问控制大家好,我是微赚淘客返利系统3.0的小编,是个冬天不穿秋裤,天冷也要风度的程序猿!SpringSecurity是Spring提供的一个功能强大且高度可定制的Java安全框架,用于保护基于Spring的应用程序。在SpringBoot中集成SpringSecurity可以......