【Spring使用注解更简单的实现Bean对象的存取】

news2025/2/1 9:08:14

🎉🎉🎉点进来你就是我的人了
博主主页:🙈🙈🙈戳一戳,欢迎大佬指点!

欢迎志同道合的朋友一起加油喔🤺🤺🤺


目录

一、前言: 

二、储存Bean对象和使用Bean对象

1. 添加存储对象的注解  (这一步完成Spring容器已经有了Bean对象)

(1)类注解 

为什么需要五大类注解

(2)方法注解

(3) 类注解和方法注解的区别

2. 对象装配/对象注入  (你的代码可以使用被注入的对象来完成各种任务)

(1) 属性注入 (日常开发中使用最多的一种注入方式)

(2) Setter方法注入

(3) 构造方法注入 ( Spring 官方从 4.x 之后推荐的注入方式)

(4) @Autowired与@Resource区别

(5)  注入异常问题



一、前言: 

上篇博客,我们讲了一个spring core项目的大致流程:

创建项目——》将对象储存到Spring容器中 ——》从Spring容器中取出Bean对象

上篇博客的这种Spring对象的存储和读取方式虽然能够满足我们的需求,但是这也只是站在小项目的层面上来讲的。如果一个项目中的类多起来了,我们如果采用上篇博客这种面向xml配置配置Spring存储Bean对象,那得在spring的配置文件中添加多少bean标签,并且id还不能够重复!所以这种操作Spring存储和读取Bean对象的做法太过复杂.

接下来我们来看如何更简单的实现Spring中存储Bean对象的存取操作!!!

首先,Spring项目的创建——这个没有什么好说的!就按我们上篇博客的步骤来进行。

但注意:与上篇博客相比,spring的配置文件发生了改变——为了更简单的实现Bean对象的存入(把对象存到spring中)

更改后的spring配置文件(resources目录下的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="在对象储存中,要扫描的路径"></content:component-scan>
</beans>

同时pom.xml中添加Spring的框架支持,xml配置如下: 

<dependencies>
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.3.27</version>
    </dependency>
 
</dependencies>

二、储存Bean对象和使用Bean对象

首先我们回忆下,我们执行储存Bean对象的方式。

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

这种存入 Bean 的方式,并不是很好!
1、需要手动添加 bean 对象 到 配置文件中
2、如果 是配置文件中出现了问题,不好调试。 

而现在我们不需要一个一个的在spring配置文件中添加我们要储存的Bean对象。我们直接:

 你以为把扫描路径添加到配置文件中就行了吗?

不过你还需要再类中添加注解——再扫描的过程中,只有添加了注解的类才能成功存储到spring中

这就引起了我们注解的概念

1. 添加存储对象的注解  (这一步完成Spring容器已经有了Bean对象)

(1)类注解 

  • @Controller,控制层(前端参数校验)
  • @Service,服务层(数据组装和接口调用)
  • @Repository,数据持久层(负责和数据库进行交互)
  • @Component,组件(非其他四类)
  • @Configuration,配置层(系统运行前,提前加载一些资源)
@Controller
public class UserController {
    public void sayHello(){
        System.out.println("hello Controller");
    }
}
  • 使用注解注入Bean对象时,bean的 id 默认用 类名的小驼峰 的表示
  • 如果原类名的第一个字母和第二个字母都是大写,那么此时的bean id为原类名不变

bean对象 为什么这样命名?让我们一起来翻看一下源码>> (五大类注解时的命名规则)

下面是添加注解后获取bean对象的示例: (其他四个注解与此用法相同)

public class Application {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
        //bean id为类名改写为小驼峰方式
        UserController userController = context.getBean("userController",UserController.class);
        userController.sayHello();
    }
}

为什么需要五大类注解

既然这5大类注解的用法这么相似,那为啥还要分成5个不同的注解?统一弄成一个注解不好吗?

这就与每个省市都有自己限定名称的车牌号一样,不仅能够节约号码,同时让别人看到能够大致清楚这是哪里的车牌号。我们的类注解也是这样,在项目开发中,一个项目中可能有特别多的类,并且不同的类可能归属于不同的层级,我们这几种类注解就是让程序员看到之后,能够直接了解该类的用途层级。如下图所示:

更详细一点:

  1. @Controller——控制器存储
    存储逻辑控制层的Bean对象。归属于程序的逻辑控制层。前端发送的数据通常会先经过逻辑控制层的控制器来判断传递的参数是否合法
  2. @Service——服务存储
    存储服务层的Bean对象。归属于程序的服务层。前端数据在经过控制器校验合法后将数据传递到服务层,而一个客户服务可能需要操控多张表,我们的服务层就负责调度底层的数据存储层逻辑来进行对应请求服务的处理。
  3. @Repository——仓库存储
    存储数据处理层的Bean对象。归属于持久层。通常情况下每一个表都会对应一个@Repository对象。该对象位于数据处理层,接受服务层的调度,完成对应的业务处理逻辑中涉及到的数据操作,这一层将直接与数据库打交道
  4. @Component——组件存储
    存储具有通用性质的工具类对象,归属于公共工具类,这些工具类提供了一些公共方法。
  5. @Configuration——配置存储
    归属于配置层,用来配置当前项目中的一些信息

(2)方法注解

@Bean注解加到方法上,将方法的返回值添加到容器中

注意: @Bean注解必须结合五大类注解来使用,并且方法必须有返回值

@Component
public class Students {
    @Bean
    public Student s1(){
        Student student = new Student();
        student.setName("小诗诗");
        student.setAge(18);
        return student;
    }
}
public class App1 {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
        Student student = context.getBean("s1",Student.class);
        System.out.println(student);
    }
}

@Bean命名规则:(使用方法注解配合类注解时的命名规则)

  • 当没有设置name属性时,方法名为bean名称
  • 当设置了name属性,只能通过name属性对应的值来获取,使用方法名就获取不到
@Component
public class Students {
    @Bean(name = "student1")
    public Student s1(){
        Student student = new Student();
        student.setName("小诗诗");
        student.setAge(18);
        return student;
    }
}
public class App1 {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
        Student student = context.getBean("student1",Student.class);
        System.out.println(student);
    }
}

 @Bean注解在起名字的时候,也可以起多个名字:

@Bean(name = {"user1","user2","user3"})
public User getUser(){
     User user = new User();
     user.setUsername("张三");
     user.setAge(10);
     return user;
}

(3) 类注解和方法注解的区别

  1. 类注解(如 @Component@Service@Repository@Controller):这种方式告诉 Spring 框架,需要为这个类创建一个 bean 实例,并将其注册到 Spring 容器中。这个类的构造方法(如果有的话)会被自动调用,以创建新的实例。但是,你不能直接在注解中指定任何参数。Spring 将尝试使用容器中可用的 bean 作为参数,进行自动装配。如果需要更复杂的初始化过程,可以使用 @PostConstruct 注解的方法或者实现 InitializingBean 接口。

  2. 方法注解@Bean):这种方式更加灵活。它允许你在一个方法中定义 bean 的创建逻辑,可以包括任意的代码。这个方法可以接收参数,Spring 将尝试使用容器中可用的 bean 作为参数,进行自动装配。这个方法返回的对象将被注册到 Spring 容器中。这种方式特别适合于创建你无法修改的类的实例(例如,来自第三方库的类),或者当你需要在创建 bean 时执行一些自定义逻辑。

2. 对象装配/对象注入  (你的代码可以使用被注入的对象来完成各种任务)

在Spring中,@Autowired@Resource注解用于自动装配(自动注入)Bean。装配方式主要有属性注入、构造方法注入和Setter方法注入。

其中,@Autowired注解可以用于标注 属性注入、Setter注入以及构造方法注入;而@Resource只能用于标注 属性注入和Setter注入,不能用于标注构造方法注入!

(1) 属性注入 (日常开发中使用最多的一种注入方式)

这是最常见的注入方式。在类的字段上直接使用@Autowired@Resource注解,Spring将自动查找并注入匹配的Bean。

@RestController
public class UserController {
    // 属性对象
    @Autowired
    private UserService userService;

    @RequestMapping("/add")
    public UserInfo add(String username, String password) {
        return userService.add(username, password);
    }
}

优点:

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

缺点:

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

(2) Setter方法注入

在类的setter方法上使用@Autowired@Resource注解,Spring将自动查找并注入匹配的Bean。

@RestController
public class UserController {
    // Setter 注入
    private UserService userService;

    @Autowired
    public void setUserService(UserService userService) {
        this.userService = userService;
    }

    @RequestMapping("/add")
    public UserInfo add(String username, String password) {
        return userService.add(username, password);
    }
}

优点:

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

缺点:

  1. 不能注入不可变对象(final 修饰的对象);
  2. 注入的对象可被修改。(通过提供的set方法修改)

原因:final修饰的变量必须直接赋值

(3) 构造方法注入 ( Spring 官方从 4.x 之后推荐的注入方式)

在类的构造方法上使用@Autowired或注解,Spring将自动查找并注入匹配的Bean。

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

@RestController
public class UserController {
    // 构造方法注入
    private UserService userService;

    @Autowired
    public UserController(UserService userService) {
        this.userService = userService;
    }

    @RequestMapping("/add")
    public UserInfo add(String username, String password) {
        return userService.add(username, password);
    }
}

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

1. 可注入不可变对象

2. 注入对象不会被修改:

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

3. 注入对象会被完全初始化:

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

4. 通用性更好

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

(4) @Autowired与@Resource区别

  1. 出身不同:@Resource来自于jdk,@Autowired时Spring框架提供的
  2. 用法不同:@Autowried可以用于属性注入,构造方法注入,Setter注入,@Resource不支持构造方法注入
  3. 参数不同:@Resource支持更多的参数设置,比如name,type,而@Autowried只支持required参数

关于@Autowired@Resource的具体区别,以下是一些主要的点​:

  • @Autowired是Spring框架的注解,它默认通过类型进行注入,然后是按照字段名称进行匹配(类型相同时)。如果Spring上下文中存在多个相同类型的Bean,你需要与@Qualifier注解一起使用来指定Bean的名称。如果没有找到匹配的Bean,它将抛出异常。如果你希望允许为null的值,你可以设置其required属性为false

  • @Resource是Java EE的注解,它默认通过名称进行注入,然后是按照类型进行匹配(名称相同时)。它有两个重要的属性:name和type。如果你使用name属性,那么你使用的是按名称自动注入策略,如果你使用type属性,那么你使用的是按类型自动注入策略。如果既没有指定name也没有指定type,那么默认使用按名称的自动注入策略。如果没有找到匹配的Bean,那么会回退到按类型匹配,并自动注入如果找到匹配的Bean.

  • @Resource的装配顺序是:如果指定了name和type,那么只有在Spring上下文中找到唯一匹配的Bean才会装配,如果没有找到,将抛出异常。如果指定了name,将装配上下文中匹配name(ID)的Bean,如果没有找到,将抛出异常。如果指定了type,当在上下文中找到唯一匹配的Bean类型时进行装配,如果没有找到或找到多个,将抛出异常。如果既没有指定name也没有指定type,装配将自动按名称进行,如果没有找到匹配,回退是按原始类型匹配,如果找到匹配,将自动装配。

 (5)  注入异常问题

当我们的ioc容器中存在了多个相同类型bean对象,再通过上方的注入方式来获取的话,程序就会报异常!

在spring容器中找bean有两种方式:

  1. 根据bean的名称也就是bean id
  2. 根据bean的类型也就是bean class

先创建一个Cat类,属性有name和color,并提供get,set,toString方法

往Spring容器中存入多个Cat对象 (方法注入)

@Controller
public class Cats {
    @Bean
    public Cat cat1(){
        Cat cat = new Cat();
        cat.setName("糯米");
        cat.setColor("白色");
        return cat;
    }
    
    @Bean
    public Cat cat2(){
        Cat cat = new Cat();
        cat.setName("汤圆");
        cat.setColor("黑色");
        return cat;
    }
}

在另一个类中获取Cat对象 (对象注入,你的代码可以使用被注入的对象来完成各种任务)

@Controller
public class CatController {

    @Autowired
    private Cat cat;

    public void getCat(){
        System.out.println(cat);
    }
}

创建启动类

public class App3 {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
        CatController catController = context.getBean(CatController.class);
        catController.getCat();
    }
}

发现报错了,因为Spring中已经注入了两个Cat对象,所以此时不知道获取哪个对象 

 @Autowired注解默认它默认通过类型进行注入,但是在匹配过程中发现了两个相同的bean对象,然后按照字段名称进行匹配,发现找不到所以报错了!!!

三种解决方案

  1. 精确的描述bean的名称(将注入的名称写对)
  2. 使用@Rsource设置name方式来重命名注入对象
  3. 使用@Autowired+@Qualifier来筛选bean对象

 方案一:精确的描述bean的名称(将注入的名称写对)

@Controller
public class CatController {

    @Autowired
    private Cat cat1; //精确描述bean名称

    public void getCat(){
        System.out.println(cat1);
    }
}

方案二: 使用@Rsource设置name方式来重命名注入对象 

@Controller
public class CatController {

    @Resource(name = "cat2")
    private Cat cat;

    public void getCat(){
        System.out.println(cat);
    }
}

 方案三:使用@Autowired+@Qualifier来筛选bean对象

@Controller
public class CatController {

    @Autowired
    @Qualifier(value = "cat1")
    private Cat cat;

    public void getCat(){
        System.out.println(cat);
    }
}

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

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

相关文章

天黑的时候如果下雨了,会比平常更亮一些

目录 一、最近的感受 二、自我的审视 三、如何变得强大 1.保持善良 2.不过度追求公平 3.在痛苦中找到自己的意义 4.令人振奋的生命力 四、情绪调节中的个人见解及如何处理情绪后的学习 1.运动 2.散步 3.找好朋友倾诉 五、总结 一、最近的感受 天黑的时候如果下雨了…

设计模式(十一):结构型之组合模式

设计模式系列文章 设计模式(一)&#xff1a;创建型之单例模式 设计模式(二、三)&#xff1a;创建型之工厂方法和抽象工厂模式 设计模式(四)&#xff1a;创建型之原型模式 设计模式(五)&#xff1a;创建型之建造者模式 设计模式(六)&#xff1a;结构型之代理模式 设计模式…

C语言:写一个代码,使用 试除法 打印100~200之间的素数(质数)

题目&#xff1a; 使用 试除法 打印100~200之间的素数。 素数&#xff08;质数&#xff09;&#xff1a;一个数只能被写成一和本身的积。 如&#xff1a;7只能写成1*7&#xff0c;那就是素数&#xff08;质数&#xff09;了。 思路一&#xff1a;使用试除法 总体思路&#xff…

HTML5 介绍

目录 1. HTML5介绍 1.1 介绍 1.2 内容 1.3 浏览器支持情况 2. 创建HTML5页面 2.1 <!DOCTYPE> 文档类型声明 2.2 <html>标签 2.3 <meta>标签 设置字符编码 2.4 引用样式表 2.5 引用JavaScript文件 3. 完整页面示例 4. 资料网站 1. HTML5介绍 1.1 介绍 …

带你手撕一颗红黑树

红黑树&#xff08;C&#xff09; 红黑树简述红黑树的概念红黑树的性质红黑树结点定义 一&#xff0c;红黑树的插入插入调整插入代码 二&#xff0c;红黑树的验证三&#xff0c;红黑树的删除待删除的结点只有一个子树删除结点颜色为红色删除结点颜色为黑色 删除的结点为叶子节点…

直流稳压电源与信号产生电路(模电速成)

目录 一、直流稳压电源 1、直流稳压电路 2、串联型稳压电路 3、集成稳压电路 二、信号产生电路 1、振荡电路 2、波形发生器 一、直流稳压电源 1、直流稳压电路 直流电源由 变压器、整流、滤波、稳压 四部分组成 整流&#xff1a;将交流变为直流 滤波&#xff1a;减小…

AI人工智能之科研论文搜索集锦

AI人工智能之科研论文搜索集锦 前言1. Google学术搜索2. Google搜索3. Arxiv#Example&#xff1a; 4. Github#Example&#xff1a; 5. Paperwithcode6. Connectedpapers7. OpenReview 总结 前言 如今越来越多领域都会与计算机、人工智能方面进行跨领域融合&#xff0c;一个万物…

帮忙投票的链接怎么弄的微信怎么创建投票链接设置投票

近些年来&#xff0c;第三方的微信投票制作平台如雨后春笋般络绎不绝。随着手机的互联网的发展及微信开放平台各项基于手机能力的开放&#xff0c;更多人选择微信投票小程序平台&#xff0c;因为它有非常大的优势。 1.它比起微信公众号自带的投票系统、传统的H5投票系统有可以图…

EMC学习笔记(二)模块划分及特殊器件的布局

模块划分及特殊器件的布局 1.模块划分1.1 按功能划分1.2 按频率划分1.3 按信号类型划分1.4 综合布局 2.特殊器件的布局2.1 电源部分2.2 时钟部分2.3 电感线圈2.4 总线驱动部分2.5 滤波器件 谈PCB的EMC设计,不能不谈PCB的模块划分及关键器件的布局。这一方面是某些频率发生器件、…

day51_mybatis

今日内容 零、 复习昨日 一、缓存 二、单例设计模式 零、 复习昨日 多表联查的时候 扩展类写接口设计方法写sql语句 不能直接映射成实体类resultMap 一对一 axxxxxxx一对多 collection 一、$和#的区别 使用# 使用$ 总结: #{} 相当于是预处理语句,会将#换成占位符?,字符串等…

【c语言进阶】深入挖掘数据在内存中的存储

深入挖掘数据在内存中的存储 数据类型介绍数据类型基本分类及其大小 整形在内存中的存储方式原码、反码、补码大小端介绍判断一个系统是大端还是小端 char与unsigned char值范围与图解整形存储相关练习题 浮点数在内存中的存储方式浮点数存储规则案列 结语 铁汁们&#xff0c;今…

计算机网络填空题

我会写下自己的答案和理解 希望自己可用在学习中体会到快乐&#xff0c;而不是麻木。 1. 网络协议三要素中语义是指 需要发出何种控制信息&#xff0c;完成何种动作以及做出何种响应 1.在计算机网络中要做到有条不紊的交换数据&#xff0c;就必须遵守一些事…

算法刷题-链表-移除链表元素

链表操作中&#xff0c;可以使用原链表来直接进行删除操作&#xff0c;也可以设置一个虚拟头结点再进行删除操作&#xff0c;接下来看一看哪种方式更方便。 203.移除链表元素 力扣题目链接 题意&#xff1a;删除链表中等于给定值 val 的所有节点。 示例 1&#xff1a; 输入&…

红黑树(Red Black Tree)基本性质 + 建树

定义 红黑树&#xff1a;一种特殊的二叉搜索树 二叉搜索树&#xff1a;一种树的类型&#xff0c;每个节点最多有两个子节点&#xff0c;其中其左节点一定小于当前节点&#xff0c;右节点一定大于当前节点 二叉树的缺点&#xff1a;如果给定的初始序列顺序不好&#xff0c;可能…

算法刷题-链表-删除链表的倒数第N个节点

删除链表的倒数第N个节点 19.删除链表的倒数第N个节点思路其他语言版本 19.删除链表的倒数第N个节点 力扣题目链接 给你一个链表&#xff0c;删除链表的倒数第 n 个结点&#xff0c;并且返回链表的头结点。 进阶&#xff1a;你能尝试使用一趟扫描实现吗&#xff1f; 示例 1…

微服务_fegin

Feign服务调用 是客户端组件 ruoyi系统中Log\Auth\User用了远程服务调用&#xff0c;用工厂模式给他的报错加了层工厂类&#xff0c;return错误的时候重写了以下方法。 在ruoyi-common-core模块中引入依赖 <!-- SpringCloud Openfeign --><dependency><group…

springboot不香吗?为什么还要使用springcloud--各个组件基本介绍(Feign,Hystrix,ZUUL)

1.Feign负载均衡简介 1.1 Feign是什么 Feign是一个声明式WebService客户端。使用Feign能让编写Web Service客户端更加简单, 它的使用方法是定义一个接口&#xff0c;然后在上面添加注解&#xff0c;同时也支持JAX-RS标准的注解。Feign也支持可拔插式的编码器和解码器。Spring…

07-根据Hutool工具的JWT实现单点登录功能

1、两种单点登录方案 1.1、使用token + Redis 实现单点登录(一般不用了) 1.2、使用JWT实现单点登录 2、 JWT单点登录的原理和JWT存在的问题及解决方案讲解 2.1、JWT结构 > Header 头部信息,主要声明了JWT的签名算法等信息。 > Payload 载荷信息,主要承载了各种声明并…

闲置APP小程序开发 你不喜欢的可能正是别人需要的

生活中我们常常会产生各种闲置物品&#xff0c;尤其是对于有宝宝的家庭来说&#xff0c;孩子小的时候可能会添置各种玩具、婴儿车或者是别的用品&#xff0c;随着孩子渐渐长大&#xff0c;这些东西都用不上了&#xff0c;但是扔了又觉得很可惜&#xff0c;留着又占地方&#xf…

给编程初学者写一篇简单的文章,快速查询本机ip,黑别人电脑的基基础

给编程小白写一篇简单的文章&#xff0c;快速查询本机ip 首先&#xff0c;按下win徽标键r键 &#xff08;先按win键再按r键&#xff09; 出现一个小窗口 在里面输入cmd 然后&#xff0c;你将得到一个cmd窗口 在里面输入ipconfig&#xff0c;然后按回车 就可以得到ip地址了 如…