【Spring】一问详解什么是Spring IoC和DI

目录

  • 一、IoC & DI入门
    • 1.1、Spring
      • 1.1.1、什么是容器
      • 1.1.2、什么是IoC
    • 1.2、IoC介绍
      • 1.2.1、传统程序开发
      • 1.2.2、问题分析
      • 1.2.3、问题解决
      • 1.2.4、 IoC优势
    • 1.3、Bean的作用域
    • 1.4、DI介绍
  • 二、IoC详解
    • 2.1、Bean的存储
      • 2.1.1、类注解的使用
      • 2.1.2、获取bean对象的其他方式
      • 2.1.3、Bean命名约定
    • 2.2、为什么要这么多类注解?
      • 2.2.1、类注解之间的关系
    • 2.3、方法注解@Bean
      • 2.3.1、方法注解需要配合类注解使用
      • 2.3.2、定义多个对象
      • 2.3.3、重命名Bean
    • 2.4、扫描路径
  • 三、DI详解
    • 3.1、属性注入
    • 3.2、构造方法注入
    • 3.3、Setter注入
    • 3.4、@Autowired存在问题
  • 四、总结

一、IoC & DI入门

1.1、Spring

通过前面的学习, 我们知道了Spring是一个开源框架, 它让我们的开发更加简单. 它支持广泛的应用场景, 有着活跃且庞大的社区, 这就是Spring能够长久不衰的原因.

但是这个概念还是比较抽象.

可以用更具体的话描述Spring, 那就是: Spring是包含了众多工具方法的IoC容器.

那问题来力, 什么是容器? 什么是IoC容器?

1.1.1、什么是容器

容器是用来容纳某种物品的(基本)装置.

我们想想, 之前接触的容器有哪些?

List/Map -> 数据存储容器

Tomcat -> Web容器

1.1.2、什么是IoC

IoC是Spring的核心思想, 也是常见的面试题, 那什么是IoC呢?

IoC我们已经使用了, 我们在前面讲到, 在类上面添加@RestController@Controller注解,就是把这个对象交给Spring管理, Spring框架启动时就会加载该类. 把对象交给Spring管理, 就是IoC思想.

IoC: Inversion of Control(控制反转), 也就是说Spring是一个"控制反转"的容器.

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

控制反转是一种思想, 在生活中也处处体现.

当人们斗地主时, 如果手里只剩下王炸, 可以不用管了, 整个托管即可.

在自动驾驶中, 驾驶员可以掌握驾驶的控制权, 也可以将这个控制权交给自动化驾驶系统.

1.2、IoC介绍

接下来我们通过案例来了解一下什么是IoC.

需求: 造一辆车

1.2.1、传统程序开发

我们的实现思路是这样的:

先设计轮子(Tire), 然后根据轮子的大小设计出底盘(Bottom), 接着根据底盘的设计车身(Framework), 最后根据车身设计好整辆汽车(Car). 这里就出现了一个"依赖"关系: 汽车依赖车身, 车身依赖底盘, 底盘依赖轮子.

在这里插入图片描述

最终实现的代码如下:

/*** @author hanson* @date 2024/4/8 19:16*/
public class NewCarExample {public static void main(String[] args) {Car car = new Car();car.run();}/*** 汽车对象*/static class Car {private FrameWork frameWork;public Car() {this.frameWork = new FrameWork();System.out.println("Car init...");}public void run() {System.out.println("Car run...");}}/*** 车身类*/static class FrameWork {private Bottom bottom;public FrameWork() {this.bottom = new Bottom();System.out.println("Frame init ...");}}/*** 底盘类*/static class Bottom {private Tire tire;public Bottom() {this.tire = new Tire();System.out.println("Bottom init ...");}}/*** 轮胎类*/static class Tire {// 尺寸private int size;public Tire() {this.size = 17;System.out.println("轮胎的尺寸:" + size);}}
}

运行结果:

在这里插入图片描述

1.2.2、问题分析

这样的设计看起来没问题, 但是可维护性却很低.

接下来需求有了变更: 随着对车的需求量越来越大, 个性化需求也越来越多, 我们需要加工多种尺寸的轮胎.

那这个时候就要对上面的程序进行修改了, 修改后的代码如下:

/*** 轮胎类*/
static class Tire {// 尺寸private int size;//        public Tire() {
//            this.size = 17;
//            System.out.println("轮胎的尺寸:" + size);
//        }public Tire(int size) {this.size = size;System.out.println("轮胎的尺寸:" + size);}
}

修改之后, 其它调用程序也会报错, 我们需要修改继续修改(即每一个构造方法都要传一个size)

完整代码如下:

public class NewCarExample {public static void main(String[] args) {Car car = new Car(20);car.run();}/*** 汽车对象*/static class Car {private FrameWork frameWork;public Car(int size) {frameWork = new FrameWork(size);System.out.println("Car init...");}public void run() {System.out.println("Car run...");}}/*** 车身类*/static class FrameWork {private Bottom bottom;public FrameWork(int size) {this.bottom = new Bottom(size);System.out.println("Frame init...");}}/*** 底盘类*/static class Bottom {private Tire tire;public Bottom(int size) {this.tire = new Tire(size);System.out.println("Bottom init...");}}/*** 轮胎类*/static class Tire {//尺寸private int size;public Tire(int size) {this.size = size;System.out.println("轮胎尺寸: " + size);}}
}

从以上代码可以看出, 以上程序的问题是: 当最底层代码改动之后, 整个调用链上的所有代码都需要修改.

程序的耦合度非常高(修改一处代码, 影响其它处代码的修改).

1.2.3、问题解决

在上面的程序当中, 我们是根据轮子的尺寸设计底盘, 轮子尺寸一改, 底盘的设计就得修改. 同样因为我们是根据底盘设计的车身, 那么车身也得修改, 同理汽车设计也得修改, 也就是整个设计都会改.

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

在这里插入图片描述
如何来实现呢?

我们可以尝试不在每个类中创建自己的下级类, 如果自己创建下级类就会出现下级类改变操作, 自己也要跟着修改.

此时, 我们只需要将原来由自己创建的下级类, 改为传递的方式(也就是注入的方式), 因为我们不需要在当前类中创建下级类了, 所以下级类即使发生变化(创建或者减少参数), 当前类不用再改变代码了, 这就实现了程序的解耦.

/*** @author hanson* @date 2024/4/8 19:16*/
public class NewCarExample1 {public static void main(String[] args) {Tire tire = new Tire(20);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("Frame 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() {
//            this.size = 17;
//            System.out.println("轮胎的尺寸:" + size);
//        }public Tire(int size) {this.size = size;System.out.println("轮胎的尺寸:" + size);}}
}

代码通过以上调整, 无论底层类如何变化, 整个调用类是不用做任何改变的, 这样就实现了代码之间的解耦, 从而实现更加灵活, 通用的程序设计了.

1.2.4、 IoC优势

在传统的代码中对象的创建对象的顺序是: Car -> FrameWork -> Bottom -> Tire

改进之后解耦的代码的对象的创建顺序是: Tire -> Bottom -> FrameWork -> Car

在这里插入图片描述

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

因此即使依赖类发生任何改变, 当前类都是不受影响的, 这就是典型的控制反转, 也就是IoC的实现思想.

学到这里, 我们就大概知道什么是控制反转了, 那什么是控制反转容器呢,也就是IoC容器.

在这里插入图片描述

这部分代码就是IoC容器所做的工作.

从上面也可以看出, IoC具有以下优点:

资源不再由资源的双方管理, 而由不使用资源的第三方管理, 这可以带来很多好处.

第一: 资源的集中管理, 实现资源的可配置和易管理, 用的时候只需要从IoC容器中取即可.

第二:降低了使用资源双方的依赖程度, 也就是我们说的耦合度.

1.3、Bean的作用域

Spring Bean支持五种作用域,后三种在web环境下才生效:

作用域说明
singleton容器内同名称的bean只有一个实例(默认)
prototype每次请求该bean时会创建新的实例(非单例)
request每个请求范围内会创建新的实例(web环境中,了解)
session每个会话范围内会创建新的实例(web环境中,了解)
application每个应用范围内会创建新的实例(web环境中,了解)

配置Bean的作用域需要加上下面这个注解
@Scope

首先测试单例模式

@Scope("singleton")
@Controller  //将对象存储到Spring中
public class MyController1 {public void sayHi(){System.out.println("Hi, UserController...");}
}
@SpringBootApplication
public class SpringIocDiApplication {public static void main(String[] args) {//获取Spring上下文对象ApplicationContext context = SpringApplication.run(SpringIocDiApplication.class, args);for (int i = 0; i < 10; i++) {MyController1 myController1 = context.getBean(MyController1.class);myController1.sayHi();System.out.println(myController1);}}
}

运行后发现
在这里插入图片描述
我们调用10次getBean方法,得到的始终是一个对象。

现在将singleton变成prototype再次测试

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

在这里插入图片描述
我们调用10次getBean方法,得到了10个bean对象

🎈注意

  • 默认singleton的bean,在容器启动的时候被创建,可以使用@Lazy注解来延迟初始化(延迟到第一次使用时)
  • prototype的bean,每一次使用该bean的时候都会创建一个新的实例
  • 实际开发中,大多数Bean的单例的,也就是说大部分bean不需要配置scope属性

1.4、DI介绍

DI:Dependency Injection(依赖注入).

容器在运行期间, 动态的为应用程序提供运行时所依赖的资源, 称之为依赖注入.

程序运行时需要某个资源, 容器就可以提供这个资源.

从这点来看, IoC(控制反转)和DI(依赖注入)是从不同角度描述同一件事情, 就是指通过引入IoC容器, 利用依赖关系注入的方式,
实现对象之间的解耦.

之前的代码中, 就是通过构造函数的方式, 将依赖的对象注入到需使用对象中.

在这里插入图片描述

DI是IoC的一种实现.

二、IoC详解

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

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

2.1、Bean的存储

我们之前只讲到了@Component注解来使得对象交给IoC容器管理. 而Spring为了更好地管理Web应用程序, 提供了更丰富的注解.

当前有两类注解:

1.类注解@Controller(控制器存储), @Service(服务存储), @Reposity(仓库), @Component(组件), @Configuration(配置)
2.方法注解: @Bean

2.1.1、类注解的使用

由于这里五个类注解在功能上基本是一致的, 所以这里用@Controller进行介绍.

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

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

如何观察这个对象已经存在Spring容器当中了呢?

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


@SpringBootApplication
public class SpringIocDiApplication {public static void main(String[] args) {//获取Spring上下文对象ApplicationContext context = SpringApplication.run(SpringIocDiApplication.class, args);//从Spring上下文中获取对象MyController myController = context.getBean(MyController.class);//获取对象myController.sayHi();}
}

ApplicationContext 翻译过来就是: Spring上下文.

因为对象交给Spring管理, 所以获取对象要从Spring中获取, 那么就得先得到Spring的上下文.

关于上下文的概念

在计算机领域, 上下文这个概念, 它是指支持进程调度的重要属性.
等下次调度回CPU时,会把寄存器上的回复回来.

这里的上下文, 就是指当前的运行环境, 也可以看作是一个容器, 容器里存很多内容, 这些内容是当前运行的环境.

观察运行结果, 发现成功从Spring中获取到Controller对象, 并执行Controller的SayHi方法.

在这里插入图片描述

2.1.2、获取bean对象的其他方式

上述代码是根据类型来查找对象, 如果Spring容器中, 同一个类型存在多个bean的话, 怎么获取呢?

ApplicationContext也提供了其它获取bean的方式, ApplicationContext获取bean对象的功能, 是父类BeanFactory提供的功能.

在这里插入图片描述

可以发现, 我们获取bean共有五种方法, 而常用的是第1, 2, 4三种. 第一种是根据名称获取bean对象, 第二种是通过名称, 类型获取bean对象. 第三种是通过类型获取对象.

其中1,2种都涉及根据名称来获取对象, bean的名称是什么呢?

Spring bean是Spring框架在运行时管理的对象, Spring会给管理的对象起一个名字.

比如学校管理学生, 会给每个学生分配一个学号, 根据学号, 可以找到对应学生.

Spring也是如此, 给每个对象起一个名字, 根据Bean名称(BeanId)就可以获取到对应对象.

2.1.3、Bean命名约定

程序开发人员不需要为bean指定名称(BeanId), 如果没有显式提供名称(BeanId), Spring容器将为该bean生成唯一的名称.

命名约定使用Java标准约定作为实例字段名, 也就是说bean名称以小写字母开头, 然后使用驼峰时大小写.

eg. MyController -> myController

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

eg. UController -> UController

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

@SpringBootApplication
public class SpringIocDiApplication {public static void main(String[] args) {//获取Spring上下文对象ApplicationContext context = SpringApplication.run(SpringIocDiApplication.class, args);//根据bean类型, 从Spring上下文中获取对象.MyController myController1 = context.getBean(MyController.class);//根据bean名称, 从Spring上下文中获取对象MyController myController2 = (MyController) context.getBean("myController");//根据bean名称+对象, 从Spring上下文中获取对象MyController myController3 = context.getBean("myController", MyController.class);//使用对象System.out.println(myController1);System.out.println(myController2);System.out.println(myController3);}}

运行结果:

在这里插入图片描述
地址一样, 说明对象是一个.

获取bean对象, 是父类BeanFactory提供的功能.

ApplicationContext VS BeanFactory (面试题)

继承关系和功能来说: Spring有两个顶级的接口: ApplicationContext 和BeanFactory. 其中

BeanFactory提供了基础的访问容器的能力, 而Application属于BeanFactory的子类.
它除了继承BeanFactory的所有功能之外, 还有独特的特性: 对国际化的支持, 资源访问支持, 以及事件传播方面的支持.

从性能来说:
ApplicationContext是一次性加载并初始化的所有Bean对象(可类似于饿汉模式),BeanFactory是需要哪个再去加载哪个,
因此更加轻量.(空间换时间) (一般建议用ApplicationContext, 因为现在机器的性能更高了).

2.2、为什么要这么多类注解?

这个也是和前面讲的应用分层相呼应. 让程序员看到注解之后, 就能知道这个类的用途.

@Controller: 控制层, 接收请求, 对请求进行处理, 并进行响应.

@Service: 业务逻辑层,处理具体的逻辑.

@Respository: 数据访问层, 也称为持久层. 负责数据的访问操作.

@Configuration: 配置层. 处理项目中的一些配置信息.

这个就类似于车牌号的功能, 一看开头, 不管后面的字符串是什么, 就能知道这个车是哪里的.

程序的应用分层, 调用逻辑如下:

在这里插入图片描述

2.2.1、类注解之间的关系

查看@Controller, @Configuration, @Repository, @Service的源码发现:

这些注解中其实都有一个元注解: @Component, 说明它们本身就属于@Component的"子类", 说明它们本身就是属于@Component的"子类". @Component是一个元注解, 也就是说可以注解其它类注解, 如@Controller, @Service, @Repository, 这些注解都可以说是@Component的衍生注解.

2.3、方法注解@Bean

类注解是添加到某个类上的, 但是存在两个问题:

1.使用外部包里的类, 没办法添加类注解

2.一个类, 需要多个对象, 比如多个数据源.

这种场景, 我们就需要注解@Bean

2.3.1、方法注解需要配合类注解使用

在Spring框架的设计中, 方法注解@Bean要配合类注解才能将对象正常地存储到Spring容器中.

举个栗子:

@Component
public class BeanConfig {@Beanpublic User user(){User user = new User();user.setName("Hanson");user.setAge(20);return user;}
}

运行下述代码:

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

得到运行结果:

在这里插入图片描述

2.3.2、定义多个对象

对于同一个类, 如何定义多个对象呢?

比如多数据源的场景, 类是同一个, 但是配置不同, 指向不同的数据源.

我们看下@Bean的使用

@Component
public class BeanConfig {@Beanpublic User user1(){User user = new User();user.setName("Hanson1");user.setAge(20);return user;}    @Beanpublic User user2(){User user = new User();user.setName("Hanson2");user.setAge(20);return user;}
}

当定义到多个对象时, 我们继续使用上面的代码, 能获取到什么对象? 我们来运行一下:

在这里插入图片描述

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

从报错信息中, 可以看出来, @Bean注解的bean, bean名称就是它的方法名.

接下来以正确的方式来获取Bean对象.

public static void main(String[] args) {//获取Spring上下文对象ApplicationContext context = SpringApplication.run(SpringIocDiApplication.class, args);User user1 = (User) context.getBean("user1");User user2 = (User) context.getBean("user2");System.out.println(user1);System.out.println(user2);}

运行结果:

在这里插入图片描述

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

2.3.3、重命名Bean

@Bean(name = {“u1”, “user1”})

添加类似的注解仍可以运行成功, 这是将user1重命名为u1的一种方式. 类似地, 还有如下方式:

@Bean({“u1”, “user1”})

//只有一个名称时, 其它的内容可以省略.

@Bean(“u1”)

2.4、扫描路径

使用前面注解声明的bean, 一定会生效吗?

不一定(原因: bean想要生效, 还需要被Spring扫描).

当在同一个包内, 可以直接被Spring扫描到, 如果不在同一个包, 就可以通过@ComponentScan来配置扫描路径.

@ComponentScan(value = "com.hanson.ioc.controller")
@SpringBootApplication
public class SpringIocDiApplication {public static void main(String[] args) {//获取Spring上下文对象ApplicationContext context = SpringApplication.run(SpringIocDiApplication.class, args);// 从上下文中获取对象User user = context.getBean("u1",User.class);System.out.println(user);}
}

也可以使用@ComponentScans配置多个包路径.

这种做法仅作了解, 不做推荐使用.

三、DI详解

接下来学习一下依赖注入DI的细节.

依赖注入是一个过程, 是指IoC容器在创建Bean时, 去提供运行时所依赖的资源, 而资源指的就是对象. 在之前的案例中, 使用了@Autowired这个注解, 完成了依赖注入这个操作.

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

在一些文章中, 依赖注入也称为"对象注入", “属性装配”, 具体含义需要结合文章的上下文理解.

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

1.属性注入(Field Injection)

2.构造方法注入(Constructor Injection)

3.Setter注入(Setter Injection).

3.1、属性注入

属性注入通过@Autowired实现的, 这里将Service类注入到Controller类中.

@Service
public class MyService {public void sayHi() {System.out.println("Hi, MyService");}
}
@Controller //将对象存储到Spring中
public class MyController2 {//注入方法1: 属性注入@Autowired private MyService myService;public void sayHi() {System.out.println("Hi, UserController...");myService.sayHi();}
}

使用:

@SpringBootApplication
public class SpringbootDemoApplication {public static void main(String[] args) {//获取Spring上下文对象ApplicationContext context = SpringApplication.run(SpringbootDemoApplication.class);MyController2 myController2 = context.getBean(MyController2.class);myController2.sayHi();}
}

最终运行结果如下:

在这里插入图片描述

3.2、构造方法注入

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

@Controller //将对象存储到Spring中
public class MyController3 {private MyService myService;//注入方法2: 构造方法注入@Autowiredpublic MyController3(MyService myService) {this.myService = myService;}public void sayHi() {System.out.println("Hi, UserController...");myService.sayHi();}
}

注意事项: 如果类中只有一个构造方法, 那么@Autowired注解可以省略(在Spring中, 如果一个类只有一个构造方法,
并且该构造方法不包含任何参数, 那么Spring在实例化这个类的时候会自动将其作为一个Bean注入到容器中); 如果类中有多个构造方法,
那么需要添加上@Autowired来明确指明到底使用哪个构造方法
.

3.3、Setter注入

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

@Controller //将对象存储到Spring中
public class MyController4 {private MyService myService;//注入方法3: Setter方法注入@Autowiredpublic void setMyService(MyService myService) {this.myService = myService;}public void sayHi() {System.out.println("Hi, UserController...");myService.sayHi();}
}

这里注意, 对于Setter方法, 是一定要写@Autowired的.

3.4、@Autowired存在问题

当同一类型存在多个bean时, 使用@Autowired会存在问题.

@Component
public class BeanConfig {@Bean("u1")public User user1() {User user = new User();user.setName("Hanson1");user.setAge(20);return user;}@Beanpublic User user2() {User user = new User();user.setName("Hanson2");user.setAge(18);return user;}
}
@Controller
public class MyController5 {@Autowiredprivate User user;public void sayHi() {System.out.println("hi, UserController5...");System.out.println(user);}
}

在这里插入图片描述

报错的原因是, 非唯一的Bean对象.

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

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

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

使用@Qualifier注解: 指定要注入的bean对象. 在@Qualifiervalue属性中,指定注入bean的名称.

@Qualifier注解不能单独使用, 必须配合@Autowired使用.

@Controller
public class MyController6 {@Qualifier("user2") //指定bean的名称.@Autowiredprivate User user;public void sayHi() {System.out.println("hi, UserController6...");System.out.println(user);}
}

使用@Resource注解: 是按照bean的方式注入. 通过name属性指定要注入的bean名称.

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

常见面试题:

@Autowired和@Resource的区别

@Autowired是Spring框架提供的注解, 而@Resource是JDK提供的注解.(@Primary,@Qualifier是Spring提供的注解).

@Autowired默认是按照类型注入, 而@Resource是按名称注入. 相比于@Autowired来说,
@Resource支持更多的参数配置, 例如name设置, 通过name获取bean.

四、总结

在项目中,我们自定义一个类,如果我们想把这个类交给ioc容器管理,加上@Component衍生注解即可,
如果这个类不是我们自己自定义的,是我们引入第三方依赖中的,而且我们还想把这个类交给ioc容器管理,那么我们应该定义一个方法,在这个方法上加上@Bean

文章代码:GitHub

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/801521.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Linux 命令】内核、驱动调试手段总结

文章目录 1. printk2. strace3. Itrace4. ptrace5. ftrace6. 动态打印7. perf8. devmem9. demsg参考&#xff1a; 1. printk **printk()**是 Linux 内核中最广为人知的函数之一。它是我们打印消息的标准工具&#xff0c;通常也是追踪和调试的最基本方法。 虽然 printk() 是基…

element问题总结之el-table使用fixed中 header换行后固定行错位问题/固定列下陷问题

固定列下陷问题 效果图问题描述解决方案1、为table添加ref2、调用节点重新自适应方法doLayout3、在操作表头的时候触发的函数header-dragend绑定doLayout方法4、成功解决 效果图 问题描述 在使用el-table的fixed中&#xff0c;发现如果header拖拽文本折行的时候会出现下陷 解…

【大数据】大数据概论与Hadoop

目录 1.大数据概述 1.1.大数据的概念 1.2.大数据的应用场景 1.3.大数据的关键技术 1.4.大数据的计算模式 1.5.大数据和云计算的关系 1.6.物联网 2.Hadoop 2.1.核心架构 2.2.版本演进 2.3.生态圈的全量结构 1.大数据概述 1.1.大数据的概念 大数据即字面意思&#x…

SRIO学习(3)使用SRIO IP核进行设计

文章目录 前言一、设计框图二、模块介绍三、上板验证 前言 本文将通过使用SRIO IP核实现数据通信&#xff0c;重点在于打通数据链路&#xff0c;具体的协议内容设计并非重点&#xff0c;打通了链路大家自己根据设计需求来即可。 一、设计框图 看了前面高速接口的一些设计&am…

探索算力(云计算、人工智能、边缘计算等):数字时代的引擎

引言 在数字时代&#xff0c;算力是一种至关重要的资源&#xff0c;它是推动科技创新、驱动经济发展的关键引擎之一。简而言之&#xff0c;算力即计算能力&#xff0c;是计算机系统在单位时间内完成的计算任务数量或计算复杂度的度量。随着科技的不断发展和应用范围的不断扩大…

流式密集视频字幕

流式密集视频字幕 摘要1 IntroductionRelated Work3 Streaming Dense Video Captioning Streaming Dense Video Captioning 摘要 对于一个密集视频字幕生成模型&#xff0c;预测在视频中时间上定位的字幕&#xff0c;理想情况下应该能够处理长的输入视频&#xff0c;预测丰富、…

C语言 | Leetcoce C语言题解之第18题四数之和

题目&#xff1a; 题解&#xff1a; int comp(const void* a, const void* b) {return *(int*)a - *(int*)b; }int** fourSum(int* nums, int numsSize, int target, int* returnSize, int** returnColumnSizes) {int** quadruplets malloc(sizeof(int*) * 1001);*returnSize…

企业版ChatGPT用户激增至60万;百度文心一言推出个性化声音定制功能

&#x1f989; AI新闻 &#x1f680; 企业版ChatGPT用户激增至60万 摘要&#xff1a;OpenAI首席运营官Brad Lightcap在接受采访时透露&#xff0c;企业版ChatGPT的注册用户已超60万&#xff0c;相较2024年1月的15万用户&#xff0c;短短三个月内增长了300%。这一版本自2023年…

C++11新特性(2) ——动态内存和智能指针从入门到入坑

动态内存与智能指针 动态内存的使用十分容易出现问题&#xff08;内存泄漏/非法内存&#xff09;&#xff0c;而智能指针能更安全、容易的使用动态内存&#xff0c;因为他负责自动释放所指向的对象&#xff0c;并且在出现异常时&#xff0c;也会自动释放。 两种智能指针&#…

《springcloud alibaba》 四 seata安装以及使用

目录 准备调整db配置准备创建数据库 seata配置nacos配置confi.txt下载向nacos推送配置的脚本 启动seata新建项目order-seata项目 订单项目数据库脚本pom.xmlapplication.yml启动类实体类dao类service类controller类feign类mapper类 stock-seata 库存项目数据库脚本pom.xmlappli…

STM32学习和实践笔记(5):时钟树

STM32一共有4个时钟源。外部时钟高低速各一个&#xff0c;内部时钟高低速各一个。 外部高速时钟是&#xff1a;4-16MHZ的HSE OSC。HS表示高速high speed. E表示外部的external。开发板该处安装的8M晶振。 外部低速时钟是&#xff1a;32.768KHz的LSI OSC。LS表示高速low speed…

为说阿拉伯语的国家进行游戏本地化

阿拉伯语是由超过4亿人使用的语言&#xff0c;并且是二十多个国家的官方语言。进入这些国家的市场并非易事——虽然他们共享一种通用语言&#xff0c;但每个国家都有自己独特的文化&#xff0c;有自己的禁忌和对审查的处理方式。这就是为什么视频游戏公司长期以来都远离阿拉伯语…

Qt QML的插件(Qt Quick 2 Extension Plugin)方法

Qt Quick的插件方法 序言环境前置注意概念——Qt Quick插件的相关知识插件里的qml文件模块名的相关知识模块名本身注意事项模块名版本注意事项 以示例来说明创建插件qmltypes的生成qmltypes的可能性失效 插件的编码注意1、插件模块版本控制2、pro里的注意 调用插件插件信息输入…

华为手机 鸿蒙系统 或者安卓系统的百度网盘下载的文件保存在手机什么位置如何查看

华为手机 鸿蒙系统 或者安卓系统的百度网盘下载的文件保存在手机什么位置如何查看 连接电脑后一般在这里位置 计算机\Mate 20 Pro (UD)\内部存储\Download\BaiduNetdisk 也就是用usb&#xff08;数据线&#xff0c;不是充电线&#xff0c;要四心的 )连接手机后&#xff0c;打…

计算机网络——40各个层次的安全性

各个层次的安全性 安全电子邮件 Alice需要发送机密的报文m给Bob Alice 产生随机的对称秘钥&#xff0c; K s K_s Ks​使用 K s K_s Ks​对报文进行加密&#xff08;为了效率&#xff09;对 K s K_s Ks​使用Bob的公钥进行加密发送 K s ( m ) K_s(m) Ks​(m)和 K B ( K S ) K…

uniapp如何配置后使用uni.chooseLocation等地图位置api

在uniapp中想要使用uni.getLocation、uni.chooseLocation ……api的时候我们需要在小程序就开启配置&#xff0c;不然无法使用。 第一步&#xff1a;首先找到manifest.json 第二步&#xff1a;点击源码视图 第三步&#xff1a;在 mp-weixin 加入下面代码 "permission&…

Paper Digest | GPT-RE:基于大语言模型针对关系抽取的上下文学习

持续分享 SPG 及 SPG LLM 双驱架构应用相关进展 1、动机 在很多自然语言处理任务中&#xff0c;上下文学习的性能已经媲美甚至超过了全资源微调的方法。但是&#xff0c;其在关系抽取任务上的性能却不尽如人意。以 GPT-3 为例&#xff0c;一些基于 GPT-3 的上下文学习抽取方…

DXP学习002-PCB编辑器的环境参数及电路板参数相关设置

目录 一&#xff0c;dxp的pcb编辑器环境 1&#xff0c;创建新的PCB设计文档 2&#xff0c;PCB编辑器界面 1&#xff09;布线工具栏 2&#xff09;公用工具栏 3&#xff09;层标签栏 ​编辑 3&#xff0c;PCB设计面板 1&#xff09;打开pcb设计面板 4&#xff0c;PCB观…

【HTML】简单制作一个分形动画

目录 前言 开始 HTML部分 效果图 ​编辑​编辑​编辑​编辑总结 前言 无需多言&#xff0c;本文将详细介绍一段代码&#xff0c;具体内容如下&#xff1a; 开始 首先新建文件夹&#xff0c;创建一个文本文档&#xff0c;其中HTML的文件名改为[index.html]&a…

JavaEE初阶之单例模式详解

目录 题外话 正题 单例模式 概念 优点 缺点 饿汉式单例模式 代码及详解 懒汉式单例模式 代码及详解 小结 题外话 昨天爬山去了,回来吃了个烧烤有点累,昨天旷了一天,每周稳定发个五篇文章是没什么太大问题的 正题 单例模式 概念 是一种常见的软件设计模式,确保一个类…