Spring Boot 学习之路 -- Service 层

news2024/9/30 19:30:50

前言

  1. 最近因为业务需要,被拉去研究后端的项目,代码框架基于 Spring Boot,对我来说完全小白,需要重新学习研究…
  2. 出于个人习惯,会以 Blog 文章的方式做一些记录,文章内容基本来源于「 Spring Boot 从入门到精通(明日科技) 」一书,做了一些整理,更易于个人理解和回顾查找,所以大家如果希望更系统性的学习,可以阅读此书(比较适合我这种新手)。

一、Service 层与 @Service 注解

在实际开发中,Service 层主要负责业务模块的逻辑应用设计。在设计 Service 层的过程中,首先设计接口,然后设计接口的实现类。通常情况下,Service 层用于封装项目中一些通用的业务逻辑,这么做的好处是有利于业务逻辑的独立性和重复利用性。因此,为了处理一个 Spring Boot 项目中的业务逻辑,Service 层是不可或缺的。

Spring Boot 中的 Service 层是业务逻辑层,其作用是处理业务需求,封装业务方法,执行 Dao 层中用于访问、处理数据的操作。Service 层通常由一个接口和这个接口的实现类组成。其中,Service 层的接口可以在 Controller 层中被调用,用于实现数据的传递和处理;Service 层的实现类须使用 @Service 注解予以标注。

说明:

Dao 层介于 Service 层和数据库之间,用于访问、操作数据库中的数据。Dao 层通常由 Dao 接口、Dao 实现类和 Dao 工厂类这 3 个部分组成。在 Dao 接口中,定义了一系列用于访问、操作数据库中数据的方法。在 Dao 实现类中,实现了 Dao 接口中的方法。Dao 工厂类的作用是返回一个 Dao 实现类的对象。

Controller 层的作用是通过调用 Service 层的接口,控制各个业务模块的业务流程。Controller 层通过解析用户通过 URL 地址发送的请求,调用不同的 Service 层的接口以处理这个请求,把处理结果返回给客户端。

在 Spring Boot 中,把被 @Service 注解标注的类称作「服务类」。@Service 注解属于 Component 组件,可以被 Spring Boot 的组件扫描器扫描到。当启动 Spring Boot 项目时,服务类的对象会被自动地创建,并被注册成 Bean。


二、Service 层的实现过程

大多数的 Spring Boot 项目采用接口模式实现 Service 层。那么,在实际开发中,如何实现 Service 层呢?如下图所示,Service 层的实现过程如下。

在这里插入图片描述

  1. 定义一个 Service 层的接口,在这个接口中定义用于传递和处理数据的方法。例如,定义一个 Service 层的接口 ProductService,代码如下:
public interface ProductService {
	... // 省略用于传递和处理数据的方法
}
  1. 定义一个 Service 层的接口的实现类,使用 @Service 注解予以标注。这个实现类的作用有两个:一个是实现 Service 层的接口中的业务方法;另一个是执行 Dao 层中用于访问、处理数据的操作。
    例如,使用 @Service 注解标注实现 ProductService 接口的 ProductServiceImpl 类,代码如下:
public class ProductServiceImpl implements ProductService {
	... // 省略用于实现接口的业务方法和用于执行访问、处理数据的操作的代码
}
  1. 在服务类的对象被自动地创建并被注册成 Bean 之后,其他 Component 组件即可直接注入这个 Bean。

三、同时存在多个实现类的情况

在上一节中,通过简单的示例只演示了一个 Service 层的接口存在一个实现类的情况。但是在实际开发中,一个 Service 层的接口可能会针对多种业务场景而存在多个实现类。

本节将介绍如何处理 Spring Boot 中的 “一个 Service 层的接口同时存在多个实现类” 的情况。

3.1 按照实现类的名称映射服务类的对象

使用 @Service 注解标注一个 Service 层的接口的实现类,这个实现类被称作服务类,这个实现类的对象被称作服务类的对象。服务类的对象会被自动地创建,并被注册成 Bean。

综上所述,Bean 的名称就是实现类的名称。需要注意的是,实现类的名称的首字母要大写,Bean 的名称的首字母是小写的。

例如,使用 @Service 注解标注实现 Service 接口的 ServiceImpl 类,代码如下:

​​​​@Service
​​​​public class ServiceImpl implements Service {  }​​

在上述代码中,实现类的名称是 ServiceImpl。因为 Bean 的名称就是实现类的名称,所以 Bean 的名称是 serviceImpl。因此,上述代码就等同于如下的用于注册 Bean 的代码:

​​​​@Bean("serviceImpl")
​​​​public Service createBean() {
​​​​    return new ServiceImpl();
​​​​}​​

这样,其他 Component 组件即可通过指定 Bean 的名称的方式注入与服务类的对象对应的 Bean。代码如下:

​​​​@Autowired
​​​​Service serviceImpl;​​

上述代码等同于如下的代码:

​​​​@Autowired
​​​​@Qualifier("serviceImpl")
​​​​Service impl;​​

掌握了以上内容后,下面编写一个实例来演示如何按照实现类的名称映射服务类的对象的方式来处理 “一个 Service 层的接口同时存在多个实现类” 的情况。

  1. 首先,创建 TranslateService 翻译服务接口,接口中只定义一个翻译方法。代码如下:
public interface TranslateService {
    String translate(String word);
}

然后,新建一个 Impl 包,并在包下创建 English2ChineseImpl 英译汉类,并实现 TranslateService 接口,同时使用 @Service 注解标注此类。在实现的翻译方法中,如果用户传入的单词是“Good morning”(不区分大小写),则返回中文“早上好”;如果传入其他内容,则返回“我还没有学会这个短句,你可以举例说明吗?”的提示信息。

English2ChineseImpl 类的代码如下:

@Service
public class English2ChineseImpl implements TranslateService {
    @Override
    public String translate(String word) {
        if ("Good morning".equalsIgnoreCase(word)) {
            return "Good morning -> 早上好";
        }
        return "我还没有学会这个短句,你可以举例说明嘛?";
    }
}

接着,在 Impl 包下创建 French2ChineseImpl 法译汉类,并实现 TranslateService 接口,同时使用 @Service 标注此类。在实现的翻译方法中,如果用户传入的单词是 “bonjour”(不区分大小写),则返回中文“早上好”;如果传入其他内容,则返回“我还没有学会这个短句,你可以举例说明吗?”的提示信息。

French2ChineseImpl 类的代码如下:

@Service
public class French2ChineseImpl implements TranslateService {
    @Override
    public String translate(String word) {
        if ("bonjour".equals(word)) {
            return "bonjour -> 早上好";
        }
        return "我还没有学会这个短句,你可以举例说明吗?";
    }
}

最后,创建 TranslateController 控制器类,分别创建两个服务类的对象,并分别按照两个实现类的名称(但首字母小写)映射这两个服务类的对象,使用 @Autowired 注解自动注入这两个服务类的对象。如果客户端访问的是 “/english” 地址,就将发来的参数交由负责英译汉的服务处理;如果访问的是 “/french” 地址,就将发来的参数交由负责法译汉的服务处理。

TranslateController类的代码如下:

@RestController
public class TranslateController {
    @Autowired
    @Qualifier("english2ChineseImpl")
    TranslateService english2ChineseImpl;  // 英译汉服务

    @Qualifier("french2ChineseImpl")
    @Autowired
    TranslateService french2EnglishImpl;   // 汉译英服务

    @RequestMapping("/english")
    public String english(String word) {
        return english2ChineseImpl.translate(word);
    }

    @RequestMapping("/french")
    public String french(String word) {
        return french2EnglishImpl.translate(word);
    }
}

使用 Postman 模拟用户通过 URL 地址发送的请求。访问 http://127.0.0.1:8080/english 地址,并添加 word 参数,参数值为 Good morning。发送请求后,即可看到下图结果,服务器将 Good morning 翻译成了“早上好”。

在这里插入图片描述
在这里插入图片描述

3.2 按照 @Service 的 value 属性映射服务类的对象

在 @Service 注解中,只包含一个 value 属性。value 属性是 @Service 注解的默认属性,它的两种语法格式如下:

@Service("id")          //在语法格式中省略了“value = ”
​​​​@Service(value = "id")  //在语法格式中没有省略“value = ”​​

为 value 属性赋值后,就相当于在创建与服务类的对象对应的 Bean 时确定了 Bean 的名称。因此,上述的语法格式等同于如下的用于注册 Bean 的代码:

​​​​@Bean("id")
​​​​public Service createBean() {
​​​​    return new ServiceImpl();
​​​​}​​

这样,其他 Component 组件即可通过指定 Bean 的名称的方式注入与服务类的对象对应的 Bean。代码如下:

​​​​@Autowired
​​​​Service id;​​

上述代码等同于如下的代码:

​​​​@Autowired
​​​​@Qualifier("id")
​​​​Service impl;​​

掌握了以上内容后,下面编写一个实例演示如何按照 @Service 的 value 属性映射服务类的对象的方式处理 “一个Service层的接口同时存在多个实现类” 的情况。

  1. 首先,创建 TranscriptsService 考试成绩服务接口,接口中只定义一个排序方法,参数为 List 类型的对象。

TranscriptsService 接口的代码如下:

public interface TranscriptService {
    void sort(List<Double> score);
}
  1. 创建 ASCTranscriptsServiceImpl 升序排列成绩类,并实现 TranslateService 接口,同时使用 @Service 注解标注此类。在实现的排序方法中,调用 Collections 类的 sort() 方法按照升序重新排列列表中的成绩。

ASCTranscriptsServiceImpl 类的代码如下:

@Service("asc")
public class ASCTTranscriptsServiceImpl implements TranscriptService {
    @Override
    public void sort(List<Double> score) {
        Collections.sort(score);  // 对 List 升序排序,默认排序规则
    }
}
  1. 接着,再创建 DESCTranscriptsServiceImpl 降序排列成绩类,并实现 TranslateService 接口,同时使用 @Service 注解标注此类。

DESCTranscriptsServiceImpl 类的代码如下:

@Service("desc")
public class DESCTranscriptsServiceImpl implements TranscriptService {
    @Override
    public void sort(List<Double> score) {
        score.sort(Comparator.reverseOrder());
    }
}
  1. 最后,创建 TranscriptsController 控制器类,分别创建两个用于排序的服务类的对象。其中,用于负责升序排列的服务类的对象使用 @Qualifier(“asc”) 注解予以标注,以表示注入的是名称为 asc 的Bean;负责降序排列的服务类的对象直接被命名为“desc”,@Autowired 注解会自动寻找名称为 “desc” 并且类型相同的 Bean 予以注入。如果前端向 “/asc” 地址发送成绩数据,则按照升序排列列表中的成绩;如果前端向 “/desc” 地址发送成绩数据,则按照降序排列列表中的成绩。

TranscriptsController 类的代码如下:

@RestController
public class TranscriptsController {
    @Autowired
    @Qualifier("asc")
    TranscriptService asc;

    @Autowired
    TranscriptService desc;

    @RequestMapping("/asc")
    public String asc(Double class1, Double class2, Double class3) {
        return getString(class1, class2, class3, asc);
    }

    @RequestMapping("/desc")
    public String desc(Double class1, Double class2, Double class3) {
        return getString(class1, class2, class3, desc);
    }

    private String getString(Double class1, Double class2, Double class3, TranscriptService asc) {
        List<Double> list = new ArrayList<>();
        list.add(class1);
        list.add(class2);
        list.add(class3);
        asc.sort(list);
        StringBuilder sb = new StringBuilder();
        list.forEach(e -> sb.append(e).append(" "));
        return sb.toString();
    }
}

使用 Postman 模拟用户通过 URL 地址发送的请求。访问 http://127.0.0.1:8080/asc 地址,先添加 class1、class2 和 class3 这 3 个参数并为其赋值,再发送请求,而后返回的结果将以升序的方式予以排列:

在这里插入图片描述
在这里插入图片描述


四、不采用接口模式的服务类

在实际开发中,一些功能非常简单的服务可以不采用接口模式,直接创建服务类并用 @Service 注解予以标注即可。下面编写一个实例来演示如何使用不采用接口模式的服务类。

  1. 创建 VerifyService 校验服务类,并用 @Service 注解予以标注。在这个服务类中,提供了一个方法,用来校验指定字符串是不是由 2~4 个中文字符组成的。

VerifyService 类的代码如下:

@Service
public class VerifyService {
    public boolean chineseName(String name) {
        String match = "^[\\u4e00-\\u9fa5]{2,4}$";
        if (name != null) {
            return name.matches(match);
        }
        return false;
    }
}
  1. 创建 VerifyController 控制器类,创建 VerifyService 服务类的对象,并使用 @Autowired 注解自动注入与服务类的对象对应的 Bean。当客户端发来一个名称时,通过 VerifyService 服务类的对象调用校验方法,判断该名称是否为有效的中文名称,并返回校验结果。

VerifyController 类的代码如下:

@RestController
public class VerifyController {
    @Autowired
    VerifyService verify;

    @RequestMapping("/verify/name")
    public String verifyName(String name) {
        if (verify.chineseName(name)) {
            return "中文名称检验通过";
        }
        return "这不是一个有效的中文名称";
    }
}

使用 Postman 模拟用户通过 URL 地址发送的请求。访问 http://127.0.0.1:8080/verify/name 地址,并添加 name 参数,name 参数的值为 “David”。因为 David 都是英文字母,所以发送请求后会看到下图结果:

在这里插入图片描述

将 name 参数的值修改为“李四”后,再次发送请求:

在这里插入图片描述

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

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

相关文章

如何帮助我们改造升级原有架构——基于TDengine 平台

一、简介 TDengine 核心是一款高性能、集群开源、云原生的时序数据库&#xff08;Time Series Database&#xff0c;TSDB&#xff09;&#xff0c;专为物联网IoT平台、工业互联网、电力、IT 运维等场景设计并优化&#xff0c;具有极强的弹性伸缩能力。同时它还带有内建的缓存、…

AI可信度标准与框架

AI 可信度被定义为&#xff1a;无论从技术层面还是社会层面&#xff0c;AI 在执行任务时均能够赢得用户的信任和接受的程度。具体地&#xff0c;一个高可信度的 AI 应包含两个组成部分&#xff0c;这些组成部分应贯穿于系统的整个生命周期&#xff1a; &#xff08;一&#xff…

数据结构 ——— C语言实现动态顺序表

目录 顺序表的概念 顺序表的结构&#xff08;静态顺序表和动态顺序表&#xff09; 1. 静态顺序表&#xff1a;使用固定长度的数组存储元素 2. 动态顺序表&#xff1a;使用动态开辟的数组存储元素 为什么选择实现动态顺序表 静态顺序表的缺点&#xff1a; 动态顺序表的优点…

读构建可扩展分布式系统:方法与实践15可扩展系统的基本要素

1. 可扩展系统的基本要素 1.1. 分布式系统在本质上就是复杂的&#xff0c;你必须考虑多种故障模式&#xff0c;并设计应对所有可能发生的情况的处理方式 1.2. 大规模应用程序需要协调大量的硬件和软件组件&#xff0c;共同实现低延迟和高吞吐量的能力 1.3. 面临的挑战是将所…

入门Django

Django Django 简介URL组成部分详解第一个Django项目创建一个Django项目运行Django项目项目结构介绍project和app的关系 URL与视图函数的映射URL的两种传参方式在URL中携带参数 path函数url路由模块化url反转 Django 简介 Django 是一个高级的 Python Web 框架&#xff0c;用于…

书生白嫖A100活动之——OpenCompass

内容来源&#xff1a;Tutorial/opencompass/readme.md at camp2 InternLM/Tutorial GitHub 概览 在 OpenCompass 中评估一个模型通常包括以下几个阶段&#xff1a;配置 -> 推理 -> 评估 -> 可视化。 配置&#xff1a;这是整个工作流的起点。您需要配置整个评估过…

HTML中的表单(超详细)

一、表单 1.语法 <!-- action&#xff1a;提交的地方 method&#xff1a;提交的方式&#xff08;get会显示&#xff0c;post不会&#xff09; --> <form action"#" method"get"><p>名字&#xff1a;<input name"name" ty…

【Geoserver使用】SRS处理选项

文章目录 前言一、Geoserver的三种SRS处理二、对Bounding Boxes计算的影响总结 前言 今天来看看Geoserver中发布图层时的坐标参考处理这一项。根据Geoserver官方文档&#xff0c;坐标参考系统 (CRS) 定义了地理参考空间数据与地球表面实际位置的关系。CRS 是更通用的模型&…

3. 轴指令(omron 机器自动化控制器)——>MC_MoveFeed

机器自动化控制器——第三章 轴指令 8 MC_MoveFeed变量▶输入变量▶输出变量▶输入输出变量 功能说明▶指令详情▶时序图▶重启运动指令▶多重启动运动指令▶异常 示例程序▶参数设定▶动作示例▶梯形图▶结构文本(ST) MC_MoveFeed 指定自外部输入的中断输入发生位置起的移动距…

2024免费录屏软件的宝藏功能与实用技巧

在手机上操作很多时候为了记录方便都直接截图或者录屏&#xff0c;其实电脑也一样。现在面向电脑的录屏工具纷繁复杂&#xff0c;很容易让我们挑花了眼。今天这篇文章我将介绍几款免费的录屏软件为大家提供参考。 1.福昕录屏大师 链接达达&#xff1a;www.foxitsoftware.cn/R…

深入探索 RUM 与全链路追踪:优化数字体验的利器

作者&#xff1a;梅光辉&#xff08;重彦&#xff09; 背景介绍 随着可观测技术的持续演进&#xff0c;多数企业已广泛采用 APM、Tracing 及 Logging 解决方案&#xff0c;以此强化业务监控能力&#xff0c;尤其在互联网行业&#xff0c;产品的体验直接关系着用户的口碑&…

使用Crawler实例进行网页内容抓取

网页内容抓取的背景 随着互联网的快速发展&#xff0c;网页上的信息量日益庞大。如何从海量的网页中快速、准确地抓取所需信息&#xff0c;成为了一个技术挑战。网页内容抓取技术通过自动化的方式&#xff0c;模拟用户浏览网页的过程&#xff0c;获取网页上的文本、图片、链接…

mybatisplus介绍以及使用(上)

目录 一、概念 1、什么是mybatisplus 2、为什么要使用mybatisplus 二、mybatisplus的使用 1、安装 2、常用注解 3、条件构造器 一、概念 1、什么是mybatisplus MyBatis-Plus&#xff08;简称MP&#xff09;是一个基于MyBatis的增强框架&#xff0c;旨在简化开发、提高…

学习C语言(20)

在这段没有更新的时间作者大一开学了&#xff0c;军训期间一直比较忙没时间学习&#xff0c;9月23号结束了为期十四天的军训&#xff0c;今天开始重新更学习C语言的博客 整理今天的学习内容 1.浮点数在内存中的储存 浮点数包括float&#xff0c;double&#xff0c;long doub…

vue实现左侧数据拖拽到右侧区域,且左侧数据保留且左侧数据不能互相拖拽改变顺序

一、案例效果 二、案例代码 封装左侧抽屉 DrawerSearch.vue <template><div><mtd-form :model"formDrawerSearch" ref"formCustom" inline><mtd-form-item><mtd-inputtype"text"v-model"formDrawerSearch.hos…

美团一面:给定两棵二叉树 `A` 和 `B`,判断 `B` 是否是 `A` 的子结构?

目录标题 问题描述思路分析代码解释详细步骤复杂度分析 问题描述 给定两棵二叉树 A 和 B&#xff0c;判断 B 是否是 A 的子结构。所谓子结构是指 B 中任意节点在 A 中存在相同的结构和节点值。 例子1&#xff1a; 输入&#xff1a;tree1 [1,7,5], tree2 [6,1] 输出&#…

LeaferJS 动画、状态、过渡、游戏框架

LeaferJS 现阶段依然专注于绘图、交互和图形编辑场景。我们引入游戏场景&#xff0c;只是希望让 LeaferJS 被更多有需要的人看到&#xff0c;以充分发挥它的价值 LeaferJS 为你带来了全新的游戏、动画、状态和过渡功能&#xff0c;助你实现那些年少时的游戏梦想。我们引入了丰富…

NVIDIA TAO 工具套件5.3.0学习介绍及操作-01

什么是 NVIDIA TAO 工具套件&#xff1f; NVIDIA TAO 工具套件基于 TensorFlow 和 PyTorch 构建&#xff0c;是 NVIDIA TAO 框架的低代码版本&#xff0c;通过抽象出 AI/深度学习框架的复杂性来加速模型训练过程。TAO 工具套件让您利用迁移学习的强大功能和自己的数据对预训练 …

Remotion:使用前端技术开发视频

前言 最近做文章突然想到很多文章其实也可以用视频的方式来展现&#xff0c;以现在短视频的火爆程度&#xff0c;肯定能让更多的人看到。 恰巧最近看了很多关于动画的前端 js 库&#xff0c;那如果将这些动画帧连续起来&#xff0c;岂不是就成了一个视频吗&#xff1f; 而且…

集成Elasticsearch到django restful

文章目录 集成ES到django restful服务端项目安装haystack基本使用安装配置索引模型ORM模型中新增discount_json字段方法全文索引字段模板 索引序列化器全文搜索的索引视图路由手动构建es索引 集成ES到django restful服务端项目 如果直接在Django项目直接编写代码作为ElasticSe…