【Spring】注解读取和存储Bean对象(上)

 

已经实现了最基本的 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 存储 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 中的全部内容,如有不妥之处欢迎批评指正


 我年华虚度,空有一身疲倦。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值