面试官:Spring Boot 控制层中,@Service 可以完全替代 @Controller 吗?90% 都会答错!

news/2024/10/30 9:11:54/文章来源:https://www.cnblogs.com/javastack/p/18514946

作者:毅航
来源:juejin.cn/post/7393533304505204787

在SpringBoot开发中,@Controller@Service基本上是日常开发中使用的最频繁的两个注解。但你有没考虑过@Service代替@Controller注解来标注到控制层的场景?换言之,经过@Service标注的控制层能否实现将用户请求分发到服务层的功能?

前言

在SpringBoot开发中,@Controller注解用于标识一个控制器类,该类负责处理Web请求。而控制器类通常包含若干个方法,每个方法对应一个HTTP请求的处理逻辑。而控制器是MVC(Model-View-Controller)架构的一部分,其主要负责将用户请求分发到适当的服务层,并返回视图或响应数据。而@Service注解用于标识一个服务类,用以负责处理业务逻辑和与数据访问层交互。

相信对于大多数Java开发者来说@Controller@Service注解的使用都不算太难。但进一步,用@Service标注控制层能否达到和@Controller注解相同的功能呢?对于这个操作你可能会觉得很疯狂,并下意识的说出不可能。但事实果真如此吗?

我们不妨先通过一个简单的例子来验证一下。

@Sercice代替@Controller

我们首先自定义一个ServiceController的控制层,其内部通过Autowired注解注入一个UserMapper,并通过userMapper来实现控制层与数据层的交互。

Spring Boot 基础就不介绍了,推荐看这个实战项目:

https://github.com/javastacks/spring-boot-best-practice

具体代码如下:

ServiceController
@Service
@RequestMapping("/ts")
public class ServiceController {@Autowiredprivate UserMapper userMapper;@GetMapping("get-services")@ResponseBodypublic User getServices() {User user = userMapper.selectOne(Wrappers.lambdaQuery(User.class).eq(User::getUsername, "zhangSan"));return user;}
}

然后,通过PostMan发送一个Get请求,以请求 http://localhost:8080/ts/get-services 其返回内容如下 :

Date: Sun, 21 Jul 2024 02:37:39 GMT
Keep-Alive: timeout=60
Connection: keep-alive{"username": "zhangSan","id": 1,"type": null,"remark": "test1"
}

通过返回内容,不难看出我们的请求顺利到ServiceControllergetServices方法。也就是说我们完全可以用@Service来替代@Controller标注在控制层上!

揭秘背后原理

你可能会觉得@Service来替代@Controller这样的操作有的反常规,因为在学习SpringBoot时,从来也没有那个教程告诉我们@Service注解还有这样的骚操作。那@Service可以这样使用的背后原因到底是什么呢?

众所周知,@Service@Controller注解都能被Spring容器所加载,并注入到Spring容器中。我们以SpringBoot应用为例来分析其注入容器的全过程。

在分析之前我们首先明确一点,对于Spring而言其会根据配置(如 XML 文件或 @ComponentScan 注解)扫描指定的包及其子包,查找标记有 @Controller@Service@Repository@Component 的类。但对于Springboot应用而言,其并没有显示的使用XmL配置或@ComponentScan指定扫描路径的方式来加载对应路径下的Bean信息。

这背后的原因主要在于@SpringBootApplication 注解的使用,@SpringBootApplication其实是一个组合注解,其内部包括以下三个注解:

  • @EnableAutoConfiguration: 启用 Spring Boot 的自动配置机制。
  • @ComponentScan: 启用组件扫描,以便自动发现并注册 Spring 组件。
  • @Configuration: 表示这是一个 Spring 配置类。

在默认情况下,Spring Boot 会从主应用类所在的包开始进行组件扫描,这意味着只要 @Controller@Service 注解的类位于主应用类所在包及其子包中,它们就会被自动发现并注册到 Spring 容器中。

明白了SpringBoot对于Bean的加载逻辑后,我们再来深入到其内部来看。SpringBoot对于这部分Bean的加载流程如图所示:

当我们在main方法中执行SpringApplication.run时,其在run方法内容会完成Spring容器的创建,以及Bean的加载。具体来看,其在AbstractApplicationContext中的invokeBeanPostBeanFactoryPostProcessore时,会通过 ConfigurationClassPostProcessorpostProcessBeanDefinitionRegistry 方法加载路径下所有的bean名称信息,然后在finshBeanFactoryInitialization完成bean的实例化。而我们绕了这么一大圈就是皆在说明,被@Service@Controller所标注的类在SpringBoot框架中是如何一步步被注入到容器的。

进一步,笔者曾在揭秘@Controller内部方法与URL绑定的全流程中谈到,对于控制层内的方法,其会在AbstractHandlerMethodMapping中的 afterPropertiesSet()完成请求url与方法的映射绑定。更进一步,afterPropertiesSet的处理逻辑全部委托于于 getCandidateBeanNamesprocessCandidateBean两个方法。

getCandidateBeanNames 会获取当前容器中所有的bean 的名称集合,并筛选类中标有@Controller或者 @RequestMapping注解的类交给processCandidateBean处理,以完成请求url与方法的映射。

此时,我们不妨来回看我们一开始的ServiceController的样例代码:

@Service
@RequestMapping("/ts")
public class ServiceController {// ....省略内部细节信息
}

不难发现,其虽然没使用@Controller修饰,但其却被@RequestMapping注解信息,也就是说其能被processCandidateBean所处理,进而其也就能完成url和方法映射关系的维护。更进一步,当请求至DispatcherServlet时,SpringMVC变更通过其url信息,找到能处理相应请求的HandlerMethod。从而也就能完成url的处理以及视图的渲染。

事实上,只要你能确保Bean信息注入到容器,并且类信息上至少有@Controller或者 @RequestMapping注解,那便能在AbstractHandlerMethodMapping中所解析,然后完成url与方法的绑定!这也就是为什么我们一开始花费精力研究@Controller@Service 注入容器的原因。

总结

在 SpringBoot 开发中,@Controller@Service 是最常用的注解。通常,@Controller 用于标识控制器类,处理 Web 请求并将请求分发到服务层;而 @Service 用于标识服务类,处理业务逻辑。然而,如果用 @Service 来标注控制层是否可以实现与 @Controller 相同的功能呢?

答案是肯定的。只要类被 @Service 标注,并且包含 @RequestMapping 等注解,Spring Boot 依然能够将其作为控制器来处理请求并返回响应。这背后的原理在于 @Service@Controller 都能被 Spring 容器加载和注册,并且 @RequestMapping 注解能够使类的方法与 URL 映射,从而实现请求处理功能。

更多文章推荐:

1.Spring Boot 3.x 教程,太全了!

2.2,000+ 道 Java面试题及答案整理(2024最新版)

3.免费获取 IDEA 激活码的 7 种方式(2024最新版)

觉得不错,别忘了随手点赞+转发哦!

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

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

相关文章

两台linux的文件传输

起因 本地拉取docker镜像timeout,然后就准备把阿里云上已经在运行的镜像打包下载下来。 指令1:rsync rsync 是一个非常强大的工具,用于文件同步和高效的数据传输。它可以用于备份、文件传输以及数据同步等多种场景。 rsync 的主要优点在于其高效性和灵活性,特别是在处理大量…

10 早期计算机如何编程

程序需要加载进入内存, 最早是纺织机利用穿孔纸卡进行编程,穿孔纸卡用在过人口普查,用于记录一条条数字,但机器只有汇总功能,汇总穿孔数目 后来机器功能增多,人需要一个控制面板执行不同操作, 最早是重新布线更换指令,后来有了插线板,控制面板成了可拔插,可以给机器插…

PbootCMS模板首页循环调用所有栏目和对应内容

{pboot:nav} 栏目链接:[nav:link] 栏目名称:[nav:name] {pboot:list scode=[nav:scode] num=4 order=date} 内容链接:[list:link] 内容名称:[list:title] 内容图片:[list:ico] 内容时间:[list:date style=Y-m-d] 内容描述:[list:description] {/pboot:list} {/pboot:nav…

PbootCMS自带的sitemap.xml增加tag标签链接

修改 SitemapModel.php 文件:打开 /apps/home/model/SitemapModel.php 文件 在 78 行后面增加以下代码:public function getSortTags($scode) {$join = array(array(ay_content_sort b, a.scode=b.scode, LEFT),array(ay_model c, b.mcode=c.mcode, LEFT));$scode_arr = arra…

PbootCMS 面包屑导航样式修改和自定义的设置方法

问题:PbootCMS面包屑导航样式修改和自定义的设置方法。 答案:面包屑调用:{pboot:position}自定义参数:separator=*:分隔符,默认为 >>。 separatoricon=*:分割图标,例如 separatoricon=fa fa-angle-double-right。 indextext=*:首页文本,默认为“首页”。 index…

Maximum execution time of 30 secon

这种问题出现在Web开发环境中,特别是PHP等脚本语言中,当某个脚本运行时间超过预设的最大执行时间(例如30秒)时,服务器会终止该脚本的执行以防止资源被长时间占用。 解决方案增加脚本的最大执行时间在PHP中,可以通过修改php.ini文件中的max_execution_time值来增加脚本的最…

工地货梯AI人数识别系统

工地货梯AI人数识别系统采用人体神经网络深度学习算法,工地货梯AI人数识别系统对升降机轿厢内的人数进行智能分析和识别,能够精确识别出升降机内的人数。系统可以实时监测升降机内的人数变化,并根据设定的门限值,当人数超过限制时自动触发图像抓取和报警功能。报警方式可以…

物品堆放限高监测系统

物品堆放限高监测系统采用神经网络深度学习算法,物品堆放限高监测系统能够实时监测物品堆放区域的状态。通过在现场安装监控摄像头,系统对摄像头拍摄的实时视频进行处理分析,识别并判断物品堆放的高度情况。当堆放超过限定的高度范围时,系统将立即触发语音告警功能。物品堆…

煤块堵塞监测识别系统

煤块堵塞监测识别系统利用现场摄像头实时监测煤矿生产线上的皮带煤块堵塞情况。煤块堵塞监测识别系统可以准确地识别出堆积在生产线上的煤块,并计算出其堆积的程度。当煤块堆积的程度超过预设的警戒范围时,系统会立刻通知相关工作人员前往现场进行物料疏通。与传统人工巡检相…

1、K8S环境渗透学习

一、概述Kubernetes,简称k8s,是当前主流的容器调度平台,被称为云原生时代的操作系统。在实际项目也经常发现厂商部署了使用k8s进行管理的云原生架构环境,在目前全面上云的趋势,有必要学习在k8s环境的下的一些攻击手法。 二、k8s用户 Kubernetes 集群中包含两类用户:一类是…

zlibrary镜像地址,zlibrary中文版地址及客户端/app

Z-Library是一个全球性的数字图书馆,它提供了一个庞大的免费资源库,致力于让全球读者能够轻松访问科学图书和学术文献。以下是关于Z-Library的一些主要介绍:藏书量:Z-Library拥有超过1100万册图书和8400多万篇学术文章,覆盖各个学科领域。 实体书籍交流中心:在全球42个国…

10.31 实验7:单例模式

[实验任务一]:学号的单一 仿照课堂的身份证的例子,实现每个同学仅有一个学号这一问题。 实验要求: 1.画出对应的类图; 2.提交源代码;#include <iostream>using namespace std;class S_num{ private:static S_num sno;static int num; public:static S_num getintan…