Spring中的Bean对象

news2024/10/7 8:23:50
经过上一篇博客,我们已经可以实现基本的 Spring 读取和存储对象的操作了,但在操作的过程中我们发 现读取和存储对象并没有想象中的那么“ 简单 ,所以接下来我们要学习更加简单的操作 Bean 对象的方法。
Spring 中想要 更简单的存储和读取对象的核心是使用注解 ,也就是我们接下来要学习 Spring 中的相关注解,来存储和读取 Bean 对象。

一、存储 Bean 对象

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

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

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

注意:想要将对象成功的存储到 Spring 中,我们需要配置一下存储对象的扫描包路径,只有被配置的包下的所有类,添加了注解才能被正确的识别并保存到 Spring 中。
spring-config.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="com.bit.service"></content:componentscan>
</beans>
其中标红的一行为注册扫描的包,如下图所示:
也就是说,即使添加了注解,如果不是在配置的扫描包下的类对象,也是不能被存储到 Spring
的。

1.2 添加注解存储 Bean 对象

想要将对象存储在 Spring 中,有两种注解类型可以实现:
1. 类注解: @Controller @Service @Repository @Component @Configuration
2. 方法注解: @Bean
接下来我们分别来看。

1.2.1 @Controller(控制器存储)

使用 @Controller 存储 bean 的代码如下所示:
@Controller // 将对象存储到 Spring 中
public class UserController {
public void sayHi(String name) {
System.out.println("Hi," + name);
    }
}
此时我们先使用之前读取对象的方式来读取上面的 UserController 对象,如下代码所示:
public class Application {
public static void main(String[] args) {
// 1.得到 spring 上下文
ApplicationContext context =
new ClassPathXmlApplicationContext("spring-config.xml");
// 2.得到 bean
UserController userController = (UserController)
context.getBean("userController");
// 3.调用 bean 方法
userController.sayHi("Bit");
    }
}

1.2.2 @Service(服务存储)

使用 @Service 存储 bean 的代码如下所示:
@Service
public class UserService {
public void sayHi(String name) {
System.out.println("Hi," + name);
    }
}

读取 bean 的代码:

class App {
public static void main(String[] args) {
// 1.得到 spring 上下文
ApplicationContext context =
new ClassPathXmlApplicationContext("spring-config.xml");
// 2.得到 bean
UserService userService = (UserService) context.getBean("userService");
// 3.调用 bean 方法
userService.sayHi("Bit");
    }
}

1.2.3 @Repository(仓库存储)

使用 @Repository 存储 bean 的代码如下所示:
@Repository
public class UserRepository {
public void sayHi(String name) {
System.out.println("Hi," + name);
    }
}
读取 bean 的代码:
class App {
public static void main(String[] args) {
// 1.得到 spring 上下文
ApplicationContext context =
new ClassPathXmlApplicationContext("spring-config.xml");
// 2.得到某个 bean
UserRepository userRepository = (UserRepository)
context.getBean("userRepository");
// 3.调用 bean 方法
userRepository.sayHi("Bit");
    }
}

1.2.4 @Component(组件存储)

使用 @Component 存储 bean 的代码如下所示:
@Component
public class UserComponent {
public void sayHi(String name) {
System.out.println("Hi," + name);
    }
}
读取 bean 的代码:
class App {
public static void main(String[] args) {
// 1.得到 spring 上下文
ApplicationContext context =
new ClassPathXmlApplicationContext("spring-config.xml");
// 2.得到某个 bean
UserComponent userComponent = (UserComponent)
context.getBean("userComponent");
// 3.调用 bean 方法
userComponent.sayHi("Bit");
    }
}

1.2.5 @Configuration(配置存储)

使用 @Configuration 存储 bean 的代码如下所示:
@Configuration
public class UserConfiguration {
public void sayHi(String name) {
System.out.println("Hi," + name);
    }
}
读取 bean 的代码:
class App {
public static void main(String[] args) {
// 1.得到 spring 上下文
ApplicationContext context =
new ClassPathXmlApplicationContext("spring-config.xml");
// 2.得到某个 bean
UserConfiguration userConfiguration = (UserConfiguration)
context.getBean("userConfiguration");
// 3.调用 bean 方法
userConfiguration.sayHi("Bit");
    }
}

1.3 为什么要这么多类注解?

既然功能是一样的,为什么需要这么多的类注解呢?
这和为什么每个省 / 市都有自己的车牌号是一样的?比如陕西的车牌号就是:陕 X XXXXXX ,北京的车牌号:京X XXXXXX ,一样。甚至一个省不同的县区也是不同的,比如西安就是,陕 A XXXXX ,咸阳:陕B: XXXXXX ,宝鸡,陕 C XXXXXX ,一样。这样做的好处除了可以节约号码之外,更重要的作用是可以直观的标识一辆车的归属地。
那么为什么需要怎么多的类注解也是相同的原因,就是让程序员看到类注解之后,就能直接了解当前类的用途,比如:
  • @Controller:表示的是业务逻辑层;
  • @Servie:服务层;
  • @Repository:持久层;
  • @Configuration:配置层。
程序的工程分层,调用流程如下:
1.3.1 类注解之间的关系
查看 @Controller / @Service / @Repository / @Configuration 等注解的源码发现:

其实这些注解里面都有一个注解 @Component,说明它们本身就是属于 @Component 子类

1.3.2 注意 Bean 的命名

通过上面示例,我们可以看出,通常我们 bean 使用的都是标准的大驼峰命名,而读取的时候首字母小写就可以获取到 bean 了,如下图所示:

 

然而,当我们首字母和第二个字母都是大写时,就不能正常读取到 bean 了,如下图所示:

 这个时候,我们就要查询 Spring 关于 bean 存储时生成的命名规则了。 

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

顺藤摸瓜,我们最后找到了 bean 对象的命名规则的方法

它使用的是 JDK Introspector 中的 decapitalize 方法,源码如下: 

public static String decapitalize(String name) {
if (name == null || name.length() == 0) {
return name;
}
// 如果第一个字母和第二个字母都为大写的情况,是把 bean 的首字母也大写存储了
if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) &&
Character.isUpperCase(name.charAt(0))){
return name;
}
// 否则就将首字母小写
char chars[] = name.toCharArray();
chars[0] = Character.toLowerCase(chars[0]);
return new String(chars);
}
所以对于上面报错的代码,我们只要改为以下代码就可以正常运行了:

 

1.4 方法注解 @Bean

类注解是添加到某个类上的,而方法注解是放到某个方法上的,如以下代码的实现:
public class Users {
@Bean
public User user1() {
User user = new User();
user.setId(1);
user.setName("Java");
return user;
    }
}
然而,当我们写完以上代码,尝试获取 bean 对象中的 user1 时却发现,根本获取不到:
public class Application {
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("spring-config.xml");
User user = (User) context.getBean("user1");
System.out.println(user.toString());
    }
}
以上程序的执行结果如下:

 

这是为什么嘞?

1.4.1 方法注解要配合类注解使用

Spring 框架的设计中,方法注解 @Bean 要配合类注解才能将对象正常的存储到 Spring 容器中,如下代码所示:
@Component
public class Users {
@Bean
public User user1() {
User user = new User();
user.setId(1);
user.setName("Java");
return user;
    }
}

再次执行以上代码,运行结果如下:

1.4.2 重命名 Bean

可以通过设置 name 属性给 Bean 对象进行重命名操作,如下代码所示:
@Component
public class Users {
@Bean(name = {"u1"})
public User user1() {
User user = new User();
user.setId(1);
user.setName("Java");
return user;
    }
}
此时我们使用 u1 就可以获取到 User 对象了,如下代码所示:
class App {
public static void main(String[] args) {
// 1.得到 spring 上下文
ApplicationContext context =
new ClassPathXmlApplicationContext("spring-config.xml");
// 2.得到某个 bean
User user = (User) context.getBean("u1");
// 3.调用 bean 方法
System.out.println(user);
    }
}
这个重命名的 name 其实是一个数组,一个 bean 可以有多个名字:
@Bean(name = {"u1", "us1"})
public User user1() {
User user = new User();
user.setId(1);
user.setName("Java");
return user;
}
并且 name={} 可以省略,如下代码所示:
@Bean({"u1", "us1"})
public User user1() {
User user = new User();
user.setId(1);
user.setName("Java");
return user;
}

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

获取 bean 对象也叫做 对象装配 ,是把对象取出来放到某个类中,有时候也叫 对象注入
对象装配(对象注入)的实现方法以下 3 种:
1. 属性注入
2. 构造方法注入
3. Setter 注入
接下来,我们分别来看。
下面我们按照实际开发中的模式,将 Service 类注入到 Controller 类中。

2.1 属性注入

属性注入是使用 @Autowired 实现的,将 Service 类注入到 Controller 类中。
Service 类的实现代码如下:
import org.springframework.stereotype.Service;
@Service
public class UserService {
/**
* 根据 ID 获取用户数据
*
* @param id
* @return
*/
public User getUser(Integer id) {
// 伪代码,不连接数据库
User user = new User();
user.setId(id);
user.setName("Java-" + id);
return user;
    }
}
Controller 类的实现代码如下:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller
public class UserController {
// 注入方法1:属性注入
@Autowired
private UserService userService;
public User getUser(Integer id) {
return userService.getUser(id);
    }
}
获取 Controller 中的 getUser 方法
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class UserControllerTest {
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("spring-config.xml");
UserController userController = context.getBean(UserController.class);
System.out.println(userController.getUser(1).toString());
    }
}
最终结果如下:

属性注入的核心实现如下:

2.2 构造方法注入

构造方法注入是在类的构造方法中实现注入,如下代码所示:
@Controller
public class UserController2 {
// 注入方法2:构造方法注入
private UserService userService;
@Autowired
public UserController2(UserService userService) {
this.userService = userService;
}
public User getUser(Integer id) {
return userService.getUser(id);
    }
}
注意事项:如果类只有一个构造方法,那么 @Autowired 注解可以省略;如果类中有多个构造方
法,那么需要添加上 @Autowired 来明确指定到底使用哪个构造方法。

2.3 Setter 注入

Setter 注入和属性的 Setter 方法实现类似,只不过 在设置 set 方法的时候需要加上 @Autowired ,如下代码所示:
@Controller
public class UserController3 {
// 注入方法3:Setter注入
private UserService userService;
@Autowired
public void setUserService(UserService userService) {
this.userService = userService;
}
public User getUser(Integer id) {
return userService.getUser(id);
    }
}

2.4 三种注入优缺点分析

  • 属性注入的优点是简洁,使用方便;缺点是只能用于 IoC 容器,如果是非 IoC 容器不可用,并且只有在使用的时候才会出现 NPE(空指针异常)。
  • 构造方法注入是 Spring 推荐的注入方式,它的缺点是如果有多个注入会显得比较臃肿,但出现这种情况你应该考虑一下当前类是否符合程序的单一职责的设计模式了,它的优点是通用性,在使用之前一定能把保证注入的类不为空。
  • Setter 方式是 Spring 前期版本推荐的注入方式,但通用性不如构造方法,所有 Spring 现版本已经推荐使用构造方法注入的方式来进行类注入了。

2.5 @Resource:另一种注入关键字

在进行类注入时,除了可以使用 @Autowired 关键字之外,我们还可以使用 @Resource 进行注入,如下代码所示:
@Controller
public class UserController {
// 注入
@Resource
private UserService userService;
public User getUser(Integer id) {
return userService.getUser(id);
    }
}
@Autowired @Resource 的区别
  • 出身不同:@Autowired 来自于 Spring,而 @Resource 来自于 JDK 的注解;
  • 使用时设置的参数不同:相比于 @Autowired 来说,@Resource 支持更多的参数设置,例如:name 设置,根据名称获取 Bean

2.6 同一类型多个 @Bean 报错

当出现以下多个 Bean ,返回同一对象类型时程序会报错,如下代码所示:
@Component
public class Users {
@Bean
public User user1() {
User user = new User();
user.setId(1);
user.setName("Java");
return user;
}
@Bean
public User user2() {
User user = new User();
user.setId(2);
user.setName("MySQL");
return user;
    }
}
在另一个类中获取 User 对象,如下代码如下:
@Controller
public class UserController4 {
// 注入
@Resource
private User user;
public User getUser() {
return user;
    }
}
以上程序的执行结果如下:
报错的原因是,非唯一的 Bean 对象。
同一类型多个 Bean 报错处理
解决同一个类型,多个 bean 的解决方案有以下两个:
  • 使用 @Resource(name="user1") 定义。
  • 使用 @Qualifier 注解定义名称。

① 使用 @Resource(name="XXX")

@Controller
class UserController4 {
// 注入
@Resource(name = "user1")
private User user;
public User getUser() {
return user;
    }
}

 ② 使用 @Qualifier

@Controller
public class UserController5 {
// 注入
@Autowired
@Qualifier(value = "user2")
private User user;
public User getUser() {
return user;
    }
}

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

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

相关文章

mac怎么转换音频格式?

mac怎么转换音频格式&#xff1f;相信很多小伙伴都知道&#xff0c;平时我们接触到的音频格式大多是mp3格式的&#xff0c;因为mp3是电脑上最为流行的音频格式&#xff0c;不过除了mp3格式外&#xff0c;还有很多不同的音频格式&#xff0c;有时候不同网上或者不同软件上下载到…

Python爬虫学习笔记(十)————Scrapy

目录 1.scrapy是什么&#xff1f; 2.安装scrapy 3. scrapy项目的创建以及运行 &#xff08;1&#xff09;创建scrapy项目&#xff1a; &#xff08;2&#xff09;项目组成&#xff1a; &#xff08;3&#xff09;创建爬虫文件&#xff1a; ①跳转到spiders文件夹中去创建…

ext4 - delay allocation数据结构

概述 延迟分配delay allocation是ext4非常重要的特性&#xff0c;启用该特性write系统将用户空间buffer写入内存page cache中即返回&#xff0c;此时也不会真正进行磁盘block分配&#xff0c;而是延迟到磁盘回写时&#xff08;比如dirty ratio达到一定值&#xff0c;定时刷新&…

高校大数据教材推荐-《Python中文自然语言处理基础与实战》

《Python中文自然语言处理基础与实战》是“十四五”职业教育国家规划教材&#xff0c;是大数据应用开发&#xff08;Python&#xff09;“1X”职业技能等级证书配套系列教材。本书以项目为载体&#xff0c;突出职业技能。坚持理实一体化的理念。理实一体化&#xff0c;就是理论…

H5基础教程

w3school官网 请点击下面工程名称&#xff0c;跳转到代码的仓库页面&#xff0c;将工程 下载下来 Demo Code 里有详细的注释 TestH5

Vue3+Vite前端知识汇总1篇

目录 1、设置package.json&#xff0c;让编译完成后自动打开浏览器。 2、设置vite.config.ts,设置src别名&#xff0c;后面就不用 ../../../ 了。 3、安装types/node 解决vscode显示红波浪线问题。 4、安装 sass和reset.css 5、创建并引入全局组件&#xff0c;HospitalTop…

2816. 判断子序列

题目链接&#xff1a; 自己的做法&#xff1a; #include <bits/stdc.h>using namespace std;const int N 1e5 10; int a[N], b[N]; int main() {int n, m;bool flag true;scanf("%d%d", &n, &m);for (int i 0; i < n; i) scanf("%d"…

【C++】AVL树的实现及测试

文章目录 AVL树节点的定义AVL树的定义AVL树的插入插入后更新平衡因子AVL树的右单旋AVL树的左单旋先左单旋再右单旋先右单旋再左单旋检查是否满足AVL树总代码 AVL树 AVL树也叫平衡二叉搜索树&#xff0c;通过旋转解决了搜索二叉树的不确定性&#xff0c;让整颗树趋近于一颗满二叉…

一本通OJ 1810 登山 题解

题目链接 题目大意 从 ( 0 , 0 ) (0,0) (0,0) 走到 ( n , n ) (n,n) (n,n) &#xff0c;不能超过直线 y x yx yx&#xff0c;并且图上有 m m m 个点不能走&#xff0c;问你有几种方案 解题思路 很明显这题与卡特兰数有关&#xff0c;但是不同点在于这题中存在点不能走…

解决阿里云服务器不能访问端口

服务器已经下载了redis&#xff0c;kafka&#xff0c;但就是访问不了端口号&#xff0c; 开通云服务器以后&#xff0c;请一定在安全组设置规则&#xff0c;放行端口 防火墙要关闭

服务器内存满了解决之路

背景&#xff1a;大清早&#xff0c;突然一通电话吵醒&#xff0c;说项目跑不了&#xff0c;还没洗漱赶紧跑过来&#xff0c;毕竟属于实时在用的系统。排查发现系统盘满了&#xff0c;数据写不进去了&#xff0c;导致报错。接手的项目&#xff0c;从来没考虑服务器问题&#xf…

SR501人体红外模块

文章目录 前言一、SR501模块介绍二、设备树添加节点三、驱动程序四、测试程序五、上机测试及效果总结 前言 人体红外模块 是一种能够检测人或动物发射的红外线而输出电信号的传感器。广泛应用于各种自动化控制装置中。比如常见的楼道自动开关、防盗报警等。 一、SR501模块介绍…

深度学习-第R1周心脏病预测

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 我的环境&#xff1a; 语言环境&#xff1a;Python3.10.7编译器&#xff1a;VScode深度学习环境&#xff1a;TensorFlow 2.13.0 一、前期工作&#xff1a; …

语义通信中基于深度双Q网络的多维资源联合分配算法

目录 论文简介系统模型多维资源联合分配模型多维资源联合分配算法 论文简介 作者 林润韬 郭彩丽 陈九九 王彦君发表期刊or会议 《移动通信》发表时间 2023.4 系统模型 场景中的边缘服务器部署在路边单元上&#xff0c;每个路边单元具有一定的无线覆盖区域&#xff0c;服务器将…

安装mmocr

安装mmocr 一、安装mmdetection 在安装前&#xff0c;如果已经安装过mmcv&#xff0c;先卸载掉&#xff0c;否则不同版本会导致ModuleNotFoundError报错&#xff01; 1、先安装对应版本的pytorch&#xff08;本次cuda10.2&#xff0c;pytorch1.7&#xff09; 2、安装对应版本的…

TableGPT: Towards Unifying Tables, Nature Language and Commands into One GPT

论文标题&#xff1a;TableGPT: Towards Unifying Tables, Nature Language and Commands into One GPT 论文地址&#xff1a;https://github.com/ZJU-M3/TableGPT-techreport/blob/main/TableGPT_tech_report.pdf 发表机构&#xff1a;浙江大学 发表时间&#xff1a;2023 本文…

搭建基于Nginx+Keepalived的高可用web集群并实现监控告警

目录 搭建相关服务器DNS服务器配置WEB服务器配置配置静态IP编译安装nginx 负载均衡器配置lb1lb2高可用配置 NFS服务器配置配置静态IP安装软件包新建共享目录web服务器挂载 监控服务器配置安装node-exporter编写prometheus.yml安装alertmanager和钉钉插件获取机器人webhook编写a…

ubuntu22.04上如何创建有privilege权限,有固定自定义IP的空容器

需求背景&#xff1a; 我想用docker来隔离自己的主机环境&#xff0c;来创建一个隔离的空白全新的开发环境&#xff0c;并且使之有固定的IP&#xff0c;在里面可以自由更新下载各种编译依赖&#xff0c;具有privileged权限的容器&#xff0c;以下是操作实现的具体步骤 查看do…

系统架构设计师-软件架构设计(2)

目录 一、基于架构的软件开发方法&#xff08;ABSD&#xff09; 1、架构需求 1.1 需求获取 1.2 标识构件 1.3 架构需求评审 2、架构设计 2.1 提出架构模型 2.2 映射构件 2.3 分析构件的相互作用 2.4 产生架构 2.5 设计评审 3、架构文档化 4、架构复审 5、架构实现 5.1 分析与…

JVM运行时区域——对象创建内存分配过程

新创建的对象&#xff0c;都存放在伊甸园区域&#xff0c;当垃圾回收时&#xff0c;将伊甸园区域的垃圾数据销毁&#xff0c;然后将存活的对象转移到幸存者0区域&#xff0c;之后创建的新的对象还是存放在伊甸园区域&#xff0c;等到再次垃圾回收后&#xff0c;将伊甸园区域和幸…