【Spring】使用注解读取和存储Bean对象

 哈喽,哈喽,大家好~ 我是你们的老朋友:保护小周ღ  


谈起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() 方法根据参数来获取,搞来搞去感觉还不如我们传统的做法,只是多了一个(依赖反转)的思想。

【Spring】使用注解读取和存储Bean对象_第1张图片

所以接下来我们需要学习更简单的操作 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)的配置如下:

"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】使用注解读取和存储Bean对象_第2张图片

如果说添加注解的类或方法不在配置扫描的包底下,也是无法被存储到 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 配置文件设置一下扫描路径

【Spring】使用注解读取和存储Bean对象_第3张图片

2. 给 Student 类添加注解

【Spring】使用注解读取和存储Bean对象_第4张图片

非常的酷,就一个注解 + 设置一下扫描路径搞定啦~ 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());
    }
}

【Spring】使用注解读取和存储Bean对象_第5张图片


2.2.1 @Service (服务) 将Bean 对象存储到 Spring 中

【Spring】使用注解读取和存储Bean对象_第6张图片

其余的三个注解 @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框架中涉及的注解种类才这么多,以应对不同的业务场景和复杂的需求。

程序的工程分层,调用流程图:

【Spring】使用注解读取和存储Bean对象_第7张图片


2.3.1 类注解之间的关系

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

ctrl + 鼠标单击注解即可查看。

【Spring】使用注解读取和存储Bean对象_第8张图片

这些注解的内部都有一个 @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对象:

【Spring】使用注解读取和存储Bean对象_第9张图片


当类名首字母和第二个字母都是大写时,此时利用默认的首字母小写就不能正常地读取到 Bean :

【Spring】使用注解读取和存储Bean对象_第10张图片


关于这个获取 Bean 对象的默认ID ,我们就要了解Spring 关于 Bean 对象存储时生成的命名规则

双击 shift 键,打开全局搜索: 输入 AnnotationbeanName

【Spring】使用注解读取和存储Bean对象_第11张图片

【Spring】使用注解读取和存储Bean对象_第12张图片

 规则使用的是 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")

下面是一种简洁的写法:

【Spring】使用注解读取和存储Bean对象_第13张图片

拓展:如果项目中没有目录,所有的类都在 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 对象下文讲述……

【Spring】使用注解读取和存储Bean对象_第14张图片

【Spring】使用注解读取和存储Bean对象_第15张图片

注意:通过方法注解的方式存储 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 = "***")

【Spring】使用注解读取和存储Bean对象_第16张图片

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());
    }
}

【Spring】使用注解读取和存储Bean对象_第17张图片

 属性注入的核心:


3.2 构造方法注入

构造方法注入是在类的构造方法中实现注入:同样使用 @Autowired 注解

【Spring】使用注解读取和存储Bean对象_第18张图片

 如果只有一个构造方法,那么 @Autowired 注解可以省略不写:

【Spring】使用注解读取和存储Bean对象_第19张图片

如果类中出现多个构造方法,那么需要添加上 @Autowired 来明确指定到底使用哪个构造方法,否则程序会报错。


3.3 Setter 注入

Setter 本质上就是使用自定义方法来代替构造方法注入,也是需要在 set 方法上添加 @Autowired 注解:

【Spring】使用注解读取和存储Bean对象_第20张图片

注意:使用 Setter 注入一定要添加@Autowired 注解


3.4 三种注入优缺点分析

3.4.1 属性注入

【Spring】使用注解读取和存储Bean对象_第21张图片

优点:实现起来比较简单,只需要一个@Autowired 注解

缺点:

1. 不能注入不可变的(final)的对象

final 修饰的引用,只能在创建的时候进行初始化赋值操作,或在构造方法中初始化。

【Spring】使用注解读取和存储Bean对象_第22张图片

 2. 只能适用于 loC 容器

属性注入通常与IoC(控制反转)容器紧密相关,并且在大多数情况下,它更常用于IoC容器中。IoC容器通过管理对象的生命周期、依赖关系和对象创建等任务,将控制权交给框架,以实现解耦和可扩展性。总体而言,属性注入与IoC容器密切相关,并且在通过IoC容器管理对象时,属性注入是一种常用的依赖注入方式,能够帮助实现代码的解耦和可配置性。然而,在非IoC场景下,属性注入的使用可能会受到一些限制,并且可能不如其他依赖注入方式适用。

3. 更容易违背单一设计原则(针对类)

属性注入在某些情况下可能会违背单一设计原则(Single Responsibility Principle,SRP)。SRP的目标是确保一个类对于变化的原因只有一个,以便提高代码的可维护性和灵活性。它强调职责的划分和分离,使每个类专注于单一的功能或关注点

以下是属性注入可能导致违背SRP的一些情况:

  1. 依赖过多:属性注入可能导致一个类具有过多的依赖关系。如果一个类依赖过多的其他类或组件,那么它可能承担过多的职责。这样的类可能会变得庞大而难以理解和维护。

  2. 责任扩散:属性注入可能导致一个类承担不应该由它承担的额外职责。当一个类注入了许多属性时,特别是来自不同领域的属性,它可能参与多个责任和业务逻辑,从而违反SRP。

  3. 耦合度增加:属性注入可能导致类之间的耦合度增加。如果一个类对其依赖项的实例化和配置有太多控制权,它与这些依赖项之间的耦合度会增加。这使得类的扩展和修改变得更加困难。

虽然属性注入在一些情况下可能会违背SRP,但这并不意味着属性注入本身是错误的或应该完全避免的。合理使用属性注入,可以通过分割类的职责、解耦依赖关系和保持代码的可维护性来避免违背SRP。在设计和实现过程中,应该谨慎选择注入的属性和依赖项,确保它们与类的职责和关注点保持一致,避免过度复杂化和耦合。


3.4.2 构造方法注入

【Spring】使用注解读取和存储Bean对象_第23张图片

优点:

1. 可以注入一个不可变(final修饰)对象,构造方法可以为其赋值。

【Spring】使用注解读取和存储Bean对象_第24张图片

 2. 注入的对象不会被修改

  • 可以被 final 的修饰(只有一个实例)
  • 构造方法伴随着类加载只执行一次

3. 通用性更好

通过构造方法注入,类的依赖关系可以明确地表达出来,构造方法注入允许依赖关系在类的实例化阶段就被传递进来。这意味着在类的实例化过程中,可以清楚地看到类所依赖的对象是哪些,而不是在方法调用时才进行依赖注入。

缺点:

没有属性注入实现简单。

需要注意的是,构造方法注入并不适用于所有情况,特别是当所依赖的对象较多时,会出现构造方法参数列表冗长的问题。在这种情况下,可以考虑使用其他形式的依赖注入方式,如属性注入或方法注入,来提供更灵活的注入方式。选择合适的依赖注入方式,取决于具体的情况和项目需求。

构造方法注入是 Spring 官方推荐的注入方法。


3.4.3 Setter 方式注入(普通方法注入)

【Spring】使用注解读取和存储Bean对象_第25张图片

Setter 方法是 Spring 早期版本推荐的注入方式,。

优点:比较符合单一设计原则(SRP),方法嘛,针对的是方法级别的,虽然在通用性上没有构造方法强,Setter方法注入也有其适用的场景,比如可选的依赖项或动态变化的依赖关系。在这些情况下,setter方法注入可能更为灵活和方便。选择合适的依赖注入方式,需要根据具体需求和设计原则进行权衡和判断。

缺点:

1. 不能注入不可变(final)的对象;

final 修饰的引用,只能在创建的时候进行初始化赋值操作,或在构造方法中初始化。

2. 注入对象可以被修改

set 方法是普通的方法,可以被重复调用,在调用时就存在被修改的风险。Spring 现版本已经推荐使用构造方法注入的方式来进行类注入。


3.5 @Resource 注入

在 Java 里面在进行类注入时,除了使用 @Autowired 注解,还可以使用 @Resource 注解:
【Spring】使用注解读取和存储Bean对象_第26张图片

 @Autowired 和 @Resource 都是在 Java 中用于进行依赖注入的注解,但它们有一些不同之处。


3.5.1 @Autowired 和 @Resource 的区别

  1. 出生不同:@Autowired 注解出自于Spring框架(非官方),  @Resource 注解出自于 JDK(官方)。
  2. 功能支持不同:@Autowired 可用于属性注入,构造方法注入,和 Setter(普通方法) 注入.而 @Resoure 只能用于 Setter 注入和属性注入, 不能用于构造方法注入(意味着不能注入不可变对象).
  3. 参数支持不同:  @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;
    }
}

以上程序执行结果:

【Spring】使用注解读取和存储Bean对象_第27张图片

同一类型多个 Bean 报错处理: 

  • 使用Resoure(name = "FunctionName") 进行类注入的时候只能指定获取@Bean注解修饰方法的名字,如此一来,问题迎刃而解~
  • 使用@Qualifier 注解定义名称,可以跟@Autowired 注解配合使用(Autowired 注解不可以指定name 注入)。

1. 使用 @Resource(name = "XXXXX") 定义

【Spring】使用注解读取和存储Bean对象_第28张图片

2. 使用 @Qualifier 

@Qualifier注解 用于帮助指定特定的依赖注入或自动装配。它通常与依赖注入框架(如Spring)一起使用,以解决多个候选对象的歧义性。在依赖注入中,当存在多个相同类型的bean时,可以使用@Qualifier注解来标识要使用的具体bean。通过在@Qualifier注解中指定一个唯一的标识符(name),可以告诉依赖注入框架,注入哪个具体的bean。

【Spring】使用注解读取和存储Bean对象_第29张图片


 四、总结

1. 将对象存储到 Spring 中:

  • 古老一点的做法是在 Spring 配置文件的 标签下直接添加注解。

不好的一点是比较麻烦,每个 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 的作用域和生命周期

感谢每一位观看本篇文章的朋友,更多精彩敬请期待:保护小周ღ *★,°*:.☆( ̄▽ ̄)/$:*.°★* 

遇见你,所有的星星都落在我的头上……

你可能感兴趣的:(JavaEE,spring,java,注解)