Spring IoC 与 依赖注入(DI)

前言:

在前面的学习里,我们了解了 Spring Boot 和 Spring MVC ,可以完成一些简单功能的开发了,但是什么是Spring呢? 它们三者之间又有什么关系?咱们带着问题去学习,我们先看看什么是 Spring.

一,Spring 是什么?

通过前面的学习,我们知道了Spring是一个功能强大、灵活易用的Java框架,它提供了许多用于构建现代企业应用程序的工具和技术。但是这个概念相对来说,还是有些抽象.
我们可以通过一句话来概括Spring,那就是:Spring 是包含了众多工具方法的 IoC 容器. 那什么是容器? 什么是IoC容器?

二,什么是容器?

容器是用来容纳某种物体的装置,生活中的水杯,冰箱,水壶等等这些都是容器.在我们之前学习中接触的容器有哪些?

  • List/ Map — 数据存储的容器
  • Tomcat — Web 容器

三,什么是 IoC?

1️⃣ IoC 是 Spring 的核心思想,那什么是IoC 呢?

其实在前面我们已经使用过了,我们在前面类上添加的 @RestController@Controller 注解,就是把这个对象交给Spring 管理,Spring 框架启动时就会加载该类.它将对象交给Spring框架来管理。
简单来说,就是由Spring框架来负责对象的创建和管理,这就是IoC思想.
IoC(控制反转): Spring 是一个 “控制反转” 的容器 .

2️⃣ 什么是控制反转呢?

就是 控制权 反转,什么控制权发生了反转呢,是获得依赖对象的过程被反转了,也就是说,当需要某个对象时,不需要在通过 new 创建对象了,把创建对象的任务交给容器,程序中只需要 依赖注入(DI) 就可以了,这个容器称为: IoC 容器.
Spring 是一个 IoC 容器,所以有时 Spring 也称为 Spring 容器.

四,介绍 IoC

接下来我们通过案例来了解一下什么是IoC.
在这里插入图片描述

接下来需求有了变更: 随着对车的需求量越来越大,个性化需求也越来越多,我们需要加工多种尺寸的轮胎,这时候就要对程序就行修改了,修改后的代码如下图所示:
在这里插入图片描述

修改之后,其他调用程序也会报错,我们需要继续修改
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
从以上代码可以看出,以上程序的问题是: 当底层代码改动之后,整个调用链上的所有代码都需要修改.程序的耦合性非常高(修改一处代码,影响其它处的代码修改).

我们可以尝试换一种思路,我们先设计汽车的大概样子,然后根据汽车的样子来设计车身,根据车身来设计底盘,最后根据底盘来设计轮子.这时候,依赖关系就倒置过来了: 轮子依赖底盘,底盘依赖车身,车身依赖汽车.

如何实现呢?

我们可以尝试不在每个类中自己创建下级类,如果自己创建下级类就会出现当下级类发生改变操作,自己也要跟着修改.此时,我们只需要将原来由自己创建的下级类,改为 传递 的方式(也就是注入的方式).
因为我们不需要在当前类中创建下级类了,所以下级类即使发生变化(创建或减少参数),当前类本身也无需修改任何代码,这样就完成了程序的解耦.

具体代码实现如下:

public class IocCar {
    public static void main(String[] args) {
        Tire tire = new Tire(17);
        Bottom bottom = new Bottom(tire);
        Framework framework = new Framework(bottom);
        Car car = new Car(framework);

        car.run();
    }


static class Car {
    private Framework framework;

    public Car(Framework framework) {
        this.framework = framework;

        System.out.println("car init...");
    }

    public void run() {
        System.out.println("car run...");
    }
}

static class Framework {
    private Bottom bottom;

    public Framework(Bottom bottom) {
        this.bottom = bottom;

        System.out.println("Framework init...");
    }
}

static class Bottom {
    private Tire tire;

    public Bottom(Tire tire) {
        this.tire = tire;

        System.out.println("bottom init...");
    }
}

static class Tire {
    private int size;

    public Tire(int size) {
        this.size = size;

        System.out.println("tire init...size: " + size );
        }
    }
}

代码经过以上调整,无论底层类如何变化,整个调用链不用做任何改变的,这样就完成代码之间的解耦.

五,IoC 优势

传统开发与IoC开发的对比:

在这里插入图片描述

从中我们发现一个规律,通用程序的实现代码,类创建属性是反的,传统代码是Car 控制并创建了 Framework, Framework 控制并创建了 Bottom,依次往下,而 改进之后的控制权发生的反转 ,不再是使用方创建对象并依赖对象了,而是把依赖注入当前对象中,依赖对象的控制权不再由当前类控制了 .

这样的话,即使依赖类发生任何改变,当前类都是不受影响的,这就是典型的控制反转,也就是 IoC 的实现思想.学到这里,我们大概知道什么是控制反转了,那什么是控制反转容器呢,也就是 IoC 容器.

从上面也可以看出来,IoC容器具备以下优点:

  1. 资源集中管理: IoC容器会帮我们管理一些资源(对象等),我们使用时,只需要去取就可以了.
  2. 我们在创建实例的时候不需要了解其中的细节,降低使用资源双方的依赖程度(耦合性).

Spring 就是一种IoC容器,帮助我们来做这些资源管理.

六,DI 介绍

在IoC开发中,需要把依赖注入当前对象中,那么 依赖注入 又是什么呢?

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

程序运⾏时需要某个资源,此时容器就为其提供这个资源.
从这点来看, 依赖注⼊(DI)和控制反转(IoC)是从不同的⻆度的描述的同⼀件事情,就是指通过引⼊ IoC 容器,利⽤依赖关系注⼊的⽅式,实现对象之间的解耦。

上述代码中, 是通过构造函数的⽅式, 把依赖对象注⼊到需要使⽤的对象中的
在这里插入图片描述

IoC是一种思想,也是"目标",而思想只是一种指导原则,最终还是要有可行的落地方案,而 DI 就属于具体的实现. 也可以说,DI 是 IoC 的一种实现.

七,IoC & DI

对 IoC 和 DI 有了初步的了解,我们接下来具体学习Spring IoC 和 DI 的代码实现

Spring 是一个 IoC 容器 ,作为容器,就应该有两个基本功能:

Spring 容器 管理的主要是对象,这些对象,我们称之为 “Bean”.我们把这些对象交由Spring管理,由Spring来父子对象的创建和销毁.我们程序只需要告诉Spring,那些需要存,以及如何从Spring中取对象.

首先,让我们看一个使用 Java 配置实现DI的例子:

  • 创建一个接口
public interface MessageService {  
    String getMessage();  
}
  • 创建两个不同的实现:
@Component  
public class MessageServiceOne implements MessageService {  
    @Override  
    public String getMessage() {  
        return "Hello from MessageServiceOne";  
    }  
}  
  
@Component  
public class MessageServiceTwo implements MessageService {  
    @Override  
    public String getMessage() {  
        return "Hello from MessageServiceTwo";  
    }  
}
  • 创建一个使用MessageService的类:
@Component  
public class MessagePrinter {  
    private final MessageService messageService;  
  
    @Autowired  
    public MessagePrinter(MessageService messageService) {  
        this.messageService = messageService;  
    }  
  
    public void printMessage() {  
        System.out.println(messageService.getMessage());  
    }  
}

在上面的示例中,我们使用Java配置来定义bean,

  1. 将实现类交给Spring管理,使用注解: @Component
  2. 注解运行时依赖的对象使用注解: @Autowired

Spring的IoC和DI的基本实现, 提高了代码的可维护性和可扩展性。通过使用Spring的IoC和DI功能,我们可以更专注于业务逻辑的实现,而不需要过多关注对象的创建和依赖管理。

八,IoC 详解

通过上面的示例,我们已经知道了 Spring IoC 和 DI 的基本操作,接下来我们系统的学习 Spring IoC 和 DI 的操作吧.

前面我们提到 IoC 控制反转,就是将对象的控制权交给Spring的IoC容器,由IoC容器创建及管理对象,也就是Bean 的存储.

🔸Bean 的存储

在之前的示例里,要把某个对象交给 IoC 容器管理时, 需要在类上添加一个注解: @Component 而Spring为了更好的服务web应用,提供了更丰富的注解.

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

  1. 类注解:@Controller,@Service,@Repository,@Component,@Configuration。
  2. 方法注解:@Bean。

1️⃣@Controller(控制器存储)

使用 @Controller 存储 Bean代码如下:

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

那我们如何观察这个对象是否已经存在Spring容器当中呢?

获取Spring容器中的对象

接下来我们学习如何 从Spring容器中获取对象

 @SpringBootApplication
public class SpringIocDemoApplication {

    public static void main(String[] args) {
        //获取Spring上下⽂对象
        ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class,args);
        //从Spring上下⽂中获取对象
        UserController userController = context.getBean(UserController.class);
        //使⽤对象
        userController.sayHi();
    }
}

观察运行结果,发现成功从Spring中获取到Controller对象,并执行Controller的sayHi方法
在这里插入图片描述

如果把@Controller删掉,再观察运行结果
在这里插入图片描述

报错信息显示:找不到类型是:com.example.demo.controller.UserController的 bean

获取 bean 对象的其他方法

上述代码是根据类型来查找对象,如果Spring容器中,通一个类型存在多个bean的话,怎样获取呢?
ApplicationContext 也提供了其他获取bean 的方式,ApplicattionContext 获取bean 对象的功能,是父类BeanFactory提供的功能。

public interface BeanFactory {

    //以上省略...

    // 1. 根据bean名称获取bean
    Object getBean(String var1) throws BeansException;
    // 2. 根据bean名称和类型获取bean
    <T> T getBean(String var1, Class<T> var2) throws BeansException;
    // 3. 按bean名称和构造函数参数动态创建bean,只适⽤于具有原型(prototype)作⽤域的bean
    Object getBean(String var1, Object... var2) throws BeansException;
    // 4. 根据类型获取bean
    <T> T getBean(Class<T> var1) throws BeansException;
    // 5. 按bean类型和构造函数参数动态创建bean, 只适⽤于具有原型(prototype)作⽤域的bean
    <T> T getBean(Class<T> var1, Object... var2) throws BeansException;

    //以下省略...
}

常用的是上述的1,2,4种,这三种方式,获取到的bean是一样的
其中1,2种都涉及根据名称来获取对象。那bean 的名称是什么呢?

Bean的命名约定

我们看下官⽅⽂档的说明: Bean Overview
在这里插入图片描述
程序开发人员不需要为bean指定名称(BeanId),如果没有显式的提供名称(BeanId),Spring容器将为该bean生成唯一的名称.命名约定使用Java标准约定作为实例字段名.也就是说,bean名称以小写字母开头,然后使用驼峰式大小写。

比如
类名:UserController,Bean的名称为: userController
类名:AccountManager,Bean的名称为: accountManager
类名:AccountService,Bean的名称为: accountService

也有一些特殊情况,当有多个字符并且第一个和第二个字符都是大写时,将保留原始的大小写。

比如
类名:UController,Bean的名称为:UController
类名:AManager,Bean的名称为:AManager

根据这个命名规则, 我们来获取Bean.

@SpringBootApplication
public class SpringIocDemoApplication {

  public static void main(String[] args) {
      //获取Spring上下⽂对象
      ApplicationContext context = SpringApplication.run(SpringIocDemoApplication,args);
      //从Spring上下⽂中获取对象
      //根据bean类型, 从Spring上下⽂中获取对象
      UserController userController1 = context.getBean(UserController.class);
      //根据bean名称, 从Spring上下⽂中获取对象
      UserController userController2 = (UserController) context.getBean("userController");
      //根据bean类型+名称, 从Spring上下⽂中获取对象
      UserController userController3 = context.getBean("userController",UserController.class);

        System.out.println(userController1);
        System.out.println(userController2);
        System.out.println(userController3);
    }
}

运行结果:
在这里插入图片描述

地址相同,说明对象是同一个.

获取bean对象,是父类BeanFactory提供的功能.
在这里插入图片描述

2️⃣ @Service(服务存储)

使用@Service 存储 bean 的代码如下所示:

@Service
public class UserService {
   public void sayHi(String name) {
        System.out.println("Hi," + name);
   }
}

读取 bean 的代码:

@SpringBootApplication
public class SpringIocDemoApplication {
      public static void main(String[] args) {
          //获取Spring上下⽂对象
          ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class,args);
          //从Spring中获取UserService对象
          UserService userService = context.getBean(UserService.class);
          //使⽤对象
          userService.sayHi();
      }
}

观察运行结果,发现成功从Spring中获取到UserService对象,并执行UserService的sayHi方法
在这里插入图片描述

3️⃣ @Repository (仓库存储)

使用@Repository存储bean 的代码如下所示:

@Repository
public class UserRepository {
   public void sayHi() {
      System.out.println("Hi,UserRepository");
   }
}

读取 bean 的代码:

@SpringBootApplication
public class SpringIocDemoApplication {
      public static void main(String[] args) {
          //获取Spring上下⽂对象
          ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class,args);
          //从Spring中获取UserService对象
          UserRepository userRepository = context.getBean(UserRepository.class);
          //使⽤对象
          userRepository.sayHi();
      }
}

观察运行结果,发现成功从Spring中获取到UserRepository对象,并执行UserRepository的sayHi方法
在这里插入图片描述

4️⃣ @Component(组件存储)

使用@Component 存储 bean 的代码如下所示:

@Component
public class UserComponent {
   public void sayHi(){
      System.out.println("Hi,UserComponent");
   }
}

读取 bean 的代码:

@SpringBootApplication
public class SpringIocDemoApplication {
      public static void main(String[] args) {
          //获取Spring上下⽂对象
          ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class,args);
          //从Spring中获取UserService对象
          UserComponent userComponent = context.getBean(UserComponent.class);
          //使⽤对象
          userComponent.sayHi();
      }
}

观察运行结果,发现成功从Spring中获取到UserComponent对象,并执行UserComponent的sayHi方法
在这里插入图片描述

5️⃣ @Configuration (配置存储)

使用@Configuration 存储 bean 的代码如下所示:

@Configuration
public class UserConfiguration {
    public void sayHi() {
        System.out.println("Hi,UserConfiguration");
    }
}

读取 bean 的代码:

@SpringBootApplication
public class SpringIocDemoApplication {
      public static void main(String[] args) {
          //获取Spring上下⽂对象
          ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class,args);
          //从Spring中获取UserService对象
          UserConfiguration userConfiguration = context.getBean(UserConfiguration.class);
          //使⽤对象
          userConfiguration.sayHi();
      }
}

观察运行结果,发现从Spring中获取到UserConfiguration 对象,并执行 UserConfiguration 的sayHi方法
在这里插入图片描述

🔸为什么要这么多类注解?

这个也是和应用分层是相互呼应的,让程序员看到类注解之后,就能直接了解当前类的用途.

  • @Controller: 控制层,接收请求,对请求进行处理,并进行响应.
  • @Service: 业务逻辑层,处理具体的业务逻辑.
  • @Repository: 数据访问层,也称为持久层.负责数据访问操作.
  • @Configuration: 配置层.处理项目中的一些配置信息.

程序的应用分层,调用流程如下:
在这里插入图片描述

1️⃣类注解之间的关系

查看 @Controller / @Service / @Repository / @Configuration 等注解的源码发现:

在这里插入图片描述
可以看到这些注解里面都有一个注解 @Component, 说明它们本身就是属于 @Component 的"子类". @Component 是一个元注解,也就是说可以注解其他类注解, 这些注解被称为 @Component 的衍生注解.

2️⃣ 方法注解 @Bean

五大注解只能加在类上,并且只能加在自己的代码上.

  1. 如果我们引入一个第三方Jar包,也希望交给Spring管理,是没有办法加五大注解.
  2. 对于一个类,定义多个对象时,比如数据库操作,定义多个数据源.

这种情况下,我们就需要使用方法注解 @Bean

我们先来看看方法注解如何使用:

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

然而,当我们写完以上代码,尝试获取 bean 对象中的 user 时却发现,根本获取不到:

@SpringBootApplication
public class SpringIocDemoApplication {
      public static void main(String[] args) {
          //获取Spring上下⽂对象
          ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class,args);
          //从Spring上下文中获取对象
          User user = context.getBean(User.class);
          //使⽤对象
          System.out.println(user);
      }
}

以上程序的执行结果如下:
在这里插入图片描述

这是为什么呢?

3️⃣ 方法注解要配合类注解使用

在 Spring 框架的设计中,方法注解 @Bean 要配合类注解才能将对象正常的存储到 Spring 容器中,如下代码所示:

@Component
public class BeanConfig {
    @Bean
    public User user(){
       User user = new User();
       user.setName("zhangsan");
       user.setAge(18);
       return user;
    }
}

再次执行以上代码,运行结果如下:
在这里插入图片描述

4️⃣ 定义多个对象

对于同一个类,如何定义多个对象呢?
我们看下@Bean的使用

@Component
public class BeanConfig {
    @Bean 
    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(20);
        return user;
    }
}

定义了多个对象的话,我们根据类型获取对象,获取的是那个对象呢?

@SpringBootApplication
public class SpringIocDemoApplication {
      public static void main(String[] args) {
          //获取Spring上下⽂对象
          ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class,args);
          //从Spring上下文中获取对象
          User user = context.getBean(User.class);
          //使⽤对象
          System.out.println(user);
      }
}

运行结果:
在这里插入图片描述

报错信息显示: 期望只有一个匹配,结果发现两个user1,user2.

从报错信息中,我们可以发现:

  • @Bean注解的 bean ,bean 的名称就是它的方法名.
  • 一个类型,存在多个bean 时,我们就不能使用类型来获取对象了.

接下来我们根据名称来获取bean对象

@SpringBootApplication
public class SpringIocDemoApplication {
      public static void main(String[] args) {
          //获取Spring上下⽂对象
          ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class,args);
          //根据bean名称,从Spring上下文中获取对象
          User user1 = (User)context.getBean("user1");
          User user2 = (User)context.getBean("user2");
          System.out.println(user);
          System.out.println(user);
      }
}

运行结果:
在这里插入图片描述

可以看到,@Bean 可以针对同一个类,定义多个对象

5️⃣ 重命名Bean

可以通过设置 name 属性给Bean 对象进行重命名操作,如下代码所示:

 @Bean(name = {"u1","user1"})
 public User user1(){
    User user = new User();
    user.setName("zhangsan");
    user.setAge(18);
    return user;
 }

此时,我们可以使用 u1 就可以获取到 User 对象了,如下代码所示:

@SpringBootApplication
public class SpringIocDemoApplication {
      public static void main(String[] args) {
          //获取Spring上下⽂对象
          ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class,args);
          //从Spring上下文中获取对象
          User u1 = (User)context.getBean("u1");
          //使⽤对象
          System.out.println(u1);
      }
}
  • name = { } 可以省略,如下代码所示:
 @Bean({"u1","user1"})
 public User user1(){
    User user = new User();
    user.setName("zhangsan");
    user.setAge(18);
    return user;
 }
  • 只有一个名称时,{ }也可以省略, 如:
@Bean("u1")
 public User user1(){
    User user = new User();
    user.setName("zhangsan");
    user.setAge(18);
    return user;
 }

6️⃣ 扫描路径

下面我们通过修改项目工程的目录结构,来测试bean对象是否生效:
在这里插入图片描述

再运行代码:

@SpringBootApplication
public class SpringIocDemoApplication {
      public static void main(String[] args) {
          //获取Spring上下⽂对象
          ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class,args);
          //从Spring上下文中获取对象
          User u1 = (User)context.getBean("u1");
          //使⽤对象
          System.out.println(u1);
      }
}

运行结果:
在这里插入图片描述

为什么没有名称为u1的bean呢?
使用五大注解声明的bean,要想生效,需要配置扫描路径,让Spring扫描到这些注解.
就是通过 @ComponentScan 来配置扫描路径.

@ComponentScan({"com.example.demo"})
@SpringBootApplication
public class SpringIocDemoApplication {
      public static void main(String[] args) {
          //获取Spring上下⽂对象
          ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class,args);
          //从Spring上下文中获取对象
          User u1 = (User)context.getBean("u1");
          //使⽤对象
          System.out.println(u1);
      }
}

那为什么前面没有配置 @ComponentScan 注解也可以呢?
@ComponentScan 注解虽然没有显式配置,但实际上已经包含在启动类声明注解@SpringBootApplication中了.

在这里插入图片描述

默认扫描路径是: 启动类所在的目录及其子孙目录,@ComponentScan这个注解可以指定扫描路径,如果没有指定,默认就是该注解所在类的目录及其子孙目录.

九,DI 详解

上面我们学习了控制反转IoC的细节,接下来,我们学习依赖注入DI的细节.
依赖注入是一个过程 ,是指IoC容器在创建Bean时,去提供运行时所依赖的资源,而资源指的就是对象.

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

  1. 属性注入(Field Injection)
  2. 构造方法注入(Constructor Injection)
  3. Setter 注入(Setter Injection)

下面我们按照实际开发中的模式,将 Service 类注入到 Controller 类中.

1️⃣ 属性注入

属性注入是使用 @Autowired 实现的,将 Service 类注入到 Controller 类中 .
Service 类的实现代码如下:

import org.springframework.stereotype.Service;

 @Service
 public class UserService {
     public void sayHi() {
     System.out.println("Hi,UserService");
     }
 }

Controller 类的实现代码如下:

@Controller
public class UserController {
    //注入方法1: 属性注入
    @Autowired
    private UserService userService;
    
    public void sayHi() {
        System.out.println("hi,UserController...");
        userService.sayHi();
    }
}

获取Controller 中的sayHi方法:

@SpringBootApplication
public class SpringIocDemoApplication {
      public static void main(String[] args) {
          //获取Spring上下⽂对象
          ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class,args);
          //从Spring上下文中获取对象
          UserController userController = (UserController)context.getBean("userController");
          //使⽤对象
          userController.sayHi();
      }
}

最终结果如下所示:
在这里插入图片描述

去掉@Autowired,再运行一下程序看看结果
在这里插入图片描述

2️⃣ 构造方法注入

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

@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 来明确指定到底使用哪个构造方法.

3️⃣ Setter 注入

Setter 注入和属性的 Setter 方法实现类似,只不过在设置set方法的时候需要加上 @Autowired 注解,如下代码所示:

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

4️⃣ 三种注入优缺点分析

在这里插入图片描述

@Autowired 存在问题

当程序中同一个类型有多个对象时,使用@Autowired会报错(一些情况下).

  1. 属性名和你需要的对象名保持一致
@Bean("u1")
public UserInfo userInfo() {
    UserInfo userInfo = new UserInfo();
    userInfo.setId(2);
    userInfo.setName("lisi");
    userInfo.setAge(13);
    return userInfo;
}
  1. 使用 @Primary 注解标识默认的对象
@Primary
@Bean
public UserInfo userInfo1(String name) {
    UserInfo userInfo = new UserInfo();
    userInfo.setId(1);
    userInfo.setName(name);
    userInfo.setAge(12);
    return userInfo;
}

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

  1. 使用 @Qualifier : 指定当前要注入的bean对象.
    在这里插入图片描述
    在 @Qualifier 的 value 属性中,指定注入的 bean 的名称,
    在 @Qualifier 注解不能单独使用,必须配合 @Autowired 使用

  2. 使用@Resource:指定是按照bean的名称进行注入.
    在这里插入图片描述
    通过 name 属性指定要注入的 bean 的名称.

@Autowired 与 @Resource 的区别:

  • @Autowired 是 Spring 框架提供的注解,而 @Resource 是 JDK 提供的注解.
  • @Autowired 默认是按照类型注入,而 @Resource 是按照名称注入. 相比于 @Autowired 来说, @Resource 支持更多的参数设置,例如 name 设置,根据名称获取 Bean.
  • @Autowired 默认是按照类型注入的,如果同一个类型存在多个对象,按名称匹配,如果名称匹配不上,就会报错.

十,Spring ,Spring Boot 和 Spring MVC 的关系以及区别:

  • Spring : 简单来说,Spring 是一个开发应用框架,什么样的框架呢, 有这么几个标签: 轻量级,一站式,模块化,其目的是用于简化企业级应用程序开发.
  • Spring MVC: Spring MVC 是 Spring 的一个子框架,Spring 诞生之后,大家觉得很好用,于是按照 MVC 模式设计了一个 MVC 框架(一些用 Spring 解耦的组件),主要用于开发WEB应用和网络接口,所以,Spring MVC 是一个 Web 框架.
  • Spring Boot: Spring Boot 是对 Spring 的一个封装,为了简化 Spring 应用的开发而出现的,中小型企业,没有成本研究自己的框架,使用Spring Boot 可以更加快速的搭建框架,降级开发成本,让开发人员更加专注于 Spring 应用的开发,而无需过多XML的配置和一些底层的实现.

最后一句话总结: Spring MVC 和 Spring Boot 都属于 Spring, Spring MVC 是基于 Spring 的一个MVC 框架,而 Spring Boot 是基于 Spring 的一套快速开发整合包.

整体框架是通过 SpringBoot 搭建的 IoC,DI 功能是 Spring 的提供的, web 相关功能是 Spring MVC 提供的.
在这里插入图片描述

这三者专注的领域不同,解决的问题也不一样,总的来说,Spring 就像一个大家族,有众多衍生产品,但他们的基础都是 Spring, 用一张图来表示他们三个的关系:
在这里插入图片描述

  • 12
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值