【Spring】更简单的读取和存储对象,五大类注解

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

在 Spring 中想要更简单的存储和读取对象的核心是使用注解,也就是我们接下来要学习 Spring 中的相关注解,来存储和读取 Bean 对象

一、存储 Bean 对象

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

<bean id="user" class="com.beans.User"></bean>

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


1、前置⼯作:配置扫描路径(重要)

注意:想要将对象成功的存储到 Spring 中,我们需要配置⼀下存储对象的扫描包路径,只有被配置的包下的所有类,添加了注解才能被正确的识别并保存到 Spring 中。

在 resources 中创建 spring-config.xml 文件

——在 spring-config.xml 中设置 bean 的扫描根路径

<?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=""></content:component-scan>
</beans>

在 java 中创建一个 package:com.beans,这就是所有要存放到 spring 中的 bean 的根路径

也就是说,即使添加了注解,如果不是在配置的扫描包下的类对象,也是不能被存储到 Spring 中的,

注意: 只会扫描对应的目录,包括在根目录下的子目录的所有类都可以被扫描


2、通过五大类注解存储 Bean 对象

想要将对象存储在 Spring 中,有两种注解类型可以实现:

  1. 类注解:

    @Controller 控制器

    @Service 服务

    @Repository 仓库

    @Component 配置

    @Configuration 组件

  2. 方法注解:

    @Bean


3、五大类注解

3.1、Controller(控制器存储)

在 com.beans 下创建一个类 UserController

使⽤ @Controller 存储 bean 的代码如下所示: @Controller 不能省略

import org.springframework.stereotype.Controller;@Controller
public class UserController {public void sayHi() {System.out.println("hello, UserController");}
}

此时我们先使⽤之前读取对象的⽅式来读取上⾯的 UserController 对象,如下代码所示:

在这里插入图片描述


3.2、@Service(服务存储)

使⽤ @Service 存储 bean 的代码如下所示

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

读取 bean 的代码:

public class App {public static void main(String[] args) {// 1、先得到上下文对象ApplicationContext context =new ClassPathXmlApplicationContext("spring-config.xml");// 2、得到 bean 对象UserService service = context.getBean("userService", UserService.class);// 3、使用 beanservice.sayHi();}
}

3.3、@Repository(仓库存储)

使⽤ @Repository 存储 bean 的代码如下所示:

@Repository
public class UserRepository {public void sayHi() {System.out.println("hello, UserRepository");}
}

读取 bean 的代码:

public class App {public static void main(String[] args) {// 1、先得到上下文对象ApplicationContext context =new ClassPathXmlApplicationContext("spring-config.xml");// 2、得到 bean 对象UserRepository repository = context.getBean("userRepository", UserRepository.class);// 3、使用 beanrepository.sayHi();}
}

3.4、@Component(组件存储)

使⽤ @Component 存储 bean 的代码如下所示:

@Component
public class UserComponent {public void sayHi() {System.out.println("hello, UserComponent");}
}

读取 bean 的代码:

public class App {public static void main(String[] args) {// 1、先得到上下文对象ApplicationContext context =new ClassPathXmlApplicationContext("spring-config.xml");// 2、得到 bean 对象UserComponent component = context.getBean("userComponent", UserComponent.class);// 3、使用 beancomponent.sayHi();}
}

3.5、@Configuration(配置存储)

使⽤ @Configuration 存储 bean 的代码如下所示:(注意不是 @Configurable)

@Configuration
public class UserConfig {public void sayHi() {System.out.println("hello, UserConfig");}
}

读取 bean 的代码:

public class App {public static void main(String[] args) {// 1、先得到上下文对象ApplicationContext context =new ClassPathXmlApplicationContext("spring-config.xml");// 2、得到 bean 对象UserConfig config = context.getBean("userConfig", UserConfig.class);// 3、使用 beanconfig.sayHi();}
}

4、为什么要这么多类注解

既然功能是⼀样的,为什么需要这么多的类注解呢?

让代码的可读性提高,让程序员能够直观的判断当前类的用途。

这和为什么每个省/市都有⾃⼰的⻋牌号是⼀样的?⽐如陕⻄的⻋牌号就是:陕X:XXXXXX,北京的⻋牌号:京X:XXXXXX,⼀样。甚⾄⼀个省不同的县区也是不同的,⽐如⻄安就是,陕A:XXXXX,咸阳:陕B:XXXXXX,宝鸡,陕C:XXXXXX,⼀样。这样做的好处除了可以节约号码之外,更重要的作⽤是可以直观的标识⼀辆⻋的归属地。

那么为什么需要怎么多的类注解也是相同的原因,就是让程序员看到类注解之后,就能直接了解当前类
的⽤途,⽐如:

  • @Controller:表示的是业务逻辑层;
  • @Servie:服务层;
  • @Repository:持久层;
  • @Configuration:配置层。

程序的⼯程分层,调⽤流程如下:

在这里插入图片描述

4.1、类注解之间的关系

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

在这里插入图片描述

其实这些注解⾥⾯都有⼀个注解 @Component,@Controller / @Service / @Repository / @Configuration 是基于 @Component 实现的,@Component 可以认为是其他 4 个注解的父类


5、Spring 使用5大类注解生成 beanName 问题

创建类 APIController,使用 getBean() 获取 bean 对象,如果 beanName 使用首字母小写,出现错误:

在这里插入图片描述

如果使用原类名,运行成功:

APIController apiController = context.getBean("APIController", APIController.class);

5.1、查询 Spring 关于 bean 存储时⽣成的命名规则

——在 Idea 中使⽤搜索关键字“beanName”可以看到以下内容:

在这里插入图片描述

Spring 原码与注释

在这里插入图片描述

——查看原码:

在这里插入图片描述

——来到了 .java 文件,说明不是 spring 的方法,打开当前类所在的目录:

在这里插入图片描述

说明 spring 生成 beanName 的方法是 JDK 自身的方法

在这里插入图片描述

——bean 对象的命名规则的⽅法,使⽤的是 JDK Introspector 中的 decapitalize ⽅法,源码如下:

在这里插入图片描述

验证:

import java.beans.Introspector;public class App2 {public static void main(String[] args) {String className1 = "UserController";String className2 = "APIController";System.out.println(Introspector.decapitalize(className1)); // userControllerSystem.out.println(Introspector.decapitalize(className2)); // APIController}
}

6、——⽅法注解

类注解是添加到某个类上的,⽽⽅法注解是放到某个⽅法上的

创建类 User,添加 Getter && Setter 和 toString 方法::

package com.beans;public class User {private int id;private String name;
}

创建类 UserBeans,添加一个 user1 方法:

public class UserBeans {@Beanpublic User user1() {User user = new User();user.setId(1);user.setName("zhangsan");return user;}
}

获取 bean 对象: 方法注解的 beanName 是方法名

在这里插入图片描述

使用类型获取 bean: 可以通过类型获取 bean 对象,但是添加一个 user2 方法,就会报错

在这里插入图片描述


6.1、方法注解要配合类注解使用

说明没有成功将对象注入到 spring 中,在 Spring 框架的设计中,方法注解 @Bean 要配合类注解才能将对象正常的存储到 Spring 容器中

修改代码:

@Component
public class UserBeans {@Bean // 注意:只使用一个 @Bean 是无法将对象存储到容器中的public User user1() {User user = new User();user.setId(1);user.setName("zhangsan");return user;}
}

运行结果: User{id=1, name=‘zhangsan’}


7、重命名 Bean

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

@Component
public class UserBeans {@Bean(name = "userInfo")public User user1() {User user = new User();user.setId(1);user.setName("zhangsan");return user;}
}

使用:

public class App {public static void main(String[] args) {// 1、先得到上下文对象ApplicationContext context =new ClassPathXmlApplicationContext("spring-config.xml");User user = context.getBean("userInfo", User.class);System.out.println(user);}
}

这个重命名的 name 其实是⼀个数组,⼀个 bean 可以有多个名字,@Bean(name = {"userInfo", "userTest"})

并且 name= 可以省略 @Bean({"userInfo", "userTest"})


7.1、Bean 命名规则

注意: 重命名之后,使用原来的方法名是否能正确获取到对象?

User user = context.getBean("user1", User.class);

报错:No bean named ‘user1’ available

  • 当没有设置 name 属性时,那么 bean 默认的名称就是方法名,
  • 当设置了 name 属性之后,只能通过重命名的 name 属性对应的值来获取,也就是说,重命名之后,再使用方法名就获取不到 bean 对象

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

1、对象装配(对象注入)的实现

从容器中获取 bean 对象也叫做对象装配,是把对象取出来放到某个类中,有时候也叫对象注入

实现方法有以下三种:

  • 属性注入

  • 构造方法注入

  • Setter 注入


1.1、属性注入 @Autowired

属性注入是使用 @Autowired 实现的,将 Service 类注⼊到 Controller 类中

——UserController:

/*** 根据属性实现 bean 对象的注入*/
@Controller
public class UserController {@Autowiredprivate UserService userService;public void sayHi() {userService.sayHi();}
}

告诉 spring 在加载 UserController 时,先将 UserService 对象,注入当前类的 userService 属性中。前提是 UserService 已经被注入到 spring 中,

——UserService:

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

——验证:

运行结果:hello, UserService

public class App {public static void main(String[] args) {ApplicationContext context =new ClassPathXmlApplicationContext("spring-config.xml");UserController userController = context.getBean(UserController.class);userController.sayHi();}
}

1.2、构造方法注入

(官方推荐的写法)

如果当前类中只存在一个构造方法,那么@Autowired 注解可以省略

——UserController2:

/*** 使用构造方法实现 bean 注入*/
@Controller
public class UserController2 {private UserService userService;@Autowiredpublic UserController2(UserService userService) {this.userService = userService;}public void sayHi() {userService.sayHi();}// 传统写法// public UserController2() {//    userService = new UserService();////}
}

——验证:

运行结果:hello, UserService

public class App {public static void main(String[] args) {// 1、得到上下文对象ApplicationContext context =new ClassPathXmlApplicationContext("spring-config.xml");UserController2 userController2 = context.getBean(UserController2.class);userController2.sayHi();}
}

1.3、Setter 注入

Setter 注入和属性的 Setter 方法实现类似,只不过在设置 set ⽅法的时候需要加上 @Autowired 注解

——UserController3:

@Controller
public class UserController3 {private UserService userService;@Autowiredpublic void setUserService(UserService userService) {this.userService = userService;}public void sayHi() {userService.sayHi();}
}

——验证:

运行结果:hello, UserService

public class App {public static void main(String[] args) {// 1、得到上下文对象ApplicationContext context =new ClassPathXmlApplicationContext("spring-config.xml");UserController3 userController3 = context.getBean(UserController3.class);userController3.sayHi();}
}

1.4、三种注⼊方式优缺点分析

属性注⼊: 优点是简洁,使⽤⽅便;缺点是只能⽤于 IoC 容器,如果是⾮ IoC 容器不可⽤,并且只有在使⽤的时候才会出现 NPE(空指针异常)

构造⽅法注⼊:Spring 推荐的注⼊⽅式,它的缺点是如果有多个注⼊会显得⽐较臃肿,但出现这种情况你应该考虑⼀下当前类是否符合程序的单⼀职责的设计模式了,它的优点是通⽤性在使⽤之前⼀定能把保证注⼊的类不为空

Setter ⽅式:Spring 前期版本推荐的注⼊⽅式,但通⽤性不如构造⽅法,所有 Spring 现版本已经推荐使⽤构造⽅法注⼊的⽅式来进⾏类注⼊了


1.5、@Resource:另⼀种注⼊关键字

将以上三种对象注入的实现改成 @Resource:属性注入,Setter 注入可以正常运行,而构造方法注入出现问题:

在这里插入图片描述

两种注入方法@Autowired 和 @Resource 的区别:

  • 出身不同: @Autowired 来⾃于 Spring,⽽ @Resource 来⾃于 JDK 的注解;

  • 使⽤时支持设置的参数不同: @Autowired 只支持 required 参数设置,相⽐于 @Autowired 来说,@Resource ⽀持更多的参数设置,例如 name, type 设置,根据名称获取 Bean

  • 用法不同: @Resource支持属性注入和 Setter 注入,但不支持构造方法注入


2、Bean 将一个类型的对象注入多次的问题

注入一个 User 对象,运行观察是否可以正常打印

UserController

@Controller
public class UserController {@Resourcepublic User user;public void sayHi() {System.out.println("User —— " + user);}
}

UserBeans

@Component
public class UserBeans {@Bean(name = "userInfo")public User user1() {User user = new User();user.setId(1);user.setName("zhangsan");return user;}@Beanpublic User user2() {User user = new User();user.setId(1);user.setName("lisi");return user;}
}

App

public class App {public static void main(String[] args) {// 1、得到上下文对象ApplicationContext context =new ClassPathXmlApplicationContext("spring-config.xml");// 2、通过上下文对象的 getBean 方法获取 bean 对象UserController controller = context.getBean(UserController.class);// 3、使用 beancontroller.sayHi();}
}

报错信息: No qualifying bean of type ‘com.beans.User’ available: expected single matching bean but found 2: userInfo,user2


2.1、解决方案

1、精确地描述 bean 的名称(将注入的名称写对)。

修改代码:

@Controller
public class UserController {@Resourcepublic User user2;public void sayHi() {System.out.println("User —— " + user2);}
}

运行结果: User —— User{id=1, name=‘lisi’}

2、使用 @Resource 设置 name 的方式来重命名注入对象

修改代码:

@Controller
public class UserController {@Resource(name = "user2")public User user;public void sayHi() {System.out.println("User —— " + user);}
}

3、使用 Autowired +Qualifier 来筛选 bean 对象

value 可以省略:@Qualifier(“user2”),不建议省略

@Controller
public class UserController {@Autowired@Qualifier(value = "user2")public User user;public void sayHi() {System.out.println("User —— " + user);}
}

综合练习

在 Spring 项⽬中,通过 main ⽅法获取到 Controller 类,调⽤ Controller ⾥⾯通过注⼊的⽅式调⽤Service 类,Service 再通过注⼊的⽅式获取到 Repository 类,Repository 类⾥⾯有⼀个⽅法构建⼀个User 对象,返回给 main ⽅法。Repository ⽆需连接数据库,使⽤伪代码即可


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

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

相关文章

【Linux】带你深入理解文件系统

目录 文件系统 背景知识 磁盘结构 磁盘的存储结构 磁盘抽象(逻辑&#xff0c;虚拟)结构 BootBlock&#xff1a; Super block Data blocks inode Table BlcokBitmap inode Bitmap Group Descriptor Table 文件名和inode编号 硬链接和软链接 软链接 硬链接 取消…

RocketMQ第一课-快速实战以及集群架构搭建

一、RocketMQ产品特点 1、RocketMQ介绍 ​ RocketMQ是阿里巴巴开源的一个消息中间件&#xff0c;在阿里内部历经了双十一等很多高并发场景的考验&#xff0c;能够处理亿万级别的消息。2016年开源后捐赠给Apache&#xff0c;现在是Apache的一个顶级项目。 ​ 早期阿里使用Act…

设计模式行为型——责任链模式

目录 什么是责任链模式 责任链模式的实现 责任链模式角色 责任链模式类图 责任链模式举例 责任链模式代码实现 责任链模式的特点 优点 缺点 使用场景 注意事项 实际应用 什么是责任链模式 责任链模式&#xff08;Chain of Responsibility Pattern&#xff09;又叫职…

【C语言项目】多臂井径电子测井成像项目(一)

目录 1、目的和意义2、本章概述3、串口R2324、OpenGL5、开发环境6、环境配置6.1、VS安装OpenGL6.2、虚拟串口生成工具 7、成品速览参考文献 1、目的和意义 本项目为获取矿藏地层的油气当量和及时精确地测量含油、含气层的压力及温度值的需求&#xff0c;辅助生产管理人员完成对…

警惕!通过谷歌和必应搜索广告传播的新型恶意活动

据观察&#xff0c;一种新的恶意广告活动利用谷歌搜索和必应的广告&#xff0c;以AnyDesk、Cisco AnyConnect VPN和WinSCP等IT工具的用户为目标&#xff0c;诱骗他们下载木马安装程序&#xff0c;目的是入侵企业网络&#xff0c;并可能在未来实施勒索软件攻击。 Sophos在周三的…

最快桌面UI:Siticone Desktop UI 2.1.1 cRACK

富图尔主义控制 80 多个 .NET UI 组件和控件 现代未来 UI/UX 组件 为 Visual Studio 开发做好准备 无限的免费产品支持案例 超轻量和快速性能 广泛可定制和主题化 低资源消耗和占地面积 免版税开发和部署 NET 的最佳 UI 和 UX 库 从最好的图书馆探索无缝流畅的体验 使…

30-使用RocketMQ做削峰处理

1、增加排队功能的思路 在出票模块里,一个消费者拿到了某个车次锁,则该车次下所有的票都由他来出,一张一张的出,知道所有的订单都出完。 2、实现排队出票功能 2.1、 修改发送到MQ消息的内容 修改MQ消息内容,只需要通知出哪天和哪个车次的票(即:组成锁的内容),不需要…

【时频分析,非线性中频】非线性STFT在瞬时频率估计中的应用(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

MATLAB与ROS联合仿真——实例程序搭建思路

一、基础运动控制实例程序搭建思路 1、需要完成的任务&#xff1a; &#xff08;1&#xff09;通过设定小车运动的速度及转角来控制ROS中小车运动。 &#xff08;2&#xff09;通过键盘输入指令控制ROS中小车运动&#xff0c;键盘输入w小车前行&#xff0c;s小车后退&#x…

uniapp 选择城市定位 根据城市首字母分类排序

获取城市首字母排序&#xff0c;按字母顺序排序 <template><view class"address-wrap" id"address"><!-- 搜索输入框-end --><template v-if"!isSearch"><!-- 城市列表-start --><view class"address-sc…

idea 关闭页面右侧预览框/预览条

idea 关闭页面右侧预览框 如图&#xff0c;预览框存在想去除 找了好多方法&#xff0c;什么去掉“setting->appearance里的show editor preview tooltips”的对钩&#xff1b;又或者在该预览区的滚动条上右键&#xff0c;“取消勾选show code lens on scrollbar hover”。都…

《向量数据库指南》——Milvus Cloud2.2.12 易用性,可视化,自动化大幅提升

Milvus Cloud又迎版本升级,三大新特性全力加持,易用性再上新台阶! 近期,Milvus Cloud上线了 2.2.12 版本,此次更新不仅一次性增加了支持 Restful API、召回原始向量、json_contains 函数这三大特性,还优化了 standalone 模式下的 CPU 使用、查询链路等性能,用一句话总…

【MySQL】centos 7下MySQL的环境搭建

从本期博客开始我们正式进入到数据库的学习&#xff0c;在学习数据库时所用到的工具是Linux环境下的MySQL 目录 一、检查环境中是否装有MySQL 二、获取MySQL官方yum源 三、配置MySQL官方yum源 四、一键安装MySQL 五、启动mysql服务 六、登录MySQL 七、修改mysql配置文件…

JS正则表达式:常用正则手册/RegExp/正则积累

一、正则基础语法 JavaScript 正则表达式 | 菜鸟教程 JS正则表达式语法大全&#xff08;非常详细&#xff09; 二、使用场景 2.1、校验中国大陆手机号的正则表达式 正则 /^1[3456789]\d{9}$/解释 序号正则解释1^1以数字 1 开头2[3456789]第二位可以是 3、4、5、6、7、8、…

cnn卷积神经网络(基础)

convolutional neural networks 特征提取&#xff08;卷积、下采样&#xff09;->分类器 &#xff08;全连接&#xff09; 卷积过程 依次进行数乘 &#xff08;每个相同位置上的数字相乘再加和&#xff09; 左右数乘矩阵channel数量要一样&#xff0c;输出得到一个通道 卷…

RabbitMQ 教程 | RabbitMQ 入门

&#x1f468;&#x1f3fb;‍&#x1f4bb; 热爱摄影的程序员 &#x1f468;&#x1f3fb;‍&#x1f3a8; 喜欢编码的设计师 &#x1f9d5;&#x1f3fb; 擅长设计的剪辑师 &#x1f9d1;&#x1f3fb;‍&#x1f3eb; 一位高冷无情的编码爱好者 大家好&#xff0c;我是 DevO…

动态线程池问题的解决

项目中需要将线程池也监控管理起来。 于是决定引入了hippo4j&#xff0c;这个引入很简单&#xff0c;官方的例子也很简单&#xff0c;拿过来直接跑。 出现问题了&#xff0c;用的和例子一模一样的&#xff0c;也没什么错&#xff0c;但是就是在服务器的管理控制台上没有找到动态…

Flutter 状态组件 InheritedWidget

Flutter 状态组件 InheritedWidget 视频 前言 今天会讲下 inheritedWidget 组件&#xff0c;InheritedWidget 是 Flutter 中非常重要和强大的一种 Widget&#xff0c;它可以使 Widget 树中的祖先 Widget 共享数据给它们的后代 Widget&#xff0c;从而简化了状态管理和数据传递…

SpringBoot的三层架构以及IOCDI

目录 一、IOC&DI入门 二、三层架构 数据库访问层 业务逻辑层 控制层 一、IOC&DI入门 在软件开发中&#xff0c;IOC&#xff08;Inversion of Control&#xff09;和DI&#xff08;Dependency Injection&#xff09;是密切相关的概念。 IOC&#xff08;控制反转&a…

flask中的蓝图

flask中的蓝图 在 Flask 中&#xff0c;蓝图&#xff08;Blueprint&#xff09;是一种组织路由和服务的方法&#xff0c;它允许你在应用中更灵活地组织代码。蓝图可以大致理解为应用或者应用中的一部分&#xff0c;可以在蓝图中定义路由、错误处理程序以及静态文件等。然后可以…