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

news2024/11/17 17:43:51

经过前面的学习,我们已经可以实现基本的 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、使用 bean
        service.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、使用 bean
        repository.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、使用 bean
        component.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、使用 bean
        config.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)); // userController
        System.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 {
    @Bean
    public 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 {
    @Autowired
    private 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;
     
     @Autowired
     public 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;

    @Autowired
    public 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 {
    @Resource
    public 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;
    }
    
    @Bean
    public 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、使用 bean
        controller.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 {
    @Resource
    public 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.coloradmin.cn/o/804178.html

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

相关文章

25.8 matlab里面的10中优化方法介绍—— 拉各朗日乘子法求最优化解(matlab程序)

1.简述 拉格朗日乘子法&#xff1a; 拉格朗日乘子法&#xff08;Lagrange multipliers&#xff09;是一种寻找多元函数在一组约束下的极值的方法。通过引入拉格朗日乘子&#xff0c;可将有 变量与 约束条件的最优化问题转化为具有变量的无约束优化问题求解 举个例子&#xff…

YOLOv5:使用7.0版本训练自己的实例分割模型(车辆、行人、路标、车道线等实例分割)

YOLOv5&#xff1a;使用7.0版本训练自己的实例分割模型&#xff08;车辆、行人、路标、车道线等实例分割&#xff09; 前言前提条件相关介绍使用YOLOv5-7.0版本训练自己的实例分割模型YOLOv5项目官方源地址下载yolov5-7.0版源码解压目录结构 准备实例分割数据集在./data目录下&…

使用python库uvicorn替代Nginx发布Vue3项目

目录 一、Vue3项目打包 二、将打包文件放到python项目 三、配置uvicorn服务 四、启动服务 【SpringBoot版传送门&#xff1a;使用SpringBoot替代Nginx发布Vue3项目_苍穹之跃的博客-CSDN博客】 一、Vue3项目打包 &#xff08;博主vue版本&#xff1a;3.2.44&#xff09; 由…

论文精读之BERT

目录 1.摘要&#xff08;Abstract&#xff09; 2.引言&#xff08;Introduction&#xff09;&#xff1a; 3.结论&#xff08;Conlusion&#xff09;&#xff1a; 4.BERT模型算法: 5.总结 1.摘要&#xff08;Abstract&#xff09; 与别的文章的区别是什么:BERT是用来设计去…

Acwing.285 没有上司的舞会(动态规划)

题目 Ural大学有N名职员&#xff0c;编号为1~N。 他们的关系就像—棵以校长为根的树&#xff0c;父节点就是子节点的直接上司。每个职员有一个快乐指数&#xff0c;用整数H给出&#xff0c;其中1≤i≤N。 现在要召开一场周年庆宴会&#xff0c;不过&#xff0c;没有职员愿意和…

js通过下标获取对象值

var a {1: a,2: b,3: c,4: d}var keys Object.keys(a)var values Object.values(a)console.log(keys , values)# 建议使用 console.log(Object.keys(a)[2] : Object.values(a)[2])#无意义 console.log(Object.keys(a)[3] : a[Object.keys(a)[3]])

下级平台级联视频汇聚融合平台EasyCVR,层级显示不正确的原因排查

视频汇聚平台安防监控EasyCVR可拓展性强、视频能力灵活、部署轻快&#xff0c;可支持的主流标准协议有GB28181、RTSP/Onvif、RTMP等&#xff0c;以及厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等&#xff0c;能对外分发RTSP、RTMP、FLV、HLS、WebRTC等…

CAN转EtherNet/IP网关can协议是什么意思

你是否曾经遇到过不同的总线协议难以互相通信的问题&#xff1f;远创智控的YC-EIP-CAN网关为你解决了这个烦恼&#xff01; 远创智控YC-EIP-CAN通讯网关是一款自主研发的设备&#xff0c;它能够将各种CAN总线和ETHERNET/IP网络连接起来&#xff0c;解决不同总线协议之间的通信…

C语言队列实现参考示例

C语言队列实现参考示例 目录 C语言队列实现参考示例前言1 代码实现1.1 实现方案1.2 代码编写 结束语 前言 队列是一种特殊的线性表&#xff0c;特殊之处在于它只允许在表的前端&#xff08;front&#xff09;进行删除操作&#xff0c;而在表的后端&#xff08;rear&#xff09;…

Java 多线程练习

目录 1.定时器操作&#xff08;实现电脑定时关机&#xff09;。 2. 每个月的月末(02:00:00) 执行一次代码 3. 模拟售票 4. 用15个线程实现&#xff0c;求123456789 之间放-和为100的表达式&#xff08;11个结果&#xff09;&#xff0c;如果一个线程求出结果&#xff0c; 立…

【LeetCode】383. 赎金信

题目&#xff1a;383. 赎金信 由于此题只含有小写字母,并且magazine里面的字母不可重复使用. 故首先用一个长度为26的整形数组记录magazine里字母出现的次数。 再用这个整形数组跟ransomeNote进行遍历比较&#xff0c;当数组中出现-1时&#xff0c;说明false,否则true. 代码&am…

大数据Flink(五十二):Flink中的批和流以及性能比较

文章目录 Flink中的批和流以及性能比较 ​​​​​​​​​​​​​​一、Flink中的批和流

python与深度学习(九):CNN和cifar10

目录 1. 说明2. cifar10实战2.1 导入相关库2.2 加载数据2.3 数据预处理2.4 数据处理2.5 构建网络模型2.6 模型编译2.7 模型训练2.8 模型保存2.9 模型评价2.10 模型测试2.11 模型训练结果的可视化 3. cifar10的CNN模型可视化结果图4. 完整代码5. 改进后的代码和结果 1. 说明 本…

史上最强,Python自动化测试框架整理,搭建框架看这篇就够了...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 什么是测试框架呢…

明星代言注意事项:确保代言活动成功的关键要素

对于商家而言&#xff0c;聘请明星代言自己的品牌产品&#xff0c;无非就是为了利用明星的人气为品牌加持热度&#xff0c;吸引明星粉丝和消费者的关注&#xff0c;增加产品转化&#xff0c;塑造品牌形象。明星代言是一项重要的市场营销策略&#xff0c;但同时也需要注意一些关…

美化与保护合二为一:3个功能厉害的水印app

每个人都有自己珍视的照片回忆&#xff0c;但在互联网上共享这些照片时&#xff0c;担心他人未经允许使用它们是很常见的。幸运的是&#xff0c;现在有了加水印软件&#xff0c;我们可以以有效的方式保护我们的珍贵照片。通过在图片上添加个性化的水印&#xff0c;你可以在不影…

textarea文本高亮选中

最近在实现原文/译文句段高亮对比显示&#xff0c;和有道翻译类似&#xff0c;如下图所示&#xff1a; 最初的解决方案是采用富文本编辑器&#xff0c;把所有句段信息都用HTML标签包裹&#xff0c;操作空间比较大&#xff0c;页面上需要的功能几乎都可以实现&#xff0c;但是由…

串口通讯接口类型:TTL、RS232和RS485(电平标准)

串口通讯接口类型&#xff1a;TTL、RS232和RS485 在串口通信中&#xff0c;常用的接口类型包括TTL、RS-232和RS-485&#xff0c;TTL、RS-232、RS422、RS-485是指的电平标准(电信号)。 通信协议规定了数据传输的规则和格式&#xff0c;包括数据的起始位、停止位、数据位数、校…

docker 搭建jenkins

1、拉取镜像 docker pull jenkins/jenkins:2.4162、创建文件夹 mkdir -p /home/jenkins_mount chmod 777 /home/jenkins_mount3、运行并构建容器 docker run --restartalways -d -p 10240:8080 -p 10241:50000 -v /home/jenkins_mount:/var/jenkins_home -v /etc/localtime:…

从零开始构建基于YOLOv5的目标检测系统

本博文从零开始搭建基于YOLOv5模型的目标检测系统&#xff08;具体系统参考本博主的其他博客&#xff09;&#xff0c;手把手保姆级完成环境的搭建。 &#xff08;1&#xff09;首先Windows R输入cmd命令后打开命令窗口&#xff0c;进入项目目录&#xff0c;本博文以野生动物…