哈喽,哈喽,大家好~ 我是你们的老朋友:保护小周ღ
谈起Java 圈子里的框架,最年长最耀眼的莫过于 Spring 框架啦,本期给大家带来的是: 将对象存储到 Spring 中、Bean 对象的命名规则、从Spring 中获取bean 对象、注入的关键字、@Autowired 与 @Resource 的区别、解决@Bean 方法注解注入多个同类型 Bean 对象的报错。本文将为大家讲解,一起来看看叭~
本期收录于博主的专栏:JavaEE_保护小周ღ的博客-CSDN博客
适用于编程初学者,感兴趣的朋友们可以订阅,查看其它 “JavaEE基础知识”。
更多精彩敬请期待:保护小周ღ *★,°*:.☆( ̄▽ ̄)/$:*.°★* ‘
一、上期回顾
我们已经实现了最基本的 Spring 的读取和存储 Bean 对象的操作了,使用 Spring 配置文件(.xml)对Bean 对象进行存储,使用 ApplicationContext 或 BeanFactory 接口获取Spring 上下文对象,上下文对象就管理着 Spring 中的 Bean 对象,然后调用 getBean() 方法获取指定的Bean 对象 ,该方法是一个重载方法,获取Bean 对象主要有三种方式:
1. 根据 bean 对象的 id (标志)来获取
2. 根据对象类型来获取 Bean
3 .根据 bean 对象的 id (标志)+ 类型 获取 bean
前两种重载获取指定Bean对象的方式各有缺陷,我们推荐使用第三种。
经过前面的学习,我们虽然实现了 Spring的基本获取和存储,但是在操作的过程中比较繁琐。
存储 Bean 对象需要我们手动的往 Spring 配置文件中添加配置。
取出 Bean 对象,首先需要我们获取上下文对象,其次需要调用 getBean() 方法根据参数来获取,搞来搞去感觉还不如我们传统的做法,只是多了一个(依赖反转)的思想。
所以接下来我们需要学习更简单的操作 Bean 对象的方法——使用注解。
使用Spring 中的相关注解,来存储和读取 Bean 对象。
详细内容:【Spring 】项目创建和使用_保护小周ღ的博客-CSDN博客
二、Spring 存储 Bean 对象
之前我们存储 bean 对象需要在Spring 配置文件(.xml)添加一行 bean 标签才行,如上图所示。
现在我们需要学习的是如何通过注解来代替之前添加 Bean 对象要手动写一行 bean 配置的尴尬。
注解(Annotation)是一种Java语言中的元数据(metadata),提供了一种代码级别的标记机制,用来表示程序中的一些结构化信息以及对这些结构进行解释说明。注解可以用于类、字段、方法、参数等各种Java程序元素上,并可以通过反射机制来获取注解信息。
简单理解 :
注解就像是一些标签,我们可以将它们添加在程序的代码中,用来给代码添加一些额外的信息和说明。这些信息可以帮助程序自动化地完成某些操作,比如代码生成、配置管理、文档生成等等。
举个例子,比如我们想要在类中添加一些配置信息,通常我们需要在代码中手写一些配置代码,然后在程序中进行解析和加载。但是,如果我们使用了注解,我们就可以通过在类上添加一些特定的注解,来让程序自动化地完成配置的加载和解析工作。这样可以节省很多时间和代码量。
总之,注解就是一种用来给程序添加额外信息的工具,可以帮助我们更加方便地管理和自动化完成程序的各种操作。
2.1 配置扫描路径
之前我们是在 Spring 配置文件(.xml)中手动添加 Bean 对象,如果使用注解的方法存储我们就只需要配置一下 bean 对象(还未实例)所在的包路径,当 Spring 启动时,上下文对象会根据Spring 配置文件配置的路径进行扫描,只有路径(包)添加了注解的 类、方法才能被正确的识别并实例到 Spring 中。
Spring 配置文件(.xml)的配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:content="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<content:component-scan base-package="animal"></content:component-scan>
</beans>
如果说添加注解的类或方法不在配置扫描的包底下,也是无法被存储到 Spring 中的。
2.2 添加注解存储 Bean 对象
想要将 Bean 对象更简单的存储到 Spring 中,使用注解有两种注解类型:
1. 类注解:顾名思义就是用来修饰类的注解,表示需要将被修饰的类实例并存储到 Spring 容器中。五大类注解:下文详细介绍这些注解的含义
@Controller【控制器】、@Service【服务】、@Repository【仓库】、@Component【组件】、
@Configuratio【配置】。
2. 方法注解:一个类的中的方法可以实例另一个类嘛,此时将该方法的返回值设置为实例后的类对象再使用 @Bean 方法注解同样可以将该对象(bean对象)存储到 Spring 容器中。
在Spring 中五大类注解都可以将 Bean 对象存储到 Spring 中,没有太多的说法,但是在后面的框架的学习当中,每个注解分层修饰类,他们的含义就大有讲究,此时,我们主要学习如何使用注解存取 Bean 对象,其他知识点后期再给大家展开。
2.2.1 @Controller (控制器) 将Bean 对象存储到 Spring 中
先定义一个学生类:
public class Student {
// 属性
private String id;
private String name;
private String sex;
private int age;
// 行为
public void homework() {
System.out.println(name + "在做作业~");
}
@Override
public String toString() {
return "student{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
", sex='" + sex + '\'' +
", age=" + age +
'}';
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
给 Student 类添加 @Controller 注解将 Student 对象(运行时自动)存储到 Spring 中
1. Spring 配置文件设置一下扫描路径
2. 给 Student 类添加注解
非常的酷,就一个注解 + 设置一下扫描路径搞定啦~ Spring 启动的时候就会自动的将 Student 类实例并添加到 Spring 中。
此时我们可以使用之前获取对象的方法来尝试获取从 Spring 中获取 Student 类的对象。
ApplicationContext 接口获取上下文对象,再调用 getBean() 方法来获取指定的 Bean 对象。
public class App {
public static void main(String[] args) {
//1. 得到 Spring 的上下文对象
ApplicationContext context =
new ClassPathXmlApplicationContext("spring-test.xml");
//2. 调用 getBean() 方法获取指定的 Bean 对象
Student student = context.getBean("student",Student.class);
//3. 使用 Bean 对象
student.setId("1");
student.setName("张三");
student.setSex("男");
student.setAge(20);
student.homework();
System.out.println(student.toString());
}
}
2.2.1 @Service (服务) 将Bean 对象存储到 Spring 中
其余的三个注解 @Repository【仓库】、@Component【组件】、@Configuratio【配置】。 在 Spring 中并没有太大的含义,都是存储 Bean 对象,这里也不给大家演示了结果都一样。感兴趣的老铁可以自己演示一下。
2.3 为什么需要这么多的类注解?
由上述事例所示,五大类注解的功能都可以将 Bean 对象存储到 Spring 当中,功能都一样的情况下,为啥还需要这么多的类注解呢???
每个注解的具体应用场景及其含义:
1. @Controller注解:【控制器】验证前端传输的参数【安全检验】
Controller层是MVC(Model-View-Controller)设计模式中的一部分,负责处理用户请求和控制应用程序的流程。它是应用程序中的一个组件,用于接收来自用户界面(View)的请求,协调其他组件(如Service层和Repository层)的工作,并返回相应的结果给用户界面。
2. @Service 注解:【服务层】
主要用于处理业务逻辑和执行具体的业务操作。它位于应用程序的中间层,位于控制层和数据访问层之间,用于实现业务逻辑的封装和处理。
3. @Repository注解:【仓库(数据仓库)】可以理解为直接操作数据库
Repository层是软件开发中的一种设计模式,用于处理数据的存取和持久化。在Java应用程序中,Repository层通常是数据访问层的一部分,与数据库或其他数据存储介质进行交互。
4. @Component 注解:【组件】通用化的工具类
Component层是Java应用程序中的一个组件化层,主要用于组件的管理和组装。它负责将各个功能模块或组件进行组合,提供应用程序的整体功能。Component层通常位于应用程序的顶层,作为应用程序的入口点。
5. @Configuration注解:【配置】此处有项目的所有配置
@Configuration注解被Spring Framework用于传统的XML配置之外,为了更好的支持Java配置,它表示一个配置类,被加上此注解后可以当做Bean定义来使用。通过@Configuration注解,可以很方便地配置Spring容器中的Bean组件。
举个例子:
我们每一个人国人都有一个唯一的18 位的身份证号,身份证号包含了:地区号码,生日、顺序码和校验码。同一个省份 / 市 的群众身份证号的前几位都是一样的,例如:湖北省新生代的身份证号码都是 “42XXXX” 开头的。其中,XXXX 是具体的地区码,用来表示湖北省内不同的行政区划。是我们可以用一个String 类型 region 变量来描述地区码,从而直观的看出这个群众是来自那个地区,用 birthday 变量可以用来描述一个出生日期……把一个完整的身份证号拆分开来,就可以更加直观的表示这个人的基本信息,一看 region 变量描述的是地区,42 开头这个老铁是湖北的,,其中类注解就是这样的道理,不同 “层次” 的类我用不同的注解描述,是不是就很直观,例如:@Repository注解 按照标准,用来修饰操作数据库的类,就很方便维护。
综上所述,虽然这五大类注解的功能都可以将 Bean 对象存储到 Spring 当中,但它们的使用场景和语义略有差异,这样可以更好地满足不同的需求和场景。也正因如此,Spring框架中涉及的注解种类才这么多,以应对不同的业务场景和复杂的需求。
程序的工程分层,调用流程图:
2.3.1 类注解之间的关系
查看 @Controller / @Service / @Repository / @Configuration 这些注解的源码发现:
ctrl + 鼠标单击注解即可查看。
这些注解的内部都有一个 @Component注解, 说明这些注解可以认为是 @Component 的子类.
2.4 Bean 对象的命名规则
上面我们通过使用注解的方式存储 Bean 对象,就是在 Spring 配置文章中设置Bean 对象所在的路径(包),当Spring 容器启动时,会根据配置文件中的路径扫描,将有注解的类实例化到容器中。我们如果使用之前的老方法获取 Bean 对象,需要先获取 Spring 上下文对象(管理Bean),有两种方式【ApplicationContext 接口】(Spring 3.x 版本后官方推荐),【BeanFactory 接口】
得到上下文对象后,我们需要调用 getBean() 方法获取具体的 Bean 对象,因为 getBean() 方法有种重载,所以主要有三种获取方法:
1. 根据 Bean 对象ID ,我们手动取得名字(缺点,:返回值是 Object 所以需要进行强制类型转换 )2. 跟根据 Bean 对象得类型 (缺点:有多个同类型得 Bean 存储在容器中此时会抛出异常)
3. 根据 Bean 对象的 ID 和 Bean 对象的类型 (推荐)
此时我们通过类注解的方式添加 Bean对象,那获取 Bean对象的方法又该做出什么样的调整呢?
通常情况下 Bean (类)使用的就是标准的大驼峰命名,而读取的时候首字母小写就可以获取 Bean对象:
当类名首字母和第二个字母都是大写时,此时利用默认的首字母小写就不能正常地读取到 Bean :
关于这个获取 Bean 对象的默认ID ,我们就要了解Spring 关于 Bean 对象存储时生成的命名规则
双击 shift 键,打开全局搜索: 输入 AnnotationbeanName
规则使用的是 JDK introspector 中的 decapitallze 方法,源码如下:
public static String decapitalize(String name) {
if (name == null || name.length() == 0) {
return name;
}
// 1. 如果第一个字母和第二个字母都为大写的情况,不做任何处理直接返回,也是就是id 是类名
if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) &&
Character.isUpperCase(name.charAt(0))){
return name;
}
// 2. 否则就将首字母小写
char chars[] = name.toCharArray();
chars[0] = Character.toLowerCase(chars[0]);
return new String(chars);
}
总结:
当使用五大类注解注册 bean对象时,默认情况下获取(getBean() 方法) bean 对象,只需要将类名的首字母小写,作为 getBean() 方法的参数即可;当 类的首字母和第二个首字母都是大写的时候,此时我们需要将原类名作为 getBean() 方法的参数才能获取到 bean 对象。
我们也可以在类注解上针对 bean 对象手动进行命名操作:
【类注解】(value = "name")
下面是一种简洁的写法:
拓展:如果项目中没有目录,所有的类都在 java 的根目录下怎么存,取 bean 对象???
<content:component-scan base-package="**"></content:component-scan>
我们可以在 Spring 配置文件中将文件路径替换成通配符 “**” ,使用通配符的意思就是 Spring 启动后会从根目录开始扫描查找那些被类注解修饰的类,然后将这些类实例到容器中,然而这样的结果就会导致执行的速度变慢慢,效率非常低,所以我们建议是给项目添加合适的路径。
2.5 方法注解 @Bean
类注解是添加到某个类上面的,顾名思义,方法注解就是用来修饰方法的,使用方法注解(@Bean)的前提条件是需要配合类注解才能将对象正常的存储到 Spring 容器中,且该方法的返回值需要与预期存储 Bean 对象的类型一样:
@Component
public class StudentDemo {
@Bean
public Student getStu() {
//1. 创建对象
Student student = new Student();
//2. 初始化对象
student.setId("1");
student.setName("李小四");
student.setSex("男");
student.setAge(18);
//3. 返回对象
return student;
}
}
以上代码的功能是 Spring 启动后扫描到了 @Component 注解修饰的 StudengDemo 类,自然而然需要将其实例化到 Spring 容器中,在实例化的过程中,发现一个 @Bean 注解,意思就是,如果你想实例 StudengDemo 类,那你得先将 @Bean 修饰得 getStu() 方法返回的 Student 对象(Bean 对象)注册的到 Spring 中。执行结果如下:
此时我们可以稍作修改,给 StudentDemo 类 和 Student 类各自提供一个构造方法,由此可以判断谁那个类先加载? 注意:我们要先将 Student 类上面的类注解去掉,不然就会存储两个 bean 对象到 Spring 中,关于存储多个同类型的 bean 对象下文讲述……
注意:通过方法注解的方式存储 Bean 对象默认情况下是没有默认 ID 的,所以我们通过类型获取,可以手动的给 Bean 对象设置 ID (name)。
结论:方法注解 @Bean 也可以将对象注册到 Spring 中,前提是需要配合类注解的使用。
上述代码中,@Bean 方法注解注册的 Student bean 对象需要依赖于 StudentDemo 类的实例,当我们启动 Spring , 首先会加载StudentDemo 类,执行构造方法, 在加载的过程中发现一个需要执行 getStu() 方法(设置属性),所以呢优先去实例 Student ,再实例 StudentDemo ,这一块涉及到 JVM 加载类的五个阶段—— Bean 对象的生命周期,下篇博客再给大家讲述~~
A 类依赖于 B类,所以首先将 B类实例到 Spring 中,现在还没涉及到注解动态的将 Bean对象取出,所以这个例子不大恰当。后面再讲述~~~
2.5.1 bean 对象重命名
@Bean 注解可以通过设置name 属性给 bean 对象进行命名操作:
@Bean(name = "***")
name 其实是一个数组, 所以一个 bean 对象可以有多个名称存在:
@Bean(name = {"student", "李小四"})
name = {} 也是可以省略的:
@Bean({"student", "李小四"})
注意:当我们给 Bean 取了名字之后,默认的使用的名称就无法使用了!!!
以上便是 使用注解的方式将 bean 对象存储到 Spring 中的全部内容,如有不妥之处欢迎批评指正
三、Spring 获取 bean 对象
获取 bean 对象也叫 “对象装配”,“对象注入”, 意思就是把对象取出来放到某个类中。
对象装配(对象注入)的实现有以下三种方法:
1. 属性注入 2. 构造方法注入 3. Setter 注入
接下来博主将围绕着3种方式展开来给大家讲述Spring 如何实现动态注入 bean 对象(运行时动态的将 bean 对象注入到某个类中)。
3.1 属性注入
属性注入需要使用到 @Autowired 注解实现。接下来给大家举一个例子:
有一个学生类(学生信息的组织),假设对应数据库中的学生表,有一个Service 层的 StuService类,这类可以从数据库中读取学生数据(增删查改),实例成一个完整的学生对象,有一个 StuController 类,他可以针对学生数据进行实际的使用(调用处理),所以 StuController 类在运行中需要获取到 StuService 类的实例对象来操作数据库中的学生表。当然这里不会给大家真的用数据库,写伪代码来模拟一下真实的环境。
@Service
public class StuService {
/**
* 根据学生id从数据库中获取学生信息
* @param id
* @return
*/
public Student getStu(String id) {
//伪代码,链接数据库,查找学生信息
Student stu = new Student();
stu.setId(id);
stu.setName("张三");
stu.setAge(18);
stu.setSex("男");
return stu;
}
}
@Controller
public class StuController {
//1. 属性注入,从Spring 中取出 StuService 类型的bean 对象并注入到类引用中
@Autowired
private StuService stuService;
//2. 调用注入的 bean 对象执行查询操作
public Student getStu(String id) {
return stuService.getStu(id);
}
}
获取 Controller 中的 getStu 的方法:正常情况下我们就将 Student 学生的信息转换为 json 格式由前端发请求,服务器做出响应,将数据(http协议)给到前端,前端利用数据构建页面。
package school.student2;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import school.student.Student;
public class App {
public static void main(String[] args) {
//1. 得到 Spring 的上下文对象
ApplicationContext context =
new ClassPathXmlApplicationContext("spring-test.xml");
//2. 调用 getBean() 方法获取指定的 Bean 对象
StuController stuController = context.getBean("stuController", StuController.class);
//3. 使用bean 对象
Student student = stuController.getStu("1");
System.out.println(student.toString());
}
}
属性注入的核心:
3.2 构造方法注入
构造方法注入是在类的构造方法中实现注入:同样使用 @Autowired 注解
如果只有一个构造方法,那么 @Autowired 注解可以省略不写:
如果类中出现多个构造方法,那么需要添加上 @Autowired 来明确指定到底使用哪个构造方法,否则程序会报错。
3.3 Setter 注入
Setter 本质上就是使用自定义方法来代替构造方法注入,也是需要在 set 方法上添加 @Autowired 注解:
注意:使用 Setter 注入一定要添加@Autowired 注解
3.4 三种注入优缺点分析
3.4.1 属性注入
优点:实现起来比较简单,只需要一个@Autowired 注解
缺点:
1. 不能注入不可变的(final)的对象
final 修饰的引用,只能在创建的时候进行初始化赋值操作,或在构造方法中初始化。
2. 只能适用于 loC 容器
属性注入通常与IoC(控制反转)容器紧密相关,并且在大多数情况下,它更常用于IoC容器中。IoC容器通过管理对象的生命周期、依赖关系和对象创建等任务,将控制权交给框架,以实现解耦和可扩展性。总体而言,属性注入与IoC容器密切相关,并且在通过IoC容器管理对象时,属性注入是一种常用的依赖注入方式,能够帮助实现代码的解耦和可配置性。然而,在非IoC场景下,属性注入的使用可能会受到一些限制,并且可能不如其他依赖注入方式适用。
3. 更容易违背单一设计原则(针对类)
属性注入在某些情况下可能会违背单一设计原则(Single Responsibility Principle,SRP)。SRP的目标是确保一个类对于变化的原因只有一个,以便提高代码的可维护性和灵活性。它强调职责的划分和分离,使每个类专注于单一的功能或关注点。
以下是属性注入可能导致违背SRP的一些情况:
依赖过多:属性注入可能导致一个类具有过多的依赖关系。如果一个类依赖过多的其他类或组件,那么它可能承担过多的职责。这样的类可能会变得庞大而难以理解和维护。
责任扩散:属性注入可能导致一个类承担不应该由它承担的额外职责。当一个类注入了许多属性时,特别是来自不同领域的属性,它可能参与多个责任和业务逻辑,从而违反SRP。
耦合度增加:属性注入可能导致类之间的耦合度增加。如果一个类对其依赖项的实例化和配置有太多控制权,它与这些依赖项之间的耦合度会增加。这使得类的扩展和修改变得更加困难。
虽然属性注入在一些情况下可能会违背SRP,但这并不意味着属性注入本身是错误的或应该完全避免的。合理使用属性注入,可以通过分割类的职责、解耦依赖关系和保持代码的可维护性来避免违背SRP。在设计和实现过程中,应该谨慎选择注入的属性和依赖项,确保它们与类的职责和关注点保持一致,避免过度复杂化和耦合。
3.4.2 构造方法注入
优点:
1. 可以注入一个不可变(final修饰)对象,构造方法可以为其赋值。
2. 注入的对象不会被修改
- 可以被 final 的修饰(只有一个实例)
- 构造方法伴随着类加载只执行一次
3. 通用性更好
通过构造方法注入,类的依赖关系可以明确地表达出来,构造方法注入允许依赖关系在类的实例化阶段就被传递进来。这意味着在类的实例化过程中,可以清楚地看到类所依赖的对象是哪些,而不是在方法调用时才进行依赖注入。
缺点:
没有属性注入实现简单。
需要注意的是,构造方法注入并不适用于所有情况,特别是当所依赖的对象较多时,会出现构造方法参数列表冗长的问题。在这种情况下,可以考虑使用其他形式的依赖注入方式,如属性注入或方法注入,来提供更灵活的注入方式。选择合适的依赖注入方式,取决于具体的情况和项目需求。
构造方法注入是 Spring 官方推荐的注入方法。
3.4.3 Setter 方式注入(普通方法注入)
Setter 方法是 Spring 早期版本推荐的注入方式,。
优点:比较符合单一设计原则(SRP),方法嘛,针对的是方法级别的,虽然在通用性上没有构造方法强,Setter方法注入也有其适用的场景,比如可选的依赖项或动态变化的依赖关系。在这些情况下,setter方法注入可能更为灵活和方便。选择合适的依赖注入方式,需要根据具体需求和设计原则进行权衡和判断。
缺点:
1. 不能注入不可变(final)的对象;
final 修饰的引用,只能在创建的时候进行初始化赋值操作,或在构造方法中初始化。
2. 注入对象可以被修改
set 方法是普通的方法,可以被重复调用,在调用时就存在被修改的风险。Spring 现版本已经推荐使用构造方法注入的方式来进行类注入。
3.5 @Resource 注入
在 Java 里面在进行类注入时,除了使用 @Autowired 注解,还可以使用 @Resource 注解:
@Autowired
和 @Resource
都是在 Java 中用于进行依赖注入的注解,但它们有一些不同之处。
3.5.1 @Autowired 和 @Resource 的区别
- 出生不同:@Autowired 注解出自于Spring框架(非官方), @Resource 注解出自于 JDK(官方)。
- 功能支持不同:@Autowired 可用于属性注入,构造方法注入,和 Setter(普通方法) 注入.而 @Resoure 只能用于 Setter 注入和属性注入, 不能用于构造方法注入(意味着不能注入不可变对象).
- 参数支持不同: @Resource 支持更多的参数设置, 例如: 对Bean 取一个名字(name), 而 @Autowired 只能设置
required 参数。required
是@Autowired
注解的一个可选属性,默认值为true
。它用于指定依赖注入是否是必需的。当required
属性设置为true
时,如果找不到匹配的依赖对象,则会抛出NoSuchBeanDefinitionException
异常。示例:
@Autowired(required = true) private MyDependency myDependency;
当
required
属性设置为false
时,如果找不到匹配的依赖对象,则会将依赖对象设置为null
,即允许依赖注入的可选性。示例:
@Autowired(required = false) private MyDependency myDependency;
尽管
required
默认值为true
,但是在某些情况下,我们可能使用@Autowired(required = false)
来标记可选的依赖注入,以便在没有匹配的依赖对象时不抛出异常。这在需要可选依赖或动态注入的场景下非常有用。
3.6 @Bean 注入多个同类型的 bean 对象报错处理
当我们使用 @Bean 方法注解往 Spring 中注入多个同类型的 Bean 对象时,就会报错。
注意:以往的例子我们需要在 Spring 配置文件中将路径注释一下,不然启动的时候Spring 容器中会注册多个同类型的 bean对象(stu)。
@Component
public class StudentDemo {
StudentDemo() {
System.out.println("StudentDemo 执行");
}
@Bean
public Student getStu() {
//1. 创建对象
Student student = new Student();
//2. 初始化对象
student.setId("1");
student.setName("李小四");
student.setSex("男");
student.setAge(18);
//3. 返回对象
return student;
}
@Bean
public Student getStu2() {
//1. 创建对象
Student student = new Student();
//2. 初始化对象
student.setId("2");
student.setName("王小五");
student.setSex("男");
student.setAge(20);
//3. 返回对象
return student;
}
}
在另一个类中获取 Student 对象:
public class StudentApp {
public static void main(String[] args) {
//1. 得到 Spring 的上下文对象
ApplicationContext context =
new ClassPathXmlApplicationContext("spring-test.xml");
//2. 调用 getBean() 方法获取指定的 Bean 对象
StudentController stuController = context.getBean(StudentController.class);
//3. 利用 stuController 对象的 getStudent()获取 bean 对象
Student student = stuController.getStudent();
System.out.println(student.toString());
}
}
@Controller
class StudentController {
//1. 注入 student 对象,在StudentDemo 类中我们使用 @Bean 注入了两个stu 对象
@Autowired
private Student student;
//2. 返回
public Student getStudent() {
return student;
}
}
以上程序执行结果:
同一类型多个 Bean 报错处理:
- 使用Resoure(name = "FunctionName") 进行类注入的时候只能指定获取@Bean注解修饰方法的名字,如此一来,问题迎刃而解~
- 使用@Qualifier 注解定义名称,可以跟@Autowired 注解配合使用(Autowired 注解不可以指定name 注入)。
1. 使用 @Resource(name = "XXXXX") 定义
2. 使用 @Qualifier
@Qualifier注解 用于帮助指定特定的依赖注入或自动装配。它通常与依赖注入框架(如Spring)一起使用,以解决多个候选对象的歧义性。在依赖注入中,当存在多个相同类型的bean时,可以使用@Qualifier注解来标识要使用的具体bean。通过在@Qualifier注解中指定一个唯一的标识符(name),可以告诉依赖注入框架,注入哪个具体的bean。
四、总结
1. 将对象存储到 Spring 中:
- 古老一点的做法是在 Spring 配置文件的 <Beans> </Beans> 标签下直接添加注解。
不好的一点是比较麻烦,每个 Bean 对象都需要我们手动的添加,对此我们有了注解的方式。
- 使用类注解:@Controller 、@Service、@Repository、@Configuration、@Component这些其他注解的内部都有一个 @Component注解, 说明这些注解可以认为是 @Component 的子类.
使用类注解:需要在Spring 配置文件中设置包扫描路径,当Spring 启动时,会根据包路径下的类注解将修饰的类实例到 Spring 当中,类直接需要配合扫描路径使用,不然无法成功注入(此处是单例模式,一个类只有一份实例)。
- 使用方法注解 @Bean 【注意事项:必须配合类注解一起使用】
2. Bean 对象的命名规则
类首字母大写和类首字母小写及第二个字母小写,都是默认都是通过首字母小写作为name 来获取 Bean ;如果类首字母和第二个字母都是大写,那么直接使用原类名作为name 来获取 Bean 。
3. 从Spring 中获取bean 对象:
- 属性注入
- 构造方法注入(官方推荐)
- Setter 注入
4. 注入的关键字:
@Autowired 和 @Resource
5. @Autowired 与 @Resource 的区别:
- 出生不同:Autowired 出自Spring ,Resource 出自 JDK
- 参数不同:@Resource 支持更多的参数,例如可以在获取的时候给 Bean 对象取名字
- 功能的不同:@Autowired 支持属性注入,构造方法注入和 Setter 方法注入,@Resource 只支持属性注入和 Setter 方法注入。
6. 解决@Bean 方法注解注入多个同类型 Bean 对象的报错:
- 使用 @Resource(name= " 方法名")
- 使用 @Qualifier (value = "方法名")
好了,到这里,【Spring】使用注解读取和存储Bean对象 博主已经分享完了,希望对大家有所帮助,如有不妥之处欢迎批评指正。
下期预告:Bean 的作用域和生命周期
感谢每一位观看本篇文章的朋友,更多精彩敬请期待:保护小周ღ *★,°*:.☆( ̄▽ ̄)/$:*.°★*
遇见你,所有的星星都落在我的头上……