Spring 中简单存取 Bean 的相关注解

news2024/11/25 20:38:39

目录

  • 前言
  • 存储 Bean 对象
    • 五大类注解
    • 方法注解(@Bean)
  • 获取 Bean 对象 (@Autowired)
    • 属性注入
      • 多个同类型 Bean 注入怎么办?
    • Setter 注入
    • 构造方法注入(官方推荐)

前言

之前我们存储获取 Bean 的操作很繁琐,需要将 Bean 放入 xml 文件中,获取 Bean 还必须得获取上下文,就很麻烦。

其实这些都可以简化,在 Spring 中想要更简单的存储和读取对象,核⼼是使⽤注解。

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

  1. 类注解:@Controller、@Service、@Repository、@Component、@Configuration。
  2. ⽅法注解:@Bean

获取对象的实现⽅法有以下 3 种:

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

存储 Bean 对象

五大类注解

五大类注解有:
@Controller(控制器)校验参数的合法性,相当于安检系统
@Service(服务)业务组装,相当于客服中心
@Repository(数据持久层)实际业务处理,就是实际办理的业务
@Component(组件)工具类层,基础工具
@Configuration(配置层)配置

首先,我们得配置一下 xml 文件:

在这里插入图片描述

注意:使用类注解存储 bean,和使用 xml 存储 bean 是可以混用的。

User 类:

//使用类注解
@Controller
public class User {
    public void sayHi(String name) {
        System.out.println("hello " + name);
    }
}
public class App {
    public static void main(String[] args) {
        //得到 spring 上下文
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
        //得到 bean 对象
        User user = (User) context.getBean("user");
        user.sayHi("张三");
    }
}

在这里插入图片描述

关于 getBean 后面应该填什么?
对于不同的类名有不同的写法,如果首字母大写,第二个字母小写,那么 bean 的名称就是类名首字母小写;如果不满足首字母大写第二字母小写,则使用原类名。

当然了,我们也可以在类注解后面设置 id,通过 id 来得到 bean 对象。

//id 可以随便取, 两种方式都可
//@Controller(value = "666")
@Controller("666")
public class User {
    public void sayHi(String name) {
        System.out.println("hello " + name);
    }
}
public class App {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
        User user = (User) context.getBean("666");
        user.sayHi("张三");
    }
}

来看看五大类注解间的关系:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这些注解⾥⾯都有⼀个注解 @Component,说明它们本身就是属于 @Component 的“⼦类”。
所以它们在使用上都差不多,那为什么还要分出 5 大类呢?
直接一个 @Component 就可以 存储 bean 对象了,还要分这么多有什么用呢?

其实是让程序员看到类注解之后,就能直接了解当前类的⽤途,比如我们看到一辆车的车牌号就知道它的归属地是哪,这个也一样。

程序的⼯程分层,调⽤流程如下:
在这里插入图片描述

方法注解(@Bean)

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

Student 类:

//普通类
public class Student {
    private String name;
    private int id;
    private int score;

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", id=" + id +
                ", score=" + score +
                '}';
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public int getScore() {
        return score;
    }

    public void setScore(int score) {
        this.score = score;
    }
}

class 类:

//注意: 方法注解要配合五大类注解一起使用
//     该类也存储在 IoC 容器中
@Controller
public class Class {

    //使用方法注解, 将方法的返回值存储到 IoC 容器中
    @Bean
    public Student student() {
        Student student = new Student();
        student.setId(6);
        student.setName("张三");
        student.setScore(150);
        return student;
    }
}

获取 bean 对象:

public class App {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
        //注意:@Bean 默认命名是方法名
        // 也就是添加方法注解的那个方法名
        Student student = context.getBean("student", Student.class);
        System.out.println(student.toString());
    }
}

在这里插入图片描述
@Bean 还有几种重命名方式:

    @Bean("aaa")
    @Bean(value = "sss")
    @Bean(name = "nnn")

点入 @Bean :

在这里插入图片描述
发现 name 和 value 效果是一样的。

除了取单个名称外,还可以取多个名称:

    @Bean(value = {"sss","888"})
    @Bean(name = {"sss","888"})
    @Bean({"sss","888"})

注意:

  1. 如果重命名了,那么获取 bean 对象时,默认的方法获取 bean 对象的方式就不能用了。
  2. 添加了 @Bean 注解的方法无法传参(方法的调用是程序在控制),也就不能重载。
  3. 如果多个 Bean 使用相同的名称,那么程序执行不会报错,但是第一个 Bean 后的对象不会被放到容器中,也就是只有第一次创建 Bean 的时候会将对应的 Bean 名称关联起来,后续再有相同名称的 Bean 存储的时候,容器会自动忽略。

获取 Bean 对象 (@Autowired)

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

属性注入

属性注⼊是使⽤ @Autowired 实现的:

普通类 User:

public class User {
    private int id;
    private String name;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

添加类注解的 UserService:

@Service
public class UserService {
    public User getUser(int id, String name) {
        User user = new User();
        user.setId(id);
        user.setName(name);
        return user;
    }
}

添加类注解的 UserController:

@Controller
public class UserController {

	//使用属性注入,将IoC容器中的UserService类型的对象
	//注入变量 userService中
    @Autowired
    private UserService userService;

    public User getUser(int id, String name) {
        return userService.getUser(id,name);
    }
}

注意:下面我们不使用 @Autowired 来进行依赖注入,因为 main 函数是静态方法,@Autowired 又不能给局部变量注入,那就只能放在类里面方法外面了,再加上 static 修饰 才能在 main 里访问,但可惜的是静态类属于方法的一部分,static 成员是不能使用 @Autowired 来注入。

public class Test {
    public static void main(String[] args) {
    	//依赖查找
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");
        UserController userController = context.getBean("userController", UserController.class);
        //检验是否得到了 UserController 类型对象
        System.out.println(userController.getUser(3,"张三").toString());
    }
}

在这里插入图片描述

依赖查找 VS 依赖注入
依赖查找:依赖 Bean 的名称来获取 Bean 对象。
依赖注入:通过注解 @Autowired 根据对象类型进行依赖注入,首先根据 getType 从容器中获取对象,如果 IoC 容器中只有一个该类型对象,则直接注入到当前属性上;如果容器中有多个该类型对象,则会使用 getName (根据名称)进行匹配。(具体体现如下)

多个同类型 Bean 注入怎么办?

问题:如若有多个同类型的 Bean 对象存储进 IoC 容器中,那么我们该如何准确获取该类型对象?

问题场景:

Users 类:

@Service
public class Users {

	//通过方法注解,添加两个同类型 bean
    @Bean("user1")
    public User user1() {
        User user = new User();
        user.setName("李四");
        user.setId(22);
        return user;
    }

    @Bean("user2")
    public User user2() {
        User user = new User();
        user.setName("王五");
        user.setId(88);
        return user;
    }
}

UserService 类:

@Service
public class UserService {

	//对 user 进行属性注入
    @Autowired
    User user;

    public void add() {
    	//拿到 user 后打印内容,判断是哪个 user
        System.out.println(user.toString());
    }
}

Test 类:

public class Test {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");
        UserService userService = context.getBean("userService", UserService.class);
        userService.add();
    }
}

看结果:
在这里插入图片描述
它是说期望有一个 bean 进行匹配,但出现了两个:user1、user2

解决问题:

  1. 将属性的名字和 Bean 的名字对应上。

就比如我要得到的是 王五 这个对象,那我只需要将下面两点的名称对应上即可:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  1. @Autowired 配合 @Qualifier 使用

这个就是在注入时,声明要注入的 bean 名称
在这里插入图片描述

属性注入分析:
优点: 使用简单
缺点:

  1. 无法注入 final 修饰的变量
  2. 只适用于 IoC 容器
  3. 容易违背单一设计原则

Setter 注入

我们写类属性时,可以对这些属性生成相应的 get 和 set 方法,Setter 注入就是针对 set 方法,进行的注入:

User 类:

@Service
public class Users {

        @Bean("user")
        public User getUser() {
            User user = new User();
            user.setName("老王");
            user.setId(26);
            return user;
        }
}

UserService 类:

@Service
public class UserService {

    private User user;

	//对类属性 user 进行 Setter 注入
    @Autowired
    public void setUser(User user) {
        this.user = user;
    }

    public void add() {
        System.out.println(user.toString());
    }
}
public class Test {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");
        UserService userService = context.getBean("userService", UserService.class);
        userService.add();
    }
}

在这里插入图片描述

注意:@Autowired 不能省略

Setter 注入分析:
优点:通常 Setter 只是注入一个属性,所以 Setter 更符合单一设计原则。
缺点:

  1. 无法注入一个 final 修饰的变量
  2. Setter 注入的对象可以被修改。(setter 是一个方法,既然是方法就可能被多次调用和修改)

构造方法注入(官方推荐)

构造⽅法注⼊是在类的构造⽅法中实现注⼊,如下代码所示:

只对 UserService 进行修改:

@Service
public class UserService {

    private User user;
	
	//构造方法注入
    @Autowired
    public UserService(User user) {
        this.user = user;
    }

    public void add() {
        System.out.println(user.toString());
    }
}

注意:如果只有一个构造方法,不写 @Autowired 也可以,但若是有多个构造方法,就得加上 @Autowired,表明是哪个构造方法需要注入。

构造方法注入分析:
优点:

  1. 可以注入一个 final 修饰的变量
  2. 注入的变量不会被修改,因为构造方法只加载一次
  3. 构造方法注入可以保证注入对象完全初始化
  4. 构造方法注入通用性更好

缺点:

  1. 写法比属性注入更复杂
  2. 使用构造方法注入,无法解决循环依赖的问题

其实 @Resource 的功能和 @Autowired 差不多,那它俩有啥区别呢?

@Autowired 与 @Resource 的区别:

  1. 出生不同:@Resource 来自 JDK,@Autowired 来自 Spring 框架
  2. 支持参数不同:@Resource 支持很多参数设置,@Autowired 只支持一个参数设置
  3. 使用上的区别:@Resource 不支持构造方法注入,@Autowired 支持构造方法注入
  4. idea 兼容性支持不同:使用 @Autowired 在 idea 专业版下可能会误报,@Resource 不存在误报问题(@Resource 相当于是亲儿子了)

关于第四点:因为@Autowired 来自 Spring 框架,@Resource 来自 JDK,所以执行顺序有差异,Spring 框架的执行是在 java 程序执行之后的,当我们使用 @Autowired 时,它不能检测到 IoC 容器中是否有这个类型的 Bean,所以就报错(运行起来不影响结果);使用 @Resource 的话,执行顺序是靠前的,它知道这个 Bean 是否存在。

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

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

相关文章

在职硕士|2023级中国社科院-美国杜兰大学合办双证能源管理硕士(MME)

金融硕士 在职硕士|2023级中国社科院-美国杜兰大学合办双证能源管理硕士(MME) 中国社会科学院大学与美国杜兰大学合作举办的能源管理专业硕士学位教育项目(UCASS-Tulane Master of Management in Energy,简称MME)于2…

《人工智能安全》课程总体结构

1 课程内容 人工智能安全观:人工智能安全问题、安全属性、技术体系等基本问题进行了归纳整理。人工智能安全的主要数据处理方法,即非平衡数据分类、噪声数据处理和小样本学习。人工智能技术赋能网络空间安全攻击与防御:三个典型实例及攻击图…

mybatis_配置之属性优化

概念 别名优化: 类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。例如: 在xml文件里为SQL映射文件中定义返回值类型的属性起个别名 之后直接使用User进行使用 核心配置文件: MyBa…

常见面试题分享1

一、对JVM的了解 1.1 什么是JVM? JVM(Java Virtual Machine),俗称Java虚拟机。它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。Java语言的一个非常重要的特点就是与平台的无关性。…

商城体系之产商品系统

本文主要讲解商城体系下产商品系统的设计。商城系统可以拆分成多个业务中台和多个应用服务。 1、产商品系统业务架构 产商品系统作为商城重要的基础信息组成部分,主要划分为产品信息和商品信息,产品信息保持最原始的产品基础属性和内容,商品…

下拉框可筛选可树状多选组件

实际效果图片 父页面 <el-form-item label"转发&#xff1a;" :label-width"formLabelWidth" class"formflex_item"><el-select ref"select" :clearable"true" clear"clearSelect" remove-tag"r…

day2 驱动开发 c语言

通过驱动开发给pcb板子点灯。 u-boot已经提前移植到了emmc中。 灯也是一种字符型设备。 编程流程需要先注册设备&#xff0c;然后创建结点&#xff0c;然后操作电灯相关寄存器 应用层直接调用read write来打开字符设备进行操作。 这样写会造成无法处理内核页面请求的虚拟地址…

SpringBoot中java操作excel【EasyExcel】

EasyExcel 处理Excel&#xff1b;简单记录&#xff0c;方便日后查询&#xff01; 官方文档&#xff1a; Easy Excel (alibaba.com) 一、EasyExcel概述 Java解析、生成Excel比较有名的框架有Apache poi、jxl。但他们都存在一个严重的问题就是非常的耗内存&#xff0c;poi有一套…

前端食堂技术周刊第 91 期:2023 npm 状态、TC39 会议回顾、浏览器中的 Sass、React 18 如何提高应用程序性能

美味值&#xff1a;&#x1f31f;&#x1f31f;&#x1f31f;&#x1f31f;&#x1f31f; 口味&#xff1a;茶椰生花 食堂技术周刊仓库地址&#xff1a;https://github.com/Geekhyt/weekly 大家好&#xff0c;我是童欧巴。欢迎来到前端食堂技术周刊&#xff0c;我们先来看下…

js基础-练习三

九九乘法表&#xff1a; <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthsc, initial-scale1.0"><title>九九乘法表</title><style&g…

5.9 Bootstrap 警告框(Alert)插件

文章目录 Bootstrap 警告框&#xff08;Alert&#xff09;插件用法选项方法事件 Bootstrap 警告框&#xff08;Alert&#xff09;插件 警告框&#xff08;Alert&#xff09;消息大多是用来向终端用户显示诸如警告或确认消息的信息。使用警告框&#xff08;Alert&#xff09;插件…

基于 Flink SQL CDC 数据处理的终极武器

文章目录 一、传统的数据同步方案与 Flink SQL CDC 解决方案1.1 Flink SQL CDC 数据同步与原理解析1.2 基于日志的 CDC 方案介绍1.3 选择 Flink 作为 ETL 工具 二、 基于 Flink SQL CDC 的数据同步方案实践2.1 CDC Streaming ETL2.2 Flink-CDC实践之mysql案例 来源互联网多篇文…

Redis—分布式系统

Redis—分布式系统 &#x1f50e;理解分布式&#x1f50e;分布式—应用服务与数据库服务分离引入更多的应用服务节点理解负载均衡 引入更多的数据库服务节点缓存分库分表 微服务 &#x1f50e;常见概念应用(Application) / 系统(System)模块(Module) / 组件(Component)分布式(D…

nvm 安装 Node 报错:panic: runtime error: index out of range [3] with length 3

最近在搞 TypeScript&#xff0c;然后想着品尝一下 pnpm&#xff0c;但是 pnmp 8.x 最低需要 Node 16.x&#xff0c;但是电脑上暂时还没有该版本&#xff0c;通过 nvm list available 命令查看可用的 Node 版本&#xff1a; nvm list available# 显示如下 | CURRENT | …

【C++进阶】:继承

继承 一.继承的概念和定义1.概念2.定义 二.基类和派生类对象赋值转换三.继承中的作用域四.派生类的默认成员函数五.继承与友元六.继承与静态成员七.复杂的菱形继承及菱形虚拟继承1.二义性2.原理 八.总结 一.继承的概念和定义 1.概念 继承(inheritance)机制是面向对象程序设计使…

虚拟文件描述符VFD

瀚高数据库 目录 环境 文档用途 详细信息 环境 系统平台&#xff1a;Linux x86-64 Red Hat Enterprise Linux 7 版本&#xff1a;14 文档用途 了解VFD 详细信息 1.相关数据类型 typedef struct vfd{int fd; /* current FD, or VFD_CLOSED if non…

23 自定义控件

案例&#xff1a;组合Spin Box和Horizontal Slider实现联动 新建Qt设计师界面&#xff1a; 选择Widget&#xff1a; 选择类名&#xff08;生成.h、.cpp、.ui文件&#xff09; 在smallWidget.ui中使用Spin Box和Horizontal Slider控件 可以自定义数字区间&#xff1a; 在主窗口w…

第17章 常见函数

创建函数 第一种格式采用关键字function&#xff0c;后跟分配给该代码块的函数名。 function name {commands }第二种 name() { commands }你也必须注意函数名。记住&#xff0c;函数名必须是唯一的&#xff0c;否则也会有问题。如果你重定义了函数&#xff0c;新定义会覆…

【时间复杂度】

旋转数组 题目 给定一个整数数组 nums&#xff0c;将数组中的元素向右轮转 k 个位置&#xff0c;其中 k 是非负数。 /* 解题思路&#xff1a;使用三次逆转法&#xff0c;让数组旋转k次 1. 先整体逆转 // 1,2,3,4,5,6,7 // 7 6 5 4 3 2 1 2. 逆转子数组[0, k - 1] // 5 6 7 4 3…

C语言基本结构:顺序、选择和循环

文章目录 前言顺序结构代码讲解 选择结构代码讲解 循环结构总结 前言 在计算机编程中&#xff0c;掌握基本的编程结构是非常重要的。C语言作为一种广泛应用的编程语言&#xff0c;具有丰富的基本结构&#xff0c;包括顺序结构、选择结构和循环结构。这些基本结构为开发人员提供…