javaEE进阶 - Spring 更简单的读取和存储对象 - 细节狂魔

x33g5p2x  于2022-07-20 转载在 Java  
字(7.5k)|赞(0)|评价(0)|浏览(471)

前言

经过前⾯的Spring 创建 和 使⽤的博文学习,我们已经可以实现基本的 Spring 读取和存储对象的操作了。
但在操作的过程中我们发现读取和存储对象并没有想象中的那么“简单”,所以接下来我们要学习更加简单的操作 Bean 对象的⽅法。

在 Spring 中想要更简单的存储和读取对象的核⼼是使⽤注解,也就是我们接下来要学习 Spring 中的相关注解,来存储和读取 Bean 对象。
需要注意的是:
Spring 中的 注解是通用的。
即:在 Spring Boot 和 Spring MVC 中,使用 Spring 的注解。
它们 是能够完全识别并使用的。

也就是说:接下来,才是重点。
这些注解,将会是我们以后在工作中,在Spring系列的项目中,最常使用的知识!!!
这些注解的功能,必须掌握!!!

1、存储 Bean 对象

之前我们存储 Bean 时,需要在 spring-config 中添加⼀⾏ bean 注册内容才⾏,如下图所示:

这种存入 Bean 的方式,并不是很好!
1、需要手动添加 bean 对象 到 配置文件中
2、如果 是配置文件中出现了问题,不好调试。
这么说吧:如果 配置文件 出现了 问题,它是不会影响到程序的运行的。
它不会像 抛异常 一样,来 “显式” 的 打印 错误信息栈,描述具体的错误位置。
配置文件出现问题,它是“默不做声”的!【没有错误提示的】
除非你经验老道,否则你很难会想到是 它 出了问题。

⽽现在我们只需要⼀个注解就可以替代之前要写⼀⾏配置的尴尬了,不过在开始存储对象之前,我们先要来点准备⼯作。

简单来说:
  就是通过利用 注解,来简化  存入 / 取出  Bean(对象)的步骤。

1.1、前置工作:配置扫描路径(重要)

如果不做这一步,后面所有的操作,都是无效的。
注意:
想要将对象成功的存储到 Spring 中,我们需要配置⼀下存储对象的扫描包路径.
只有被配置的包下的所有类,添加了注解才能被正确的识别并保存到 Spring 中。
即:注释 就像是 一个“桥梁”,将注释的 bean,“送往(存入)” Spring中。
但是!有一些步骤还是不能省略的!
比如创建项目,引入 Spring 框架的支持。
1、创建一个普通的 Maven 项目

创建一个启动类 和 main 方法。

2、引入 Spring 框架的支持
将下面我给的 依赖,复制粘贴到 pom.xml 中。
就是一开始进入项目,显示页面文件

<dependencies>
   <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.2.3.RELEASE</version>
    </dependency>
  
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
        <version>5.2.3.RELEASE</version>
    </dependency>
</dependencies>

如果引入失败,坑是没有使用国内源。
你们可以 去看这篇文章Spring 创建 和 使⽤,里面讲的很清楚

在resources 目录下,创建一个 spring-config.xml 的配置文件。
我们配置扫描路径,就是在这里配置的。
将我们下面给的 配置内容,拷贝我们创建的 配置文件中。

准备工作,到此就结束!
下面,我们就可以开始尝试:使用更简单的方式(使用注解) 来 存储/取出 bean。

1.2、简单的将 bean 存储到容器

一共有两者方法:
1、使用 5 大类注解实现
1、@Controller 【Controller - 控制器】
2、@Service 【service - 服务】
3、@Repository 【repository - 仓库】
4、@Configuration 【configuration - 配置/布局】
5、@Component 【component - 组件】
通过上述五大类注解中的任何一个,都可以将 bean 存储到 Spring 中。

2、通过 方法注解@Bean ,也可以将 一个 bean 存储到 Spring 中。

1、使用 5 大类注解实现将 bean 存储到 容器

@Controller 注解

为了验证 注解 的 效果,我们先来在 beans 目录中,创建一个 User 类。


前面不是说:想要 bean 存入到 Spring中,需要满足2个条件:
1、创建的 诶 需要在 根目录底下
2、加上 五大类注解中的任意一个
x现在,我们就来破坏条件2。
看看 是否 代码 无法执行?

不难得出结论1:注解不可省略。

再看看,不处于根目录下,会是什么样的效果。

由此,不难得出结论:
要存入 Spring 中的 bean,必须处于 根目录下,而且注解不可省略!

思考一个问题

为什么?我们的 Spring 一定要有下面这个配置呢?

如果没有这个配置,意味着什么??
大家可以想象一下:
一个 Spring 项目中,我们的类可分两种类型:
1、需要 进行 控制反转的类,将类的“生命周期”交给 Spring 来管理的类。【比如:UserController】

2、不需要存入 Spring 中的 类。

假设,我们有一个大型项目,需要存入 Spring 的 类 和 不需要存入 Spring 中的类,个数占比是五五开的。
这就会存在问题了!
如果我们没有 描述 根目录 的 这一行代码,
Spring 就会去扫描 所有的类,看看这些类中有哪些。
但是!项目中 需要存入 Spring 中的类,只占 50 %。
即:Spring 要浪费一倍的时间,去排查 不需要 存入 Spring 中的类。
所以,Spring 为了 提升效率,你必须要给我指定扫描的目录。
保证该目录下,一定是需要存入 Spring中的类。
这样 Spring就只需要扫描 对应目录中的类,就可以了!

再思考一个问题:
如果 根目录“beans”下面,还有子目录b。
我们将 UserController 类 存放到 b 中,会不会被加载呢?

@Service

还是一样的配方!

@Repository

@Configuration

@Component

问题一:为什么需要五大类注解?

这就涉及到 软件工程方面的知识了!

之所以讲这个 关于“软件分层” 的定义呢,就是为了 后面讲解 “为什么要有五大注解类” 做铺垫!

那么,问题来了,为什么不直接弄一个 “集大成者” 的注解呢?
一个注解当五个用!
这和为什么每个省/市都有⾃⼰的⻋牌号是⼀样的!
⽐如:
陕⻄的⻋牌号就是:陕X:XXXXXX
北京的⻋牌号:京X:XXXXXX,⼀样。
甚⾄⼀个省不同的县区也是不同的,
⽐如
⻄安就是,陕A:XXXXX,咸阳:陕B:XXXXXX,宝鸡,陕C:XXXXXX,⼀样。
这样做的好处除了可以节约号码之外,更重要的作⽤是可以直观的标识⼀辆⻋的归属地。

拓展一下:还有一个好处!
不知道你们清不清楚,反正我记得 有些城市的道路,在某些特定时间,只允许本地车辆通行。这就起到了一个限流的作用。
而且,对于现在我们国家正处于疫情阶段,车牌号 能帮助 判断 车辆是否 是从 高中风险 地区过来的,方便进行隔离。

如果中国车辆的车牌,都是 中xxxxx
那我们如何进行分别 车辆 的来源?
那就只能几个人坐在电脑前,一个规格车牌进行输入查询。
这就很麻烦!!!
明明 第风险区 和 本地 不用盘查,现在却要都进行盘查。

五大类注解,就是为了避免 混淆的情况出现,所以针对 类的不同场景,进行分层。每个注解负责的层次,是不一样的。
但是只需要处理 各自负责的事务,就可以了。
这就能提高执行的效率。

可能有些人的想法比较奇特:

为什么你就不能为 每个 层次的代码,创建一个目录,把 对应的代码 都放在 对应的目录下,
等到你需要查看这个类,是属于那一层的时候。
直接访问这个类所处于的目录,看看这个类是在 哪个目录底下嘛?
不就行了嘛!
注意!这样做,会产生 2个 有难度的操作!
1、我们得去扫描项目的每个路径,来获取这个类所处的目录【层次】。
这就我们前面举的例子一样,由于一辆车一辆车的去在电脑上查询信息。
这是非常低效!!!

2、将本应该处于某一层的类,创建到另一层里面去了,我们还不知道,怎么办?
这是非常难以判断!你的类又没有移动!因为你直接创建到另一层中的。

如果是使用的注解,那就简单多了。

这就好比:每一辆车都有它们自己地区信息的牌照。
直接 针对 该地区中的车牌,进行查找对应的信息。
不必在全国的范围中,进行搜索。
查询的基数大大降低,查找效率大大提高!

得出结论:
使用 五大类注解,是为了让代码的可读性提高,让程序员能够更直观的判断当前类的业务用途。

问题二:五大类注解之间有什么关系?

这五个类注解实现的功能 都是一样的,只是“长得”不一样。
那么,这 五个类注解之间,有什么关系呢?

结论:
Component 注解,与其它四大注解,呈 “父子” 关系。
Controller,Service, Repository,configuration注解, 都是基于 Component 注解实现的。

问题三:关于 bean id 的命名,为什么是小驼峰结构?

在我们前面演示 五大类注解 的时候,我们其实就猜出了 bean id,默认是小驼峰。
但是!这一块有一个特例!!

总之,随你!
看哪个都行!【建议下载下来,用idea 看,更舒服!】

验证一下:

到这里,我们就明白了 bean 的命名规则。
但是!最好还是 不要斜侧 APIController 这种形式。
虽然能执行,但是不符合规范。
最好,还是写 UserController 这种。
生成的 bean Name 是 小驼峰,才是“正统”!

2、使用 方法注解 @Bean 将 bean更简单的存入容器。

同时,我有验证了一下 beanName,发现和 beanName 是没有关系的!

就是因为 @Bean 注解,需要搭配五大类注解 来使用。
同时,说明了 方法的 beanName 就是 方法名。
不需要 大驼峰,或者 小驼峰的格式。
直接照搬!
你们需要注意的是:方法 和 类 的命名规则,不是同一个!
因此,管你是大骆驼,还是小骆驼,都是不行滴!
只能使用 方法的原名!!!!

重命名 Bean

大家来思考一个问题:
真的在公司里工作,方法的名字,我们会取 像 user1 这么low的名字嘛?
答案:否定的!
就算你愿意这么取,公司也不愿意!
公司对代码,是由规范要求的!
而且,相信大部分朋友,可能都喜欢 将其命名为 getUser.

又或者是通过某种条件,来获取对象。
比如:name 和 id,即 getUserByName,getUserById ,这种命名方式。
相信是部分朋友的“习惯”。

虽然这么命名,也能获取到对象。

这么说吧:我们 想要获取的是 bean,你填一个 方法名,好像有点不太合适吧?
难道我们就不能使用 像类名的 命名规则 给 方法 重命名吗?
答案:有的!!!

这里需要注意一点: 如果方法被重命名,原先的方法名,就没用了!
换个说法:就是无法通过方法名,来获取 bean 了!

结论:
@Bean 命名规则,当没有设置 name 属性时,那么 beanName 默认就是 方法名。

反之,当设置 name 属性之后, name 属性对应的值,就是 beanName。
而且,属性的值,可以有多个,每一个都是可以使用的。
但是!此时 方法名 就不可以使用了!

2、获取 Bean 对象(对象装配)

获取 bean 对象也叫做 对象装配,是把对象取出来放到某个类中,有时候也叫 对象注⼊。
对象装配,简单来说:
就是在使用 A 的时候,需要用到 B了。
那么,你将 B 对象 注入到 A 里面来,让我来用。
因此,对象装配,又可以称为 对象注入。
更直白来说:对象装配(对象注入),就是将 bean 从 Spring 中取出来。

对象装配(对象注⼊)的实现⽅法以下 3 种:

1、 属性(字段)注⼊
2、 构造⽅法注⼊
3、 Setter 注⼊
接下来,我们分别来看。

1、属性注入

使⽤ @Autowired 实现 属性注入。

因此,可以得出结论:
在使用 @Autowired 进行 属性注入的时候
如果 注入的对象,被多次 存入 Spring 中了,那么,光凭 属性的类型是找不到匹配的 bean的!需要将 属性的变量名 改成 BeanName,根据 BeanName 来找寻匹配的对象(bean)并进行 属性注入。

方法不止这一种,但是上述这种 “精确描述 bean 的名称” 的方法,是最简单高效的!

是不是很简单,不用去获取 Spring 的上下文对象 和 getBean 方法,直接通过一个注解,即可获取对应的bean(从Spring中取出 bean)。

使用 @Resource 注解 进行 属性注入

@Resource 注解,是来自于 JDK 的!【JDK 自带的注解】
而 @Autowired, 是属于 Spring的!
@Resource 注解 比 @Autowired 的 属性要强!
因为 @Resource 注解 有一个 name 属性,可以用指定 注入的 bean 的名称。

但是问题来了!如果公司强制要求 使用 @Autowired,怎么办!?
你也不知道是那个老六,闲的没事,注入一个,甚至几个相同的bean。
取的名字又龊!都不想用,怎么办?
其实还有第三种方法:
@Autowired 注解, 搭配使用 @Qualifier 注解

@Autowired注解,搭配使用 @Qualifier 注解,实现属性注入

Qualifier - 限定符 :解决注入迷失问题的。
你可以将 @Qualifier 注解 认为是 一个筛选器。
我们不是通过@Autowired注解 获得了 User1,User2,两个 bean 嘛。
此时,我们就可以通过 @Qualifier 注解,对齐搜索结果,进行筛选!

那么,怎么去使用 @Qualifier 注解呢?
很简单!给 @Qualifier 一个参数(限定符 / beanName),根据 这个参数,对 @Autowired注解 查询的结果,进行筛选!

其实 @Qualifier 和 @Resource 的 name属性类似的!
为什么这么说?
因为 @Qualifier,其实调用了 自身的 value 属性。(隐式的调用 / 默认调用)

虽然 @Qualifier 的属性 可以省略。
但是!指不定 那次版本更新,就删除(或修改)了这一机制。
要求我们必须显式的 给 value 属性 赋值(或者,默认调用的属性,不再是 value了)。
因此,最好还是 显式 的 给 value 赋值。

通过 这种写法,我们的属性名可以随便取自己喜欢的名称。
不用去刻意的修改成 对应的beanName。

总结

对象注入的方法有三种

1、精确描述 bean 的 名称(注入的名称要写对)
2、使用 @Resource 来进行注入。
3、@ Autowired注解,搭配使用 @Qualifier 注解

2、 构造方法注入

还是使用 @Autowired 来注解来实现。

需要注意的是 @Resource 注解,是不支持 构造方法注入的!

另外,在补充一个细节:
当 类里面只有一个构造方法的时候,@Autowired 是可以省略的!

3、 Setter 注入

这个也是一样的,还是通过 @Autowired 实现 Setter 注入。

另外,@Resource也支持 Setter 注入。

拓展:经典面试题 -> 属性注入 ,构造方法注入 和 Setter 注入 之间,有什么区别?

@Resource VS @Autowired 的区别

1、用法不同
@Autowired,支持 属性注入,构造方法注入,Setter 方法注入。

@Resource:支持 属性注入,Setter方法注入。不支持 构造方法注入。

2、@Resource 的 属性注入 比 @Autowired 的 属性注入,使用的更舒服。 因为 @Resource 有很多的属性可以设置;而 @Autowired 只有一个 value 属性。

有很多的属性,即意味着可以使用很多其它的功能。

比如: @Resource 的 name 属性,
那么,可以利用 name 属性可以用来指定 beanName,从而注入对应的 bean。

而 @Autowired 需要将属性变量名修改成对应的 beanName,才能获取对应的bean。
又或者,通过搭配 @Qualifier 注解 来获取对应的bean。
至于其它的,@Autowired 都没有属性了,还怎么对比。。
所以,关于 属性注入方面, @Resource 注解 更好用!

3、出身不同

@Autowired 是 Spring 框架 提供的。

@Resource 是 JDK 提供的。

这么说吧:
如果使用的是 @Resource,转移平台的时候(框架改变了),只要对方也是基于Java实现的,程序是能够使用的。

反之,如果使用的 @Autowired,它是只支持 spring 。
换了一个框架,就不可以用了。
庆幸的是:Spring 占据中国市场的份额,几乎是 100% !
因此,这个问题可以不用担心!
知道有这件事就行。

总结

1、将对象存储奥 Spring 中
1、使用五大类注解:@Controller,@Service,@Reposity,@Configuration,@Component【@Component 是 其余四大类注解的 “父” 级】

2、使用 方法注解 @Bean
【注意事项:@Bean 必须搭配五大类注解才能使用】

2、Bean的命名规则:

需要存入Spring中的类,其类名的第二个字母非大写,在没有指定 beanName 的情况下,默认生成的 beanName 是 类名的首字母小写 的状态(小驼峰)

反之,如果类名的 首字母 和 第二个字母 都是大写的,在没有指定 BeanName 的情况下,默认生成的 BeanName 就是 类的原名。

3、从 Spring 中获取对象

1、属性注入
  1.1、使用 @Autowired注解实现(属性的变量名 和 【bean id / bean name】 相同)

  1.2、使用 @Resource 注解 实现。(添加一个name 属性,其name的值是 bean id)

  1.3、使用 @Autowired 的同时,搭配 @Qualifier 注解。【通过 指定 @Qualifier 的 value 属性值(值为 bean id),】


2、Setter 注入

3、构造方注入 【官方推荐,但是它自己却用的很少】

4、注入的关键字有

1、@Autowired
2、@Resource

5、@Autowired 和 @Resource

1、出身不同【@Autowired 是 Spring 提供的,@Resource 是 JDK 提供的】
2、使用的属性不同【@Autowired 只有一个 value 属性,@Resource 除了name,还有很多】
3、用法不同
【@Autowired 支持 属性注入,构造方法注入,Setter 注入】
【@Resource 支持 属性注入,Setter 注入】

6、解决同一类型多个Bean的报错

1、使用 @Resource(name =“bean id”)
2、使用 @Autowired + @Qualifier(value=“bean id”)

相关文章