文章目录

  • IoC & DI 介绍
    • IoC介绍
    • DI 介绍
  • 组件注册
    • Bean 命名约定
    • 方法注解 @Bean
    • 总结
  • 扫描路径
  • DI 详解
    • 属性注入
    • 构造方法注入
    • Setter 注入
    • 三种注入优缺点分析
  • 当同一类型存在多个Bean时,直接使用@Autowired会存在问题
    • 使用@Primary注解
    • 使用@Qualifier注解
    • 使用Bean的名称
    • 使用@Resource注解

IoC & DI 介绍

IoC介绍

Spring 是什么?

Spring 是一个开源框架,让我们的开发更加简单。我们用一句更具体的话来概括 Spring,那就是:Spring 是包含了众多工具方法的 IoC 容器

那么问题来了,什么是容器?什么是 IoC 容器?接下来我们一起来看

什么是容器?

容器是用来容纳某些物品的装置。生活中的水壶,冰箱都是容器。Java中 List,Map 等集合类(数据存储容器),Tomcat(Web 容器) 也是容器

什么是 IoC: Inversion of Control (控制反转)?

IoC 是 Spring 的核心思想。其实在类上面添加 @RestController 或者@Controller 注解,就是把这个对象交给 Spring 管理,Spring 框架启动时就会加载该类。而把对象交给 Spring 管理这个理念,就是 IoC 思想

什么是控制反转呢?

也就是控制权反转(获得依赖对象的过程被反转了)

当需要某个对象时,传统开发模式中需要自己 new 对象,而 IoC 不需要自己创建,是把创建对象的任务交给容器,程序中只需要依赖注入 (Dependency Injection,DI) 就可以了,这个容器称为 IoC 容器, Spring 就是一个 IoC 容器,所以有时 Spring 也称为 Spring 容器

示例:造一辆车(分为四部分:轮子,底盘,车身,汽车)

传统程序开发的实现思路是这样的:先设计轮子(Tire),然后根据轮子的大小设计底盘(Bottom),接着根据底盘设计车身(Framework),最后根据车身设计好整个汽车(Car)。这里就出现了一个"依赖"关系:汽车依赖车身,车身依赖底盘,底盘依赖轮子

实现代码如下:

public class NewCarExample {public static void main(String[] args) {Car car = new Car();car.run();}//汽车static class Car {private Framework framework;public Car() {framework = new Framework();System.out.println("Car init....");}public void run() {System.out.println("Car run...");}}//车身static class Framework {private Bottom bottom;public Framework() {bottom = new Bottom();System.out.println("Framework 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);}}
}

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

比如接下来需求有了变更:我们需要加工多种尺寸的轮胎。那这个时候就要对上面的程序进行修改了,修改后的代码如下所示:

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) {bottom = new Bottom(size);System.out.println("Framework 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);}}
}

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

问题就是:当最底层类创建或减少参数之后,整个调用链上的所有类都需要修改。程序的耦合度非常高 (修改一处代码,影响其他处的代码修改)

此时,我们可以把上级类自己创建下级类的方式,改为传递的方式(也就是注入的方式),这样即使下级类发生变化(创建或减少参数),当前类也无需修改任何代码

基于以上思路,我们把示例改造一下,把创建子类的方式,改为注入传递的方式,具体实现代码如下:

public class IocCarExample {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("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 优势

传统代码中对象创建顺序是:Car->Framework->Bottom->Tire
改进后的代码的对象创建顺序是:Tire->Bottom->Framework->Car

传统代码是 Car 控制并创建了 Framework,Framework 控制并创建了 Bottom,依次往下,而改进之后的控制权发生反转,不再是使用方创建并控制依赖对象了,而是把依赖对象注入到当前对象中,依赖对象的控制权不再由当前类控制了,这样的话,即使依赖类发生改变,当前类都是不受影响的,这就是典型的控制反转,也就是 IoC 的实现思想

从上面可以看出,loC 容器的资源不由资源双方管理,而由不使用资源的第三方管理,这可以带来很多好处:

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

总结

IoC (控制反转),就是将对象的控制权交给 Spring 的 IoC 容器,由 IoC 容器创建及管理对象

DI 介绍

上面学习了 loC, 那么什么是 DI: Dependency Injection (依赖注入)呢?

容器在运行期间,程序需要某个资源,此时容器就为其提供这个资源。动态的为应用程序提供运行时所依赖的资源,称之为依赖注入

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

上述代码中,是通过构造函数的方式,把依赖对象注入到需要使用的对象中的:轮胎注入到底盘,底盘再注入到车身,最后车身注入到汽车中

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

对 IoC 和 DI 有了初步的了解,接下来我们具体学习 Spring IoC 和 DI 的代码实现。既然 Spring 是一个 IoC(控制反转)容器,作为容器,那么它就具备两个最基础的功能:存和取

Spring 容器管理的主要是对象,这些对象,我们称之为 “Bean”。 我们把这些对象交由 Spring 管理,由 Spring 负责对象的创建和销毁。我们程序只需要告诉 Spring 哪些需要存,以及如何从 Spring 中取出对象并注入到需要使用的类中就行了

  1. 组件注册(交给 Spring 管理 —— 存):使用@Component及其派生注解(如@Controller@Service@Repository@Configuration)可以将类标记为 Spring 管理的 Bean。这些注解会触发组件扫描机制,使 Spring 自动发现并注册这些类
  2. 依赖注入(取):使用@Autowired注解可以自动注入依赖的 Bean。该注解可用于构造函数、Setter 方法或字段,让 Spring 自动解析并注入所需的依赖对象

组件注册

要把某个对象交给 IoC 容器管理,共有两类注解可以实现:

  • 类注解: @Controller@Service@Repository@Component@Configuration
  • 方法注解: @Bean

接下来我们分别来看

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

//把 TestController 交给 Spring 管理,由 Spring 来管理对象
@org.springframework.stereotype.Controller
public class TestController {public void sayHi() {System.out.println("Hi");}
}

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

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

更改启动类代码如下,注意把类名换成自己的

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;@SpringBootApplication
public class J20250624Application {public static void main(String[] args) {//获取Spring上下文对象,可以理解成拿到Spring容器ApplicationContext context = SpringApplication.run(J20250624Application.class, args);//从Spring上下文中获取对象TestController bean = context.getBean(TestController.class);//使用对象System.out.println(bean);bean.sayHi();}
}

ApplicationContext 翻译就是: Spring 上下文

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

关于上下文的概念

上下文就是指当前的运行环境。比如进行线程切换的时候,切换前会把线程的状态信息暂时储存起来,这里的上下文就包括了当前线程的信息,等下次该线程又得到 CPU 的时候,从上下文中拿到线程上次运行的信息

运行启动类,观察运行结果,发现成功从 Spring 中获取到 TestController 对象,并执行 TestController 的 sayHi 方法

在这里插入图片描述

如果把@Controller注释掉,就会报错

在这里插入图片描述

在这里插入图片描述

也就是没有org.example.j20250624.TestController这个对象

那么自动创建的 Bean 的名称是什么呢?

Bean 是 Spring 框架在运行时管理的对象,Spring 会给管理的对象起一个名字方便管理,根据 Bean 的名称就可以获取到对应的对象

Bean 命名约定

我们看下官方文档的说明: Bean Overview :: Spring Framework

在这里插入图片描述
程序开发人员不需要为 Bean 指定名称, 如果没有显式的提供名称,Spring容器自动为该 Bean 生成唯一的名称

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

比如:

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

也有一些特殊情况,当类名有多个字符并且第一个和第二个字符都是大写时,将保留原始的大小写。规则与 java.beans.Introspector.decapitalize 定义的规则相同

比如:

  • 类名:UController,Bean的名称为:UController
  • 类名:AManager,Bean的名称为:AManager
public class Test {public static void main(String[] args) {String className = "UserController";System.out.println(Introspector.decapitalize(className));}
}

在这里插入图片描述

public class Test {public static void main(String[] args) {String className = "UController";System.out.println(Introspector.decapitalize(className));}
}

在这里插入图片描述

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

@SpringBootApplication
public class J20250624Application {public static void main(String[] args) {//获取Spring上下文对象,可以理解成拿到Spring容器ApplicationContext context = SpringApplication.run(J20250624Application.class, args);// 从 Spring 上下文中获取对象// 方式1:根据 bean 类型获取TestController testController1 = context.getBean(TestController.class);// 方式2:根据 bean 名称获取(需强制类型转换)TestController testController2 = (TestController) context.getBean("testController");// 方式3:根据 bean 名称和类型获取TestController testController3 = context.getBean("testController", TestController.class);// 验证获取的对象实例System.out.println(testController1);System.out.println(testController2);System.out.println(testController3);}
}

在这里插入图片描述

地址一样,说明对象是同一个。能获取 Bean 对象,其实是 BeanFactory 提供的功能

ApplicationContext vs BeanFactory(常见面试题)

  • 从继承关系和功能方面来说:Spring 容器有两个顶级接口: BeanFactory 和 ApplicationContext。其中 BeanFactory 提供了基础的访问容器能力,而 ApplicationContext 属于 BeanFactory 的子接口,它继承了 BeanFactory 的所有功能之外,还添加了国际化、事件发布、AOP 等方面的支持
  • 从性能方面来说:ApplicationContext 是一次性加载并初始化所有的 Bean 对象,而 BeanFactory 是需要哪个才去加载哪个,因此 BeanFactory 更加轻量。两者本质是时间与空间的权衡

除了@Controller,使用 @Service@Repository@Configuration@Component一样也可以把类交给spring管理

为什么要这么多类注解?

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

  • @Controller:控制层。接收请求,对请求进行处理,并进行响应
  • @Service:业务逻辑层。处理具体的业务逻辑
  • @Repository:数据访问层,也称为持久层。负责数据访问操作
  • @Configuration:配置层。处理项目中的一些配置信息
  • @Component:通用组件层。当一个类不好明确归类到其他层的时候就可以用 @Component

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

在这里插入图片描述

类注解之间的关系

查看 @Controller / @Service / @Repository / @Configuration 的源码发现这些注解里面都有 @Component

@Component 是一个元注解,也就是说可以注解其他类注解,用于标识一个类为 Spring 管理的 Bean。当 Spring 进行组件扫描时,会自动发现并注册被 @Component 标记的类。如 @Controller@Service@Repository 等。这些注解被称为 @Component 的衍生注解

方法注解 @Bean

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

  1. 使用外部包里的类,没办法添加类注解
  2. 五大注解交给Spring管理的都是单例的。一个类,可能需要多个对象,比如多个数据源

这些场景,我们就需要使用方法注解 @Bean

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

public class User {private String name;private int age;public User(String name, int age) {this.name = name;this.age = age;}@Overridepublic String toString() {return "User{" +"name='" + name + '\'' +", age=" + age +'}';}
}
public class BeanConfig {@Beanpublic User user(){return new User("zhangsan", 23);}
}

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

@SpringBootApplication
public class J20250624Application {public static void main(String[] args) {//获取Spring上下文对象,可以理解成拿到Spring容器ApplicationContext context = SpringApplication.run(J20250624Application.class, args);// 从 Spring 上下文中获取对象User user = context.getBean(User.class);//使用对象System.out.println(user);}
}

在这里插入图片描述

这是因为@Bean必须搭配五大注解使用

@Component
public class BeanConfig {@Beanpublic User user(){return new User("zhangsan", 23);}
}

再次执行,运行结果如下:

在这里插入图片描述

定义多个对象

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

如果让Spring再管理一个User对象,用类型取会报错

@Component
public class BeanConfig {@Beanpublic User user1(){return new User("zhangsan", 18);}@Beanpublic User user2(){return new User("lisi", 19);}
}
@SpringBootApplication
public class J20250624Application {public static void main(String[] args) {//获取Spring上下文对象,可以理解成拿到Spring容器ApplicationContext context = SpringApplication.run(J20250624Application.class, args);// 从 Spring 上下文中获取对象User user = context.getBean(User.class);//使用对象System.out.println(user);}
}

在这里插入图片描述

报错信息显示:期望只有一个匹配,结果发现了两个,user1,user2。@Bean管理的对象的名称就是方法名

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

@SpringBootApplication
public class J20250624Application {public static void main(String[] args) {//获取Spring上下文对象,可以理解成拿到Spring容器ApplicationContext context = SpringApplication.run(J20250624Application.class, args);//根据bean名称,从Spring上下文中获取对象User user1 = (User) context.getBean("user1");User user2 = (User) context.getBean("user2");//使用对象System.out.println(user1);System.out.println(user2);}
}

运行结果:

在这里插入图片描述

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

重命名 Bean

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

@Component
public class BeanConfig {@Bean(name = {"u1","user1"})public User user1(){return new User("zhangsan", 18);}@Beanpublic User user2(){return new User("lisi", 19);}
}

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

@SpringBootApplication
public class J20250624Application {public static void main(String[] args) {//获取Spring上下文对象,可以理解成拿到Spring容器ApplicationContext context = SpringApplication.run(J20250624Application.class, args);//根据bean名称,从Spring上下文中获取对象User u1 = (User) context.getBean("u1");User user1 = (User) context.getBean("user1");User user2 = (User) context.getBean("user2");//使用对象System.out.println(u1);System.out.println(user1);System.out.println(user2);}
}

在这里插入图片描述

name= 可以省略,如下代码所示:

@Component
public class BeanConfig {@Bean({"u1","user1"})public User user1(){return new User("zhangsan", 18);}@Beanpublic User user2(){return new User("lisi", 19);}
}

只有一个名称时, {}也可以省略, 如:

@Component
public class BeanConfig {@Bean("u1")public User user1(){return new User("zhangsan", 18);}@Beanpublic User user2(){return new User("lisi", 19);}
}

这样原来的user1对象就没有了

在这里插入图片描述

总结

Bean 的命名

  • 五大注解存储的 Bean

① 前两位字母均为大写,Bean 名称为类名
② 其他的为类名首字母小写
③ 重命名通过 value 属性设置 @Controller (value = "user")

  • @Bean 注解存储的 Bean

① Bean 名称为方法名
② 重命名通过 name 属性设置 @Bean (name = {"u1","user1"})


如果存在多个Bean, 根据类型去拿就会有问题,通过名称不会有问题(名称不会重复)

扫描路径

Q: 五大注解和@Bean声明的 bean,一定会生效吗?
A: 不一定(原因: bean 想要生效,还需要被 Spring 扫描)

下面我们通过修改项目工程的目录结构,来测试 Bean 对象是否生效:

记得把代码改回正确的再测试

原本的目录结构:

在这里插入图片描述

因为现在启动类测试的是@Bean创建的bean,把启动类移动到configuration包下还是能正确运行的,所以就把启动类放到controller包下进行测试

在这里插入图片描述

@SpringBootApplication 内部已默认包含 @ComponentScan(默认扫描当前主启动类所在包及其子包的组件),可以显式添加 @ComponentScan 重新配置扫描路径, 但默认的扫描逻辑会被覆盖

@ComponentScan("org.example.j20250624.configuration")
@SpringBootApplication
public class J20250624Application {public static void main(String[] args) {//获取Spring上下文对象,可以理解成拿到Spring容器ApplicationContext context = SpringApplication.run(J20250624Application.class, args);//根据bean名称,从Spring上下文中获取对象User u1 = (User) context.getBean("u1");User user1 = (User) context.getBean("user1");User user2 = (User) context.getBean("user2");//使用对象System.out.println(u1);System.out.println(user1);System.out.println(user2);}
}

这样就能扫描到configuration包了

在这里插入图片描述
加上{} 可以配置多个包路径

@ComponentScan({"org.example.j20250624.configuration", "org.example.j20250624.model"})

DI 详解

依赖注入是一个过程,是指 IoC 容器在创建 Bean 时,去提供运行时所依赖的资源,而资源指的就是对象。简单来说,就是把对象取出来放到某个类的属性中。

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

依赖注入是使用 @Autowired 实现的,Spring 给我们提供了三种方式:

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

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

属性注入

Service 类的实现代码如下:

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

Controller 类的实现代码如下:

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

调用 UserController 中的 sayHi 方法:

@SpringBootApplication
public class J20250624Application {public static void main(String[] args) {//获取Spring上下文对象,可以理解成拿到Spring容器ApplicationContext context = SpringApplication.run(J20250624Application.class, args);//从Spring上下文中获取对象UserController userController = (UserController) context.getBean("userController");//使用对象userController.sayHi();}
}

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

在这里插入图片描述

构造方法注入

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

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

注意事项:如果类只有一个构造方法,那么 @Autowired 可以省略,Spring 会自动使用该构造方法进行依赖注入;如果类中有多个构造方法,那么需要加上 @Autowired 来明确指定使用哪个构造方法

@Controller
public class UserController {private UserService userService;private String name;// 构造方法1:注入 UserService@Autowired //必须指定public UserController(UserService userService) {this.userService = userService;}// 构造方法2:注入 UserService 和 namepublic UserController(UserService userService, String name) {this.userService = userService;this.name = name;}public void sayHi(){System.out.println("hi,UserController2...");userService.sayHi();}
}

Setter 注入

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

//注入方法3: Setter方法注入@Controller
public class UserController {private UserService userService;@Autowiredpublic void setUserService(UserService userService) {this.userService = userService;}public void sayHi(){System.out.println("hi,UserController3...");userService.sayHi();}
}

三种注入优缺点分析

  1. 属性注入
  • 优点:代码简洁直观,通过 @Autowired 直接标记字段即可注入,开发效率高
  • 缺点:强依赖 IoC 容器。无法注入 final 修饰的字段(因为字段初始化依赖容器注入,而 final 要求构造时赋值)
  1. 构造函数注入
  • 优点:支持注入 final 修饰的字段(构造方法执行时初始化,符合 final 语义)。
    依赖在类创建阶段(构造方法执行)就完成注入,保证对象使用时依赖已完全初始化。
    通用性强:构造方法是 JDK 基础特性,不依赖框架,切换框架时无需修改注入逻辑。
    注入的对象状态稳定(依赖不可变),避免后续被意外修改。

  • 缺点:若类依赖多个对象,构造方法参数会增多,代码略显繁琐。

  1. Setter 注入
  • 优点:支持对象创建后动态修改依赖,灵活度高,适合处理 “可选依赖”(依赖可后续配置,不影响类初始化)
  • 缺点:无法注入 final 修饰的字段(依赖通过 Setter 方法赋值,晚于 final 字段初始化时机)。
    依赖可能被多次修改(Setter 方法可被外部调用),存在状态变更风险。

当同一类型存在多个Bean时,直接使用@Autowired会存在问题

@Component
public class BeanConfig {@Bean("u1")public User user1(){return new User("zhangsan", 18);}@Beanpublic User user2(){return new User("lisi", 19);}
}
@Controller
public class UserController {//注入user@Autowiredprivate User user;
}

在这里插入图片描述

只需要一个Bean,但找到两个

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

  1. @Primary
  2. @Qualifier
  3. 改成 Bean的名称
  4. @Resource

使用@Primary注解

@Primary 注解的作用是在存在多个相同类型的 Bean 时,指定某个 Bean 作为默认注入选择

@Component
public class BeanConfig {@Primary@Bean("u1")public User user1(){return new User("zhangsan", 18);}@Beanpublic User user2(){return new User("lisi", 19);}
}
@Controller
public class UserController {//注入user@Autowiredprivate User user;
}

使用@Qualifier注解

@Qualifier的value属性中,指定当前要注入的Bean的名称。@Qualifier注解不能单独使用,必须配合@Autowired使用

@Component
public class BeanConfig {@Bean("u1")public User user1(){return new User("zhangsan", 18);}@Beanpublic User user2(){return new User("lisi", 19);}
}
@Controller
public class UserController {//注入user@Qualifier("u1")@Autowiredprivate User user;
}

使用Bean的名称

@Component
class BeanConfig {@Beanpublic User user1(){return new User("zhangsan",18);}@Beanpublic User user2() {return new User("lisi",19);}
}
@Controller
public class UserController {@Autowiredprivate User user1;
}

使用@Resource注解

通过name属性指定要注入的Bean的名称

@Component
public class BeanConfig {@Bean("u1")public User user1(){return new User("zhangsan", 18);}@Beanpublic User user2(){return new User("lisi", 19);}
}
@Controller
public class UserController {//注入user@Resource(name = "u1")private User user;
}

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

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

相关文章

【Flutter】解决 flutter_inappwebview在 Windows 上使用导致应用闪退问题

问题背景 在 Windows 11 上运行 Flutter 桌面应用时,应用出现闪退现象。通过系统事件日志分析,发现是 MSVCP140.dll 模块的访问冲突异常(错误代码 c0000005)导致的崩溃。 问题分析 1. 错误现象 应用启动后立即闪退Windows 事件…

使用 JavaScript、Mastra 和 Elasticsearch 构建一个具备代理能力的 RAG 助手

作者:来自 Elastic JD Armada 了解如何在 JavaScript 生态系统中构建 AI 代理。 Elasticsearch 与业界领先的生成式 AI 工具和服务商有原生集成。查看我们的网络研讨会,了解如何超越 RAG 基础,或使用 Elastic 向量数据库构建可投入生产的应用…

Active Directory 环境下 Linux Samba 文件共享服务建设方案

Active Directory 环境下 Linux Samba 文件共享服务建设方案 目录 需求分析方案总体设计技术架构与选型详细部署规划共享文件性能测试非域终端共享配置运维与权限安全管理建议1. 需求分析 因某公司(编的)新增多个部门,各部门之间存在多类型终端系统,但又有同时访问文件库…

Python爬虫网安-项目-简单网站爬取

源码: https://github.com/Wist-fully/Attack/tree/pc pc_p1 目标: 1.进入列表页,顺着列表爬取每个电影详情页 2.利用正则来提取,海报,名称,类别,上映的时间,评分,剧…

Golang中的数组

Golang Array和以往认知的数组有很大不同。有点像Python中的列表 1. 数组:是同一种数据类型的固定长度的序列。 2. 数组定义:var a [len]int,比如:var a [5]int,数组长度必须是常量,且是类型的组成部分。一…

《Origin画百图》之矩阵散点图

矩阵散点图的作用 一、直观展示多变量间的两两关系 矩阵散点图的基本单元是两两变量的散点图,每个散点图对应矩阵中的一个单元格,可直接反映变量间的: 相关性方向:正相关(散点向右上倾斜)或负相关&#x…

Flask文件下载send_file中文文件名处理解决方案

Flask文件下载send_file中文文件名处理解决方案 Flask文件下载中文文件名处理解决方案问题背景问题分析核心问题常见症状 解决方案技术实现关键技术点 完整实现示例 Flask文件下载中文文件名处理解决方案 问题背景 在Web应用开发中,当用户下载包含中文字符的文件时…

新手指南:在 Ubuntu 上安装 PostgreSQL 并通过 VS Code 连接及操作

本文档记录了一个初学者在 Ubuntu 系统上安装、配置 PostgreSQL 数据库,并使用 Visual Studio Code (VS Code) 作为客户端进行连接和操作的全过程。其中包含了遇到的常见错误、分析和最终的解决方案,旨在为新手提供一个清晰、可复现的操作路径。 最终目…

二刷 苍穹外卖day10(含bug修改)

Spring Task Spring框架提供的任务调度工具,可以按照约定的时间自动执行某个代码逻辑 cron表达式 一个字符串,通过cron表达式可以定义任务触发的时间 **构成规则:**分为6或7个域,由空格分隔开,每个域代表一个含义 …

Android Native 之 inputflinger进程分析

Android IMS原理解析 - 简书 Android 输入事件分发全流程梳理(一)_android input事件分发流程-CSDN博客 Android 输入事件分发全流程梳理(二)_android输入事件流程图-CSDN博客 inputflinger模块与surfaceflinger模块在同级目录…

Python实例题:基于 Flask 的在线聊天系统

目录 Python实例题 题目 要求: 解题思路: 代码实现: Python实例题 题目 基于 Flask 的在线聊天系统 要求: 使用 Flask 框架构建一个实时在线聊天系统,支持以下功能: 用户注册、登录和个人资料管理…

v-bind指令

好的,我们来学习 v-bind 指令。这个指令是理解 Vue 数据驱动思想的基石。 核心功能:v-bind 的作用是将一个或多个 HTML 元素的 attribute (属性) 或一个组件的 prop (属性) 动态地绑定到 Vue 实例的数据上。 简单来说,它在你的数据和 HTML …

【设计模式04】单例模式

前言 整个系统中只会出现要给实例,比如Spring中的Bean基本都是单例的 UML类图 无 代码示例 package com.sw.learn.pattern.B_create.c_singleton;public class Main {public static void main(String[] args) {// double check locking 线程安全懒加载 ⭐️ //…

飞算科技依托 JavaAI 核心技术,打造企业级智能开发全场景方案

在数字经济蓬勃发展的当下,企业对智能化开发的需求愈发迫切。飞算数智科技(深圳)有限公司(简称 “飞算科技”)作为自主创新型数字科技公司与国家级高新技术企业,凭借深厚的技术积累与创新能力,以…

20250701【二叉树公共祖先】|Leetcodehot100之236【pass】今天计划

20250701 思路与错误记录1.二叉树的数据结构与初始化1.1数据结构1.2 初始化 2.解题 完整代码今天做了什么 题目 思路与错误记录 1.二叉树的数据结构与初始化 1.1数据结构 1.2 初始化 根据列表,顺序存储构建二叉树 def build_tree(nodes, index0):# idx是root开始…

Web应用开发 --- Tips

Web应用开发 --- Tips General后端需要做参数校验代码风格和Api设计风格的一致性大于正确性数据入库时间应由后端记录在对Api修改的时候,要注意兼容情况,避免breaking change 索引对于查询字段,注意加索引对于唯一的字段,考虑加唯…

CSS 安装使用教程

一、CSS 简介 CSS(Cascading Style Sheets,层叠样式表)是用于为 HTML 页面添加样式的语言。通过 CSS 可以控制网页元素的颜色、布局、字体、动画等,是前端开发的三大核心技术之一(HTML、CSS、JavaScript)。…

机器学习中为什么要用混合精度训练

目录 FP16与显存占用关系机器学习中一般使用混合精度训练:FP16计算 FP32存储关键变量。 FP16与显存占用关系 显存(Video RAM,简称 VRAM)是显卡(GPU)专用的内存。 FP32(单精度浮点)&…

[附源码+数据库+毕业论文+答辩PPT]基于Spring+MyBatis+MySQL+Maven+vue实现的中小型企业财务管理系统,推荐!

摘 要 现代经济快节奏发展以及不断完善升级的信息化技术,让传统数据信息的管理升级为软件存储,归纳,集中处理数据信息的管理方式。本中小型企业财务管理就是在这样的大环境下诞生,其可以帮助管理者在短时间内处理完毕庞大的数据信…

华为云Flexus+DeepSeek征文 | 对接华为云ModelArts Studio大模型:AI赋能投资理财分析与决策

引言:AI金融,开启智能投资新时代​​ 随着人工智能技术的飞速发展,金融投资行业正迎来前所未有的变革。​​华为云ModelArts Studio​​结合​​Flexus高性能计算​​与​​DeepSeek大模型​​,为投资者提供更精准、更高效的投资…