从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;
    }

    @Override
    public 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;
    }

    @Override
    public 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 {
    // 直接操作数据库(违反分层原则)
    @Autowired
    private 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 {
    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    public 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;
    }

    @Override
    public User findById(Long id) {
        return userDao.findById(id);
    }
}

Controller层

@RestController
@RequestMapping("/api/users")
public class UserController {
    private final UserService userService;

    @Autowired
    public 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 {
    @Autowired
    private UserDao userDao;
}

 3.依赖注入 DI

用法区别

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

public interface PaymentGateway {
    void pay();
}

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

@Component("alipay") // Bean名称=alipay
public class Alipay implements PaymentGateway {
    @Override
    public 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容器与依赖注入机制,是构建松耦合、高可维护应用的关键。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

神经网络|(三)线性回归基础知识

【1】引言 前序学习进程中&#xff0c;已经对简单神经元的工作模式有所了解&#xff0c;这种二元分类的工作机制&#xff0c;进一步使用sigmoid()函数进行了平滑表达。相关学习链接为&#xff1a; 神经网络|(一)加权平均法&#xff0c;感知机和神经元-CSDN博客 神经网络|(二…

2024年博客之星主题创作|猫头虎分享AI技术洞察:2025年AI发展趋势前瞻与展望

2025年AI发展趋势前瞻&#xff1a;猫头虎深度解析未来科技与商业机遇 摘要 2024年&#xff0c;AI技术迎来爆发式增长&#xff0c;AIGC、智能体、AIRPA、AI搜索、推理模型等技术不断突破&#xff0c;AI应用场景持续扩展。2025年&#xff0c;AI将进入全新发展阶段&#xff0c;W…

Android多语言开发自动化生成工具

在做 Android 开发的过程中&#xff0c;经常会遇到多语言开发的场景&#xff0c;尤其在车载项目中&#xff0c;多语言开发更为常见。对应多语言开发&#xff0c;通常都是在中文版本的基础上开发其他国家语言&#xff0c;这里我们会拿到中-外语言对照表&#xff0c;这里的工作难…

【Maui】提示消息的扩展

文章目录 前言一、问题描述二、解决方案三、软件开发&#xff08;源码&#xff09;3.1 消息扩展库3.2 消息提示框使用3.3 错误消息提示使用3.4 问题选择框使用 四、项目展示 前言 .NET 多平台应用 UI (.NET MAUI) 是一个跨平台框架&#xff0c;用于使用 C# 和 XAML 创建本机移…

AI导航工具我开源了利用node爬取了几百条数据

序言 别因今天的懒惰&#xff0c;让明天的您后悔。输出文章的本意并不是为了得到赞美&#xff0c;而是为了让自己能够学会总结思考&#xff1b;当然&#xff0c;如果有幸能够给到你一点点灵感或者思考&#xff0c;那么我这篇文章的意义将无限放大。 背景 随着AI的发展市面上…

pycharm 运行远程环境问题 Error:Failed to prepare environment.

问题排查 拿到更详细的报错信息&#xff1a; Help > Diagnostic Tools > Debug Log Settings section: 添加下面的配置 com.intellij.execution.configurations.GeneralCommandLine 重显报错&#xff0c;我这里是再次运行代码打开 Help | Collect Logs and Diagnosti…

C语言自定义数据类型详解(一)——结构体类型(上)

什么是自定义数据类型呢&#xff1f;顾名思义&#xff0c;就是我们用户自己定义和设置的类型。 在C语言中&#xff0c;我们的自定义数据类型一共有三种&#xff0c;它们分别是&#xff1a;结构体(struct)&#xff0c;枚举(enum)&#xff0c;联合(union)。接下来&#xff0c;我…

Windows上通过Git Bash激活Anaconda

在Windows上配置完Anaconda后&#xff0c;普遍通过Anaconda Prompt激活虚拟环境并执行Python&#xff0c;如下图所示&#xff1a; 有时需要连续执行多个python脚本时&#xff0c;直接在Anaconda Prompt下可以通过在以下方式&#xff0c;即命令间通过&&连接&#xff0c;…

MinIO的安装与使用

目录 1、安装MinIO 1.1 下载 MinIO 可执行文件 1.2 检查 MinIO 是否安装成功 1.3 设置数据存储目录 1.4 配置环境变量&#xff08;可选&#xff09; 1.5 编写启动的脚本 1.6 开放端口 1.7 访问 2、项目实战 2.1 引入依赖 2.2 配置yml文件 2.3 编写Minio配置类 2.4…

零基础Vue学习1——Vue学习前环境准备

目录 环境准备 创建Vue项目 项目目录说明 后续开发过程中常用命令 环境准备 安装开发工具&#xff1a;vscode、webstorm、idea都可以安装node:V22以上版本即可安装pnpm 不知道怎么安装的可以私信我教你方法 创建Vue项目 本地新建一个文件夹&#xff0c;之后在文件夹下打开…

Linux查看服务器的内外网地址

目录&#xff1a; 1、内网地址2、外网地址3、ping时显示地址与真实不一致 1、内网地址 ifconfig2、外网地址 curl ifconfig.me3、ping时显示地址与真实不一致 原因是dns缓存导致的&#xff0c;ping这种方法也是不准确的&#xff0c;有弊端不建议使用&#xff0c;只适用于测试…

二叉树的最大深度(C语言详解版)

一、摘要 嗨喽呀大家&#xff0c;leetcode每日一题又和大家见面啦&#xff0c;今天要讲的是104.二叉树的最大深度&#xff0c;思路互相学习&#xff0c;有什么不足的地方欢迎指正&#xff01;好啦让我们开始吧&#xff01;&#xff01;&#xff01; 二、题目简介 给定一个二…

OpenCV imread函数读取图像__实例详解

OpenCV imread函数读取图像__实例详解 本文目录&#xff1a; 零、时光宝盒 一、imread函数定义 二、imread函数支持的文件格式 三、imread函数flags参数详解 &#xff08;3.1&#xff09;、Flags-1时&#xff0c;样返回加载的图像&#xff08;使用alpha通道&#xff0c;否…

VMware虚拟机安装macOS11

1.安装虚拟机 如果尚未安装虚拟机&#xff0c;请先进行安装。地址&#xff1a;VMware17下载地址​​​​​​ 2、下载苹果镜像文件 macOS Big Sur 11.0.1 (20B29) 3、下载unlock文件&#xff08;目的是开启VMware的macOS选项功能&#xff09; https://download.csdn.net/d…

探究 Facebook 隐私安全发展方向,未来走向何方?

随着社交媒体的普及&#xff0c;隐私和数据安全问题成为了全球关注的焦点。Facebook&#xff0c;作为全球最大的社交平台之一&#xff0c;其隐私安全问题尤其引人注目。近年来&#xff0c;随着用户数据泄露事件的不断发生&#xff0c;Facebook 不断调整其隐私政策&#xff0c;探…

jQuery阶段总结(二维表+思维导图)

引言 经过23天的学习&#xff0c;期间有期末考试&#xff0c;有放假等插曲。本来应该在学校里学习&#xff0c;但是特殊原因&#xff0c;让回家了。但是在家学习的过程&#xff0c;虽然在学&#xff0c;很让我感觉到不一样。但是效果始终还是差点的&#xff0c;本来17、18号左右…

LabVIEW太阳能照明监控系统

在公共照明领域&#xff0c;传统的电力照明系统存在高能耗和维护不便等问题。利用LabVIEW开发太阳能照明监控系统&#xff0c;通过智能控制和实时监测&#xff0c;提高能源利用效率&#xff0c;降低维护成本&#xff0c;实现照明系统的可持续发展。 ​ 项目背景 随着能源危机…

Golang Gin系列-8:单元测试与调试技术

在本章中&#xff0c;我们将探讨如何为Gin应用程序编写单元测试&#xff0c;使用有效的调试技术&#xff0c;以及优化性能。这包括设置测试环境、为处理程序和中间件编写测试、使用日志记录、使用调试工具以及分析应用程序以提高性能。 为Gin应用程序编写单元测试 设置测试环境…

Spring Boot 邂逅Netty:构建高性能网络应用的奇妙之旅

一、引言 在当今数字化时代&#xff0c;构建高效、可靠的网络应用是开发者面临的重要挑战。Spring Boot 作为一款强大的 Java 开发框架&#xff0c;以其快速开发、简洁配置和丰富的生态支持&#xff0c;深受广大开发者喜爱。而 Netty 作为高性能、异步的网络通信框架&#xff…

科普篇 | “机架、塔式、刀片”三类服务器对比

一、引言 在互联网的世界里&#xff0c;服务器就像是默默运转的超级大脑&#xff0c;支撑着我们日常使用的各种网络服务。今天&#xff0c;咱们来聊聊服务器家族中的三位 “明星成员”&#xff1a;机架式服务器、塔式服务器和刀片式服务器。如果把互联网比作一座庞大的城市&…