Spring中如何用注解方式存取JavaBean?有几种注入方式?

 

  • 博主简介:想进大厂的打工人
  • 博主主页:@xyk:
  • 所属专栏: JavaEE进阶 

本篇文章将讲解如何在spring中使用注解的方式来存取Bean对象,spring提供了多种注入对象的方式,常见的注入方式包括 构造函数注入,Setter 方法注入和属性注入,不同的注入方式都有优缺点,下面我们来讲解一下~~


目录

文章目录

一、使用注解方式的前提

1.1 前置工作

1.2 什么是注解?

二、spring基于注解存储Bean对象

2.1 类注解方式

2.2 如何读取Bean对象?

2.3 读取Bean对象时的命名规则

2.4 方法注解方式

 三、基于注解获取Bean对象(对象装配)

3.1 属性注入

3.2 Setter方法注入

3.3 构造方法注入

3.4 三种注入方式的优缺点:

缺点3:设计原则问题

优点3:完全初始化

优点4:通用性更好

四、@Resource另⼀种注⼊关键字

4.1 @Autowired 和 @Resource 的区别

4.2 同⼀类型多个 Bean 报错处理


一、使用注解方式的前提

1.1 前置工作

在我们使用注解方式来存储 Bean对象 的前提,我们要先将配置文件写好,因为在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.demo"/></beans>

其中 base-package 中的路径对应你项目中的包名即可 

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

pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>org.example</groupId><artifactId>spring-demo1</artifactId><version>1.0-SNAPSHOT</version><properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target></properties><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>
</project>

1.2 什么是注解?

注解就是代码中的特殊标记,无需在xml中配置繁琐的Bean对象代码,注解可以作用在类、方法、属性上。spring 针对 Bean对象的管理提供了注解方式,在 spring中,注解可以分为两大类:

  1. 类注解: 以下四个注解都可以用来创建bean实例,只是为了便于开发者清晰区分当前层。
  2. 方法注解:@Bean
  • @Controller:表示的是业务逻辑层;
  • @Service:服务层;
  • @Repository:持久层;
  • @Configuration:配置层;
  • @Component:组件层

为什么需要这么多个类注解呢?

这是因为让程序猿看到类注解之后,就能直接了解当前类的用途,也是为了开发更加方便~

 在我们查看上面类注解的源码中,可以发现:

 其实这些注解⾥⾯都有⼀个注解 @Component,说明它们本身就是属于 @Component 的“⼦类”

二、spring基于注解存储Bean对象

2.1 类注解方式

@Controller 控制器存储

@Controller
public class BController {public String sayHi() {return "Hi,Controler.";}
}

@Service 服务存储

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

@Repository 仓库存储

@Repository
public class UserRepository {public String sayHi(){return "Hi,@Repository";}
}

@Component 组件存储

@Component
public class UserComponent {public String sayHi() {return "Hi,@Component.";}
}

@Configuration 配置存储

@Configuration
public class UserConfiguration {public String sayHi(){return "Hi,@Configuration";}
}

2.2 如何读取Bean对象?

这里以读取 BController 对象为例, 由于前两个首字母都是大写的,所以我们使用原类名就可以读取到相应的 JavaBean 了。

那如果我们的首字母不大写,或者只有一个字母大写会发生什么?

2.3 读取Bean对象时的命名规则

这个时候,我们就要查询 Spring 关于 bean 存储时⽣成的命名规则了。我们可以在 Idea 中使⽤搜索关键字“beanName”可以看到以下内容。

 它使⽤的是 JDK Introspector 中的 decapitalize ⽅法,源码如下:

    public static String decapitalize(String name) {if (name == null || name.length() == 0) {return name;}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. 如果第⼀个字⺟和第⼆个字⺟都为⼤写的情况,是把 bean 的⾸字⺟也⼤写存储了
  2. 其他的命名规则都是首字母小写即可(默认)

如果不遵守这个规则的话,可是会报错的,如下:找不到此Bean对象

2.4 方法注解方式

类注解用于标记类为Spring Bean,使用@ComponentScan扫描时,每个注解只会创建一个Bean对象,因此在同一个ApplicationContext容器中,获得的对象是同一个。

那么如果想要获取不同的对象怎么办呢? 下面我们就来聊一聊方法注解~

顾名思义,⽅法注解就是是放到某个⽅法上的,以下是简单的代码实现:需要注意的是 ⽅法注解 @Bean 要配合类注解才能将对象正常的存储到 Spring 容器中

@Component
public class StudentBeans {//    @Bean(name = {"s1", "s2"})@Beanpublic Student student1() {// 伪代码,构建对象Student stu = new Student();stu.setId(1);stu.setName("张三");stu.setAge(18);return stu;}@Beanpublic Student student2() {// 伪代码,构建对象Student stu = new Student();stu.setId(2);stu.setName("李四");stu.setAge(20);return stu;}}
public class App {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");Student student = context.getBean("student1",Student.class);System.out.println(student);Student student2 = context.getBean("student2",Student.class);System.out.println(student2);}
}

@Bean默认情况下,Bean name = 方法名

 三、基于注解获取Bean对象(对象装配)

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

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

  1. 属性注⼊
  2. 构造⽅法注⼊
  3. Setter 注⼊

不同的注入方式有不同的适用场景和优缺点。以下案例则以将 Service 类注⼊到 Controller 类中为切入点,帮助大家了解三种注入方式的优缺点和区别~

3.1 属性注入

Controller

@Controller
public class BController {@Autowiredprivate StudentService studentService;public void sayHi() {studentService.sayHi();}
}

Service 

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

此时,通过如下的代码,即可通过 Controller 的 sayHello() 方法调用 service 中的 sayHi() 方法。其余两种装配实现方式,该部分代码等同,Service 也等同,就不再赘述了。

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

3.2 Setter方法注入

即,在 setter 方法前加上 @Autowired 注解。

@Controller
public class BController {private StudentService studentService;@Autowiredpublic void setStudentService(StudentService studentService){this.studentService = studentService;}public void sayHi() {studentService.sayHi();}
}

3.3 构造方法注入

构造⽅法注⼊是在类的构造⽅法中实现注⼊。在类中添加带有参数的构造方法,Spring会根据参数类型和名称,在容器中找到相应的Bean进行注入。特别地,如果只有⼀个构造⽅法,那么 @Autowired 注解可以省略~

@Controller
public class BController {private StudentService studentService;@Autowiredpublic BController(StudentService studentService){this.studentService = studentService;}public void sayHi() {studentService.sayHi();}
}

3.4 三种注入方式的优缺点:

1.属性注入最大的优点就是实现简单、使用简单

   属性注入的缺点主要包含以下 3 个:

  1. 功能性问题:无法注入一个不可变的对象(final 修饰的对象);
  2. 通用性问题:只能适应于 IoC 容器;
  3. 设计原则问题:更容易违背单一设计原则。

缺点1:功能性问题

使用属性注入无法注入一个不可变的对象(final 修饰的对象),如下图所示:

原因也很简单:在 Java 中 final 对象(不可变)要么直接赋值,要么在构造方法中赋值,所以当使用属性注入 final 对象时,它不符合 Java 中 final 的使用规范,所以就不能注入成功了。

如果要注入一个不可变的对象,要怎么实现呢?使用下面的构造方法注入即可。

缺点2:通用性问题

使用属性注入的方式只适用于 IoC 框架(容器),如果将属性注入的代码移植到其他非 IoC 的框架中,那么代码就无效了,所以属性注入的通用性不是很好。

缺点3:设计原则问题

单一设计原则 定义: 就一个类而言, 应该仅有一个引起它变化的原因。单一设计原则就是自己只负责自己的事,不需要去关心别人的事。

使用属性注入的方式,因为使用起来很简单,所以开发者很容易在一个类中同时注入多个对象,而这些对象的注入是否有必要?是否符合程序设计中的单一职责原则?就变成了一个问题。 但可以肯定的是,注入实现越简单,那么滥用它的概率也越大,所以出现违背单一设计原则的概率也越大。 注意:这里强调的是违背设计原则(单一职责)的可能性,而不是一定会违背设计原则此处针对对象是类。

2.Setter 注入的优缺点

要说 Setter 注入有什么优点的话,那么首当其冲的就是它完全符合单一职责的设计原则,因为每一个 Setter 只针对一个对象

它的缺点主要体现在以下 2 点:

  1. 不能注入不可变对象(final 修饰的对象);
  2. 注入的对象可被修改。

缺点2:注入对象可被修改

Setter 注入提供了 setXXX 的方法,意味着你可以在任何时候、在任何地方,通过调用 setXXX 的方法来改变注入对象,所以 Setter 注入的问题是,被注入的对象可能随时被修改

3. 构造方法注入的优缺点

构造方法注入是 Spring 官方从 4.x 之后推荐的注入方式。

构造方法注入相比于前两种注入方法,它可以注入不可变对象,并且它只会执行一次,也不存在像 Setter 注入那样,被注入的对象随时被修改的情况,它的优点有以下 4 个:

  1. 可注入不可变对象;
  2. 注入对象不会被修改;
  3. 注入对象会被完全初始化;
  4. 通用性更好。

优点2:注入对象不会被修改

因为加了final关键字,而且构造方法注入不会像 Setter 注入那样,构造方法在对象创建时只会执行一次,因此它不存在注入对象被随时(调用)修改的情况。

优点3:完全初始化

因为依赖对象是在构造方法中执行的,而构造方法是在对象创建之初执行的,因此被注入的对象在使用之前,会被完全初始化,这也是构造方法注入的优点之一。

优点4:通用性更好

构造方法和属性注入不同,构造方法注入可适用于任何环境,无论是 IoC 框架还是非 IoC 框架,构造方法注入的代码都是通用的,所以它的通用性更好。

四、@Resource另⼀种注⼊关键字

在进⾏类注⼊时,除了可以使⽤ @Autowired 关键字之外,我们还可以使⽤ @Resource 进⾏注⼊,如下代码所示:

@Controller
public class BController {@Resourceprivate StudentService studentService;public void sayHi() {studentService.sayHi();}
}

4.1 @Autowired 和 @Resource 的区别

  1. 出身不同:@Autowired 来⾃于 Spring,⽽ @Resource 来⾃于 JDK 的注解;
  2. 使⽤时设置的参数不同:相⽐于 @Autowired 来说它只支持required,@Resource ⽀持更多的参数设置,例如name 设置,根据名称获取 Bean。
  3. @Autowired 可⽤于 Setter 注⼊、构造函数注⼊和属性注⼊,⽽ @Resource 只能⽤于 Setter 注⼊和属性注⼊,不能⽤于构造函数注⼊

4.2 同⼀类型多个 Bean 报错处理

当出现以下多个 Bean,返回同⼀对象类型时程序会报错,如下代码所示:

@Component
class UserComponent {@Beanpublic User user1() {User user = new User();user.setId(1);user.setName("Java");return user;}@Beanpublic User user2() {User user = new User();user.setId(2);user.setName("MySQL");return user;}
}

在另⼀个类中获取 User 对象,如下代码如下:

@Controller
public class UserController {// 注⼊@Resourceprivate User user;public User getUser() {return user;}
}

解决同⼀个类型,多个 bean 的解决⽅案有以下两个:

  1. 使⽤ @Resource(name="user1") 定义。
  2. 使⽤ @Qualifier 注解定义名称,结合@Autowired

使⽤ @Resource(name="XXX")

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

使⽤ @Qualifier

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

@Qualifier中value可以省略~


 创作不易,欢迎大家私信我,一起探讨问题~ 

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

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

相关文章

如何在局域网外SSH远程访问连接到家里的树莓派?

文章目录 如何在局域网外SSH远程访问连接到家里的树莓派&#xff1f;如何通过 SSH 连接到树莓派步骤1. 在 Raspberry Pi 上启用 SSH步骤2. 查找树莓派的 IP 地址步骤3. SSH 到你的树莓派步骤 4. 在任何地点访问家中的树莓派4.1 安装 Cpolar4.2 cpolar进行token认证4.3 配置cpol…

word图自动编号引用

一.引用&#xff0c;插入题注&#xff0c;新建标签&#xff0c;图1-&#xff0c;这样生成的就是图1-1这种&#xff0c;确定 再添加图片就点击添加题注就行&#xff0c;自动生成图1-2这种 二.图例保存为书签 插入&#xff0c;书签&#xff0c;书签命名&#xff0c;如图1 三…

Tensorflow(二)

一、过拟合 过拟合现象:机器对于数据的学习过于自负(想要将误差减到最小)。 解决方法:利用正规化方法 二、卷积神经网络(CNN) 卷积神经网络是近些年来逐渐兴起的人工神经网络&#xff0c;主要用于图像分类、计算机视觉等。 卷积:例如对图片每一小块像素区域的处理&#xff…

sqlite触发器1

SQLite 的触发器&#xff08;Trigger&#xff09;可以指定在特定的数据库表发生 DELETE、INSERT 或 UPDATE 时触发&#xff0c;或在一个或多个指定表的列发生更新时触发。 SQLite 只支持 FOR EACH ROW 触发器&#xff08;Trigger&#xff09;&#xff0c;没有 FOR EACH STATEM…

SpringBoot+Jpa+Thymeleaf实现增删改查

SpringBootJpaThymeleaf实现增删改查 这篇文章介绍如何使用 Jpa 和 Thymeleaf 做一个增删改查的示例。 1、pom依赖 pom 包里面添加Jpa 和 Thymeleaf 的相关包引用 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.…

Ubuntu 20.04下的录屏与视频剪辑软件

ubuntu20.04下的录屏与视频剪辑 一、录屏软件SimpleScreenRecorder安装与使用 1、安装 2、设置录制窗口参数 3、开始录制 二、视频剪辑软件kdenlive的安装 1、安装 2、启动 一、录屏软件SimpleScreenRecorder安装与使用 1、安装 &#xff08;1&#xff09;直接在终端输入以下命…

DAY2,Qt(继续完善登录框,信号与槽的使用 )

1.继续完善登录框&#xff0c;当登录成功时&#xff0c;关闭登录界面&#xff0c;跳转到新的界面中&#xff0c;来回切换页面&#xff1b; ---mychat.h chatroom.h---两个页面头文件 #ifndef MYCHAT_H #define MYCHAT_H#include <QWidget> #include <QDebug> /…

STC12C5A60S2 单片机串口2的通信功能测试

根据手册说明&#xff0c;STC12C5A60S2 系列单片机可以直接使用 reg51.h 的头文件&#xff0c;只是在用到相应的特殊功能寄存器时&#xff0c;要做相应的定义即可。 笔记来自视频教程链接: https://www.bilibili.com/video/BV1Qq4y1Z7iS/?spm_id_from333.880.my_history.page…

EtherNet/IP转Modbus网关以连接AB PLC

本案例为西门子S7-1200 PLC通过捷米特Modbus转EtherNet/IP网关捷米特JM-EIP-RTU连接AB PLC的配置案例。 网关分别从ETHERNET/IP一侧和MODBUS一侧读写数据&#xff0c;存入各自的缓冲区&#xff0c;网关内部将缓冲区的数据进行交换&#xff0c;从而实现两边数据的传输。 网关做为…

Vlan端口隔离(第二十四课)

一、端口隔离 1、端口隔离技术概述 1)端口隔离技术出现背景:为了实现报文之间的二层隔离,可以将不同的端口加入不同的VLAN,但这样会浪费有限的VLAN ID资源。 2)端口隔离的作用:采用端口隔离功能,可以实现同一VLAN内端口之间的隔离。 3)如何实现端口隔离功能:只需要…

tcl学习之路(一)(Vivado与Tcl)

学习第一步&#xff1a;安装tcl编译软件 点击这里进入activestate的官网&#xff0c;下载你喜欢的操作系统所需的安装包。这里我下载的是windows下的安装包。一步一步安装即可。   那么&#xff0c;安装后&#xff0c;我们可以在开始的菜单栏处看到三个应用程序。      …

Cisco IOS操作(红茶三杯CCNA)

Cisco路由器组件 CPU&#xff1a;执行指令 RAM&#xff1a;断电内容丢失 - 运行操作系统 - 运行配置文件 - IP路由表 - ARP缓存 - 数据包缓存区 ROM&#xff1a;保存开机自检软件&#xff0c;存储路由器的启动引导程序 - bootstrap指令 - 基本的自检软件 - 迷你版IOS 非易失RAM…

STM32CubeMX配置STM32G031多通道ADC + DMA采集(HAL库开发)

时钟配置HSI主频配置64M 勾选打开8个通道的ADC 使能连续转换模式 添加DMA DMA模式选择循环模式 使能DMA连续请求 采样时间配置160.5 转换次数为8 配置好8次转换的顺序 配置好串口&#xff0c;选择异步模式配置好需要的开发环境并获取代码 修改main.c 串口重定向 #include &…

从上到下打印二叉树

题目描述 从上到下打印出二叉树的每个节点&#xff0c;同一层的节点按照从左到右的顺序打印。 例如: 给定二叉树: [3,9,20,null,null,15,7], 返回&#xff1a; [3,9,20,15,7] 算法思想 建立一个vector数组ret用来当做返回的结果数组&#xff0c;建立一个队列用来接收二叉树…

毓恬冠佳冲刺上市:打破汽车天窗外商垄断,长安汽车为其主要客户

撰稿|行星 来源|贝多财经 7月23日&#xff0c;上海毓恬冠佳科技股份有限公司&#xff08;以下简称“毓恬冠佳”&#xff09;在深圳证券交易所的审核状态变更为“已问询”。据贝多财经了解&#xff0c;毓恬冠佳于2023年6月27日递交招股书&#xff0c;准备在创业板上市。 本次冲…

pycharm中运行py文件时,报错:找不到自己编写的包等目录问题ModuleNotFoundError: No module named ‘xxx‘

【问题描述】&#xff1a;pycharm中运行py文件时&#xff0c;报错&#xff1a;找不到自己编写的包等目录问题 【报错】: ModuleNotFoundError: No module named ‘xxx’ ERROR: file not found 【问题定位】&#xff1a;运行的py文件和用到的包或者数据不在同一个文件目录下时…

基于Java+SpringBoot+vue前后端分离网上租赁系统设计实现

博主介绍&#xff1a;✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专…

如何使用GPT作为SQL查询引擎的自然语言

​生成的AI输出并不总是可靠的&#xff0c;但是下面我会讲述如何改进你的代码和查询的方法&#xff0c;以及防止发送敏感数据的方法。与大多数生成式AI一样&#xff0c;OpenAI的API的结果仍然不完美&#xff0c;这意味着我们不能完全信任它们。幸运的是&#xff0c;现在我们可以…

Vue前端渲染blob二进制对象图片的方法

近期做开发&#xff0c;联调接口。接口返回的是一张图片&#xff0c;是对二进制图片处理并渲染&#xff0c;特此记录一下。 本文章是转载文章&#xff0c;原文章&#xff1a;Vue前端处理blob二进制对象图片的方法 接口response是下图 显然&#xff0c;获取到的是一堆乱码&…

FUNBOX_1靶场详解

FUNBOX_1靶场复盘 这个系列的靶场给出的干扰因素都挺多的&#xff0c;必须从中找到有用的线索才可以。 这个靶场你扫描到ip地址后打开网页会发现&#xff0c;ip自动转换成域名了&#xff0c;所以我们需要添加一条hosts解析才可以。 192.168.102.190 funbox.fritz.box从目录…