从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,一经查实,立即删除!

相关文章

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

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

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

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

日志收集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: 路径 })保留当前页面&…

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

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

寒假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;使人能直接感知到…

Latex常用语法介绍

LaTeX 是一种基于 TeX 的排版系统&#xff0c;专注于高质量文档的排版&#xff0c;尤其适合学术论文、技术报告、书籍等复杂格式的文档。以下是 LaTeX 的核心语法和功能的详细介绍&#xff1a; 一、LaTeX 文档结构 1. 基本框架 每个 LaTeX 文档由 导言区 和 正文区 组成&…

闲鱼自动抓取/筛选/发送系统

可监控闲鱼最新发布商品&#xff0c;发送钉钉 1&#xff0c;精准关键词匹配&#xff1a;输入核心关键词&#xff0c;精准定位与之高度契合的信息&#xff0c;确保搜索结果直击要点&#xff0c;满足您对特定内容的急切需求。 2&#xff0c;标题关键词智能筛选&#xff1a;不仅着…

深入理解MySQL事务(万字详)

文章目录 什么是事务为什么会出现事务事务的版本支持事务的提交方式事务常见操作方式正常演示 - 证明事务的开始与回滚非正常演示1 - 证明未commit&#xff0c;客户端崩溃&#xff0c;MySQL自动会回滚&#xff08;隔离级别设置为读未提交&#xff09;非正常演示2 - 证明commit了…

[Spring] Gateway详解

&#x1f338;个人主页:https://blog.csdn.net/2301_80050796?spm1000.2115.3001.5343 &#x1f3f5;️热门专栏: &#x1f9ca; Java基本语法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12615970.html?spm1001.2014.3001.5482 &#x1f355; Collection与…

Arduino Uno 和 1.44 英寸 TFT 屏幕(SPI 接口)初体验

在嵌入式项目中&#xff0c;1.44 英寸 TFT 屏幕&#xff08;SPI 接口&#xff09;是一种非常实用的显示设备&#xff0c;适合用于显示文本、图形和简单动画。本文将详细介绍如何使用 Arduino Uno 和 1.44 英寸 TFT 屏幕进行基本的显示操作&#xff0c;包括显示文本、绘制图形和…

Linux应用编程(五)USB应用开发-libusb库

一、基础知识 1. USB接口是什么&#xff1f; USB接口&#xff08;Universal Serial Bus&#xff09;是一种通用串行总线&#xff0c;广泛使用的接口标准&#xff0c;主要用于连接计算机与外围设备&#xff08;如键盘、鼠标、打印机、存储设备等&#xff09;之间的数据传输和电…

Git处理冲突详解

文章目录 Git处理冲突详解一、引言二、冲突产生的原因三、解决冲突的步骤1. 手动解决冲突1.1 查看冲突文件1.2 编辑冲突文件1.3 提交解决冲突 2. 使用合并工具解决冲突 四、使用示例五、总结 Git处理冲突详解 一、引言 在团队协作开发中&#xff0c;Git冲突是不可避免的。当多…

Linux 下注册分析(4)

系列文章目录 Linux 设备树 Linux 下注册分析&#xff08;1&#xff09; Linux 下注册分析&#xff08;2&#xff09; Linux 下注册分析&#xff08;3&#xff09; Linux 下注册分析&#xff08;4&#xff09; 文章目录 系列文章目录1、device_create简介device_createdevice_c…

第十五届蓝桥杯大赛软件赛省赛Java 大学 B 组(1、2题)

1.报数游戏 问题描述 小蓝和朋友们在玩一个报数游戏。由于今年是 2024 年&#xff0c;他们决定要从小到大轮流报出是 20或 24 倍数的正整数。前 10 个被报出的数是&#xff1a;20,24,40,48,60,72,80,96,100,120。请问第 202420242024个被报出的数是多少? 答案提交 这是一道结果…