从Spring请求处理到分层架构与IOC:注解详解与演进实战

引言

        在Spring开发中,请求参数处理、统一响应格式、分层架构设计以及依赖管理是构建可维护应用的核心要素。然而,许多开发者在实践中常面临以下问题:

  • 如何规范接收不同格式的请求参数?

  • 为何要引入分层架构?

  • 什么是控制反转(IoC)和依赖注入(DI)?

  • Spring的注解如@RestController@RequestBody等有何区别?

        本文将通过一个完整的案例演进,从基础请求处理出发,逐步引入分层架构与IoC容器,结合注解的深度解析,最终实现高内聚、低耦合的代码结构。过程中会详细讲解Bean对象管理组件扫描机制,以及常用注解的核心用法。

一、请求参数处理与统一响应

1. 请求参数接收方式

这里用Postman作为测试工具

(1) 简单参数:@RequestParam

简单post请求

 

 //保证参数名和请求参数名一致,或者用@RequestParam注解指定参数名@RequestMapping(value = "/simpleParam")public String simpleParam(String name, int age) {System.out.println(name + ": " + age);return "success";}
  • @RequestParam:绑定请求参数到方法参数,支持:

    • name:指定参数名(若省略则默认匹配方法参数名)

    • defaultValue:设置默认值

  • 测试URLhttp://localhost:8080/user?name=Tom&age=20

 (2)简单实体参数

        如果传入参数太多,我们可以进行实体化再进行传入,需要注意的是,传递的参数名字和接口方法里的参数名字需要对应,否则就需要用上面提到的@RequestParam进行指定

 User类

package com.ffyc.entity;public class User {private String name;private Integer age;private Address address;public String getName() {return name;}public void setName(String name) {this.name = name;}public Address getAddress() {return address;}public void setAddress(Address address) {this.address = address;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}@Overridepublic String toString() {return "User{" +"name='" + name + '\'' +", age=" + age +", address=" + address +'}';}}
 //简单实体参数@RequestMapping(value = "/simpleEnt")public String simpleEnt(User user) {System.out.println(user);return  "success";}
(3)复杂实体参数 

        假如我们需要传递用户的,姓名,年龄和地址,而地址作为一个新的对象,包含省份,城市,需要传递这些复杂的实体参数

Address类

package com.ffyc.entity;public class Address {private String province;private String city;public String getProvince() {return province;}public void setProvince(String province) {this.province = province;}public String getCity() {return city;}public void setCity(String city) {this.city = city;}@Overridepublic String toString() {return "Address{" +"province='" + province + '\'' +", city='" + city + '\'' +'}';}
}
    //复杂实体参数@RequestMapping(value = "/complexEnt")public String complexEnt(User user) {System.out.println(user);return  "success";}
(4)数组参数/集合参数

比如遇到复选框时我们可以选择数组,或者集合(list)进行传递

hobbies对应方法中的hobbies 

    //数组参数@RequestMapping(value = "/arrayParam")public String arrayParam(String[] hobbies) {System.out.println(Arrays.toString(hobbies));return "success";}

 用list集合进行传递

 hobby需要对应

    //集合参数@RequestMapping(value = "/collectionParam")public String collectionParam(@RequestParam List<String>hobby) {System.out.println(hobby);return "success";}
 (5)时间日期参数

    //时间日期参数@RequestMapping(value = "/dateParam")public String dateParam(@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime updateTime ) {System.out.println(updateTime);return "success";}

需要用到@DateTimeFormat注解指定格式再进行传递

(6)JSON参数

    //JSON参数@RequestMapping(value = "/jsonParam")public String jsonParam(@RequestBody User user) {System.out.println(user);return "success";}

@RequestBody: 将请求体中的JSON反序列化为Java对象

 (7)路径参数

有时候传递的参数再路径中

    //路径参数@RequestMapping(value = "/pathParam/{id}")public String pathParam(@PathVariable("id") Integer id) {System.out.println(id);return "success";}

@PathVariable:从URL路径中提取参数

 2. 统一响应格式

统一规范,方便前后端数据交互

响应实体类定义

@Data
@AllArgsConstructor
public class Result<T> {private int code;private String message;private T data;public static <T> Result<T> success(T data) {return new Result<>(200, "success", data);}
}

使用@ResponseBody返回JSON

@GetMapping("/user/{id}")
@ResponseBody
public Result<User> getUser(@PathVariable Long id) {User user = userService.findById(id);return Result.success(user);
}

3. @Controller vs @RestController

 代码对比

// 传统Controller返回视图
@Controller
public class PageController {@GetMapping("/home")public String home() {return "index.html"; // 返回视图名称}
}// RestController返回JSON
@RestController
public class UserController {@GetMapping("/api/user")public User getUser() {return new User("Tom", 20); // 自动转为JSON}
}

 

二、Bean管理与组件扫描

1. Bean对象的概念

  • 定义:由Spring容器管理的对象,生命周期由容器控制

  • 创建方式

    • 通过@Component及其派生注解(@Service@Repository@Controller)标记类

    • 通过@Bean方法在配置类中显式定义

2. 组件扫描:@ComponentScan

  • 作用:指定Spring扫描的包路径,自动注册标记了@Component的类为Bean

  • Spring Boot默认行为

    • @SpringBootApplication已包含@ComponentScan

    • 默认扫描主类所在包及其子包

手动配置示例

@Configuration
@ComponentScan(basePackages = "com.example.service")
public class AppConfig { }

三、分层架构演进

1. 原始单层架构的问题

高度耦合的Controller

@RestController
public class UserController {// 直接操作数据库(违反分层原则)@Autowiredprivate JdbcTemplate jdbcTemplate;@GetMapping("/user/{id}")public User getUser(@PathVariable Long id) {String sql = "SELECT * FROM user WHERE id = ?";return jdbcTemplate.queryForObject(sql, new UserRowMapper(), id);}
}

缺陷

  • 业务逻辑与数据访问混杂

  • 难以复用和维护

2. 三层架构改造

(1) 分层结构

(2) 分层代码实现

DAO层

@Repository
public class UserDaoImpl implements UserDao {@Autowiredprivate JdbcTemplate jdbcTemplate;@Overridepublic User findById(Long id) {String sql = "SELECT * FROM user WHERE id = ?";return jdbcTemplate.queryForObject(sql, new UserRowMapper(), id);}
}

Service层

@Service
public class UserServiceImpl implements UserService {private final UserDao userDao;@Autowired // 构造器注入(推荐)public UserServiceImpl(UserDao userDao) {this.userDao = userDao;}@Overridepublic User findById(Long id) {return userDao.findById(id);}
}

Controller层

@RestController
@RequestMapping("/api/users")
public class UserController {private final UserService userService;@Autowiredpublic UserController(UserService userService) {this.userService = userService;}@GetMapping("/{id}")public Result<User> getUser(@PathVariable Long id) {User user = userService.findById(id);return Result.success(user);}
}

3. 分层优势

四、IoC与依赖注入优化

1. 紧耦合问题演示

// 直接依赖具体实现类
public class UserServiceImpl implements UserService {private UserDao userDao = new UserDaoImpl(); // 紧耦合
}

问题

  • 更换DAO实现需修改代码

  • 难以进行单元测试

 2.控制反转 IOC

没有什么是加一个中间层不能解决的

 

 容器里面的队象成为Bean,默认是该类的名字首字母小写,例如,类的名字叫 EmpService,那么对应的Bean对象的就是 empService ,可以通过 value属性进行指定名字,不过一般用不到

@Service
public class UserServiceImpl implements UserService {@Autowiredprivate UserDao userDao;
}

 3.依赖注入 DI

用法区别

一个支付系统支持微信支付和支付宝支付

public interface PaymentGateway {void pay();
}@Component("wechatPay") // Bean名称=wechatPay
public class WechatPay implements PaymentGateway {@Overridepublic void pay() {System.out.println("微信支付");}
}@Component("alipay") // Bean名称=alipay
public class Alipay implements PaymentGateway {@Overridepublic void pay() {System.out.println("支付宝支付");}
}

使用@Autowired + @Qualifier

@Service
public class PaymentService {@Autowired@Qualifier("wechatPay")private PaymentGateway paymentGateway;
}

使用@Resource

@Service
public class PaymentService {@Resource(name = "wechatPay")private PaymentGateway paymentGateway;
}

五、总结

        掌握从基础请求处理到分层架构设计的完整路径,理解Spring的IoC容器与依赖注入机制,是构建松耦合、高可维护应用的关键。

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

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

相关文章

逐笔成交逐笔委托Level2高频数据下载和分析:20250124

逐笔成交逐笔委托下载 链接: https://pan.baidu.com/s/1UWVY11Q1IOfME9itDN5aZA?pwdhgeg 提取码: hgeg Level2逐笔成交逐笔委托数据分享下载 通过Level2逐笔成交与逐笔委托的详细数据&#xff0c;这种以毫秒为单位的信息能揭示许多关键点&#xff0c;如庄家意图、误导性行为&…

详解Redis的Zset类型及相关命令

目录 Zset简介 ZADD ZCARD ZCOUNT ZRANGE ZREVRANGE ZRANGEBYSCORE ZPOPMAX BZPOPMAX ZPOPMIN BZPOPMIN ZRANK ZREVRANK ZSCORE ZREM ZREMRANGEBYRANK ZREMRANGEBYSCORE ZINCRBY ZINTERSTORE 内部编码 应用场景 Zset简介 有序集合相对于字符串、列表、哈希…

深度解析:哪种心磁图技术是心脏检查的精准之选?

在全球心血管疾病的阴影日益笼罩的今天&#xff0c;医学界正积极寻求一种无损、无创、无辐射的心脏健康监测方式。心磁图仪&#xff08;MCG&#xff09;&#xff0c;这一前沿技术&#xff0c;凭借其独特的优势&#xff0c;悄然成为心脏电磁功能监测的新星。它不仅为心肌缺血、心…

jupyter配置说明

使用以下命令修改jupyter的配置文件参数&#xff1a; vim /root/.jupyter/jupyter_lab_config.py #这里填写远程访问的IP名&#xff0c;填*则默认是主机IP名 c.ServerApp.ip * # 这里的密码填写上面生成的密钥 c.ServerApp.password ************************************…

对grid布局有哪些了解【css】

CSS Grid 布局是现代网页设计中非常强大的布局方式之一&#xff0c;它能够使你以更加灵活且直观的方式来设计网页的布局&#xff0c;特别适用于复杂的多行多列的布局。它允许你在网页上创建非常精确的网格&#xff0c;帮助你把内容放置在多个行和列中。 1. Grid 布局的基本概念…

Node.js下载安装及环境配置教程 (详细版)

Node.js&#xff1a;是一个基于 Chrome V8 引擎的 JavaScript 运行时&#xff0c;用于构建可扩展的网络应用程序。Node.js 使用事件驱动、非阻塞 I/O 模型&#xff0c;使其非常适合构建实时应用程序。 Node.js 提供了一种轻量、高效、可扩展的方式来构建网络应用程序&#xff0…

软件越跑越慢的原因分析

如果是qt软件&#xff0c;可以用Qt Creator Profiler 作性能监控如果是通过web请求&#xff0c;可以用JMeter监控。 软件运行过程中逐渐变慢的现象&#xff0c;通常是因为系统资源&#xff08;如 CPU、内存、磁盘 I/O 等&#xff09;逐渐被消耗或软件中存在性能瓶颈。这个问题…

leetcode_链表 203.移除链表元素

203.移除链表元素 给你一个链表的头节点 head 和一个整数 val &#xff0c;请你删除链表中所有满足 Node.val val 的节点&#xff0c;并返回新的头节点 。 # Definition for singly-linked list. # class ListNode(object): # def __init__(self, val0, nextNone): # …

日志收集Day005

1.filebeat的input类型之filestream实战案例: 在7.16版本中已经弃用log类型,之后需要使用filebeat,与log不同&#xff0c;filebeat的message无需设置就是顶级字段 1.1简单使用&#xff1a; filebeat.inputs: - type: filestreamenabled: truepaths:- /tmp/myfilestream01.lo…

Vue中设置报错页面和“Uncaught runtime errors”弹窗关闭

文章目录 前言操作步骤大纲1.使用Vue自带的报错捕获机制添加报错信息2.在接口报错部分添加相同机制3.把报错信息添加到Vuex中方便全局使用4.添加报错页面备用5.app页面添加if判断替换报错界面 效果备注&#xff1a;vue项目中Uncaught runtime errors:怎样关闭 前言 在开发Vue项…

01、硬件设计常用经典电路

前言 一直想入职嵌入式软件&#xff0c;但是25年作为学历一般的应届生真是太难了&#xff0c;于是今年实习就不想跑太远了&#xff0c;就在老家5线小城市进入了一家小企业&#xff0c;当电子工程师实习&#xff08;学徒&#xff09;。 抱着入职什么&#xff0c;就学习什么的态…

使用Cline+deepseek实现VsCode自动化编程

不知道大家有没有听说过cursor这个工具&#xff0c;类似于AIVsCode的结合体&#xff0c;只要绑定chatgpt、claude等大模型API&#xff0c;就可以实现对话式自助编程&#xff0c;简单闲聊几句便可开发一个软件应用。 但cursor受限于外网&#xff0c;国内用户玩不了&#xff0c;…

微信小程序中常见的 跳转方式 及其特点的表格总结(wx.navigateTo 适合需要返回上一页的场景)

文章目录 详细说明总结wx.navigateTo 的特点为什么 wx.navigateTo 最常用&#xff1f;其他跳转方式的使用频率总结 以下是微信小程序中常见的跳转方式及其特点的表格总结&#xff1a; 跳转方式API 方法特点适用场景wx.navigateTowx.navigateTo({ url: 路径 })保留当前页面&…

Redis for AI

Redis存储和索引语义上表示非结构化数据&#xff08;包括文本通道、图像、视频或音频&#xff09;的向量嵌入。将向量和关联的元数据存储在哈希或JSON文档中&#xff0c;用于索引和查询。 Redis包括一个高性能向量数据库&#xff0c;允许您对向量嵌入执行语义搜索。可以通过过…

[Unity 热更方案] 使用Addressable进行打包管理, 使用AssetBundle进行包的加载管理.70%跟练

在正常的开发过程中我们经常遇到一些关于热更的方案,有一些已有的方案供我们选择,但是实机情况往往不尽如人意,各有优缺点. 现在我们同样有一个热更的需求,但是要求打包简单,加载过程可查,防止出现一些资源和流程的问题. 下面介绍我在项目中使用的方案. 打包方面使用Addressabl…

免费GPU算力,不花钱部署DeepSeek-R1

在人工智能和大模型技术飞速发展的今天,越来越多的开发者和研究者希望能够亲自体验和微调大模型,以便更好地理解和应用这些先进的技术。然而,高昂的GPU算力成本往往成为了阻碍大家探索的瓶颈。幸运的是,腾讯云Cloud Studio提供了免费的GPU算力资源,结合深度求索(DeepSeek…

寒假1.23

题解 web&#xff1a;[极客大挑战 2019]Secret File&#xff08;文件包含漏洞&#xff09; 打开链接是一个普通的文字界面 查看一下源代码 发现一个链接&#xff0c;点进去看看 再点一次看看&#xff0c;没什么用 仔细看&#xff0c;有一个问题&#xff0c;当点击./action.ph…

Agent群舞,在亚马逊云科技搭建数字营销多代理(Multi-Agent)(下篇)

在本系列的上篇中&#xff0c;小李哥为大家介绍了如何在亚马逊云科技上给社交数字营销场景创建AI代理的方案&#xff0c;用于社交动态的生成和对文章进行推广曝光。在本篇中小李哥将继续本系列的介绍&#xff0c;为大家介绍如何创建主代理&#xff0c;将多个子代理挂载到主代理…

【游戏设计原理】81 - 功能可见性暗示

一、什么是功能可见性&#xff1f; 功能可见性&#xff08;Affordance&#xff09;是一个设计心理学的概念&#xff0c;指的是物体或界面元素通过其外观或形态向用户传递的功能暗示。换句话说&#xff0c;功能可见性是指一个物体本身所具备的特性&#xff0c;使人能直接感知到…

6. 马科维茨资产组合模型+政策意图AI金融智能体(DeepSeek-V3)增强方案(理论+Python实战)

目录 0. 承前1. 幻方量化 & DeepSeek1.1 What is 幻方量化1.2 What is DeepSeek 2. 重写AI金融智能体函数3. 汇总代码4. 反思4.1 不足之处4.2 提升思路 5. 启后 0. 承前 本篇博文是对上一篇文章&#xff0c;链接: 5. 马科维茨资产组合模型政策意图AI金融智能体(Qwen-Max)增…