代码工艺:Spring Boot 防御式编程实践

news2024/10/3 0:16:50

防御式编程是一种编程实践,其核心理念是编写代码时要假设可能会发生错误、异常或非法输入,并通过各种手段防止这些问题引发系统崩溃、错误行为或安全漏洞。该编程方法的目的是让程序在面对不可预测的情况(如输入数据异常、硬件故障、意外的用户行为等)时仍然能够安全、稳定地运行。防御式编程特别强调在开发阶段尽可能地考虑各种边界情况、异常处理和系统的健壮性。

在使用 Spring Boot 开发 Java 后端时,结合《代码大全 2》中的防御式编程思想,可以有效提升代码的健壮性、可维护性和安全性。以下是一些重要的防御式编程实践要点:

1. 参数验证和预防无效输入

  • 原则: 所有外部输入(如 API 请求参数、表单输入等)都应该进行严格的验证,确保它们符合业务规则并且不会破坏系统的稳定性。
  • Spring Boot 实践:
    • 使用 @Valid@Validated 注解来自动验证请求体和方法参数。
    • 借助 javax.validation.constraints 提供的注解(如 @NotNull, @Size, @Pattern)来声明性地定义字段验证规则。
    • 如有复杂的自定义校验需求,可以实现 ConstraintValidator 进行自定义验证逻辑。
@PostMapping("/createUser")
public ResponseEntity<String> createUser(@Valid @RequestBody UserDto userDto) {
    // 请求体通过验证后,安全地处理逻辑
    return ResponseEntity.ok("User created");
}

2. 异常处理

  • 原则: 捕获异常并提供有用的信息,而不是让异常未经处理直接向上传播,避免系统崩溃或泄漏敏感信息。
  • Spring Boot 实践:
    • 使用全局异常处理器,如 @ControllerAdvice@ExceptionHandler,集中管理异常处理,避免散落在各个控制器中。
    • 为每种异常提供友好和清晰的错误响应,同时保证不会暴露内部实现细节。
    • 对于未预料的异常,提供通用处理以保证系统稳定。
@ControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<String> handleValidationExceptions(MethodArgumentNotValidException ex) {
        return ResponseEntity.badRequest().body("Invalid input");
    }

    @ExceptionHandler(Exception.class)
    public ResponseEntity<String> handleGeneralException(Exception ex) {
        // 避免泄漏堆栈信息,返回通用错误信息
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("An error occurred");
    }
}

3. 防止 Null 引发的异常

  • 原则: 时刻保持对 null 值的警觉,防止因 NullPointerException 产生的不稳定因素。
  • Spring Boot 实践:
    • 使用 Optional 来处理可能为 null 的返回值,避免直接操作可能为 null 的对象。
    • 在服务或数据层代码中,明确处理 null 情况,避免隐式假设参数或返回值永不为 null
public Optional<User> findUserById(Long id) {
    return userRepository.findById(id);
}

public ResponseEntity<String> getUser(Long id) {
    return findUserById(id)
        .map(user -> ResponseEntity.ok("User found"))
        .orElse(ResponseEntity.notFound().build());
}

4. 健壮的错误日志记录

  • 原则: 错误日志应清晰、详细,但不能暴露敏感信息(如密码、密钥等),确保在发生问题时可以追踪并解决问题。
  • Spring Boot 实践:
    • 使用 SLF4JLogback 等日志框架,结合 @Slf4j 注解记录不同级别的日志信息。
    • 确保在日志中只输出必要的信息,同时保护敏感数据不被泄漏。
@Slf4j
@Service
public class UserService {
    public void processUser(Long userId) {
        try {
            // 核心业务逻辑
        } catch (Exception ex) {
            log.error("Error processing user with id {}: {}", userId, ex.getMessage());
        }
    }
}

5. 确保线程安全

  • 原则: 当应用中存在并发或异步处理时,确保对共享资源的正确同步,以避免数据竞争或死锁。
  • Spring Boot 实践:
    • 在需要并发访问的类或方法中,使用线程安全的集合和类(如 ConcurrentHashMap, CopyOnWriteArrayList)。
    • 使用 @Async 处理异步任务时,确保方法的执行不会引发共享资源的并发问题。
    • 在涉及数据库操作时,使用合适的事务管理策略,确保数据的一致性。
@Service
public class AsyncService {
    @Async
    public CompletableFuture<String> processAsyncTask() {
        // 异步任务处理
        return CompletableFuture.completedFuture("Task Completed");
    }
}

6. 正确管理资源(关闭连接、文件等)

  • 实践: 正确管理和释放资源,如数据库连接、文件流等,避免资源泄漏。
  • 具体实现:
    使用 Java 的 try-with-resources 语句自动关闭资源。
public void readFile(String filePath) {
    try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {
        String line;
        while ((line = reader.readLine()) != null) {
            // 处理文件内容
        }
    } catch (IOException e) {
        // 处理异常
    }
}

7. 幂等性设计

  • 实践: 在设计 API 时,确保幂等性,即无论请求重复执行多少次,结果都应保持一致。幂等性可以防止重复提交导致数据不一致。
  • 具体实现:
    对于可能重复的请求(如支付或订单创建),可以引入唯一标识符(如 idempotency-key)来确保操作的幂等性。
@PostMapping("/processPayment")
public ResponseEntity<String> processPayment(@RequestHeader("Idempotency-Key") String idempotencyKey, @RequestBody PaymentRequest request) {
    if (paymentService.isPaymentProcessed(idempotencyKey)) {
        return ResponseEntity.ok("Payment already processed");
    }
    paymentService.processPayment(request, idempotencyKey);
    return ResponseEntity.ok("Payment processed successfully");
}

增强措施: 在数据库中保存操作的幂等性标记,如事务 ID 或 idempotency-key,确保即使发生网络问题或客户端重复提交,也不会引发重复的操作。

8. 事务管理和数据库一致性

  • 实践: 使用 Spring 的事务管理机制(@Transactional 注解)确保数据库操作的原子性,避免数据不一致。
  • 具体实现:
    在处理多个数据库操作时,确保使用事务管理来避免数据一致性问题。
@Service
public class OrderService {

    @Transactional
    public void createOrder(Order order) {
        orderRepository.save(order);
        paymentService.processPayment(order);
        // 如果支付失败,整个事务回滚,避免订单数据不一致
    }
}

增强措施: 对于跨多个数据库或外部服务的操作,使用事务管理来保证数据的完整性。对于分布式系统,可以考虑使用分布式事务或补偿性事务模式。

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

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

相关文章

区块链媒体推广:15个数字解读未来-华媒舍

区块链技术性作为一种区块链技术和加密的数据帐簿技术性&#xff0c;正在逐步引起广泛关注。伴随着新闻媒体市场的发展&#xff0c;区块链媒体推广也成为了新的发展趋势。下面我们就带大家探寻15个数字&#xff0c;揭露将来区块链媒体推广的新方向。 1、网络传播年增长率 数字…

Mac 网络连接正常,微信可以使用,但浏览器打不开网页?

解决&#xff1a; Step1&#xff0c;选择&#x1f34e;图标&#xff0c;选择系统设置&#xff08;或系统偏好设置&#xff09;打开&#xff1b; Step2&#xff0c;选择网络&#xff0c;Wi-Fi Step3&#xff0c;选择详细信息&#xff1b; Step4: 选择代理&#xff0c;关闭右…

每日OJ题_牛客_JOR26最长回文子串_C++_Java

目录 牛客_OR26最长回文子串 题目解析 C代码1 C代码2 Java代码 牛客_OR26最长回文子串 最长回文子串_牛客题霸_牛客网 描述&#xff1a; 对于长度为n的一个字符串A&#xff08;仅包含数字&#xff0c;大小写英文字母&#xff09;&#xff0c;请设计一个高效算法&#xf…

Redis: 持久化之RDB和AOF

概述 Redis 有一个高质量的课题&#xff1a;数据安全性与数据可靠性Redis 是一个内存型数据库&#xff0c;数据大部分都是存在内存里面当信息在内存中流通时&#xff0c;Redis 节点突然就故障挂掉当重新启动的时候&#xff0c;内存中的数据肯定是全部丢失了如果在这种情况下&a…

MySQL 中如何优化 DISTINCT 查询

一、引言 在 MySQL 数据库中&#xff0c;DISTINCT关键字用于查询结果集中去除重复的行。然而&#xff0c;使用DISTINCT可能会导致查询性能下降&#xff0c;特别是在处理大量数据时。本文将介绍一些优化 MySQL 中DISTINCT查询的方法。 二、理解 DISTINCT 查询的性能影响 &…

Oracle中TRUNC()函数详解

文章目录 前言一、TRUNC函数的语法二、主要用途三、测试用例总结 前言 在Oracle中&#xff0c;TRUNC函数用于截取或截断日期、时间或数值表达式的部分。它返回一个日期、时间或数值的截断版本&#xff0c;根据提供的格式进行截取。 一、TRUNC函数的语法 TRUNC(date) TRUNC(d…

2024/10/2

1 线代内积和外积 2 在 PyTorch 中&#xff0c;x.dot(torch.ones(3)) 是执行向量点积&#xff08;dot product&#xff09;操作的代码。假设 x 是一个一维张量&#xff08;向量&#xff09;&#xff0c;其形状是 (N,)&#xff0c;且 N 应该与 torch.ones(3) 的长度相匹配。具…

查找与排序-插入排序

排序算法可以分为内部排序和外部排序&#xff0c;内部排序是数据记录在内存中进行排序&#xff0c;而外部排序是因排序的数据很大&#xff0c;一次不能容纳全部的排序记录&#xff0c;在排序过程中需要访问外存。常见的内部排序算法有&#xff1a;插入排序、希尔排序、选择排序…

java基础应用-循环控制

1、使用while与自增运算符循环遍历数组 1.1 实例说明 本实例利用自增运算符结合while循环获取每个数组元素的值&#xff0c;然后把它们输出到控制台中。其中自增运算符控制索引变量的递增。程序运行结果如图1所示。 图1 实例运行结果 1.2 实现过程 创建ErgodicArray类&#…

企业网盘预算规划,了解2024年最新价格标准

2024年全球企业云存储市场将增15%&#xff0c;企业网盘收费多样&#xff0c;包括用户数量、存储容量定价及综合功能套餐。ZohoWorkDrive、DropboxBusiness、GoogleWorkspace为主流选择&#xff0c;价格因企业规模、功能需求而异&#xff0c;建议灵活选择套餐和长期合作计划。 一…

yub‘s Algorithmic Adventures_Day3

yub’s Algorithmic Adventures_Day3 有序数组的平方 link&#xff1a;977. 有序数组的平方 - 力扣&#xff08;LeetCode&#xff09; 非递减顺序 一个数列中的元素从左到右依次不减&#xff0c;或者说不降序排列. 比如&#xff1a;1233445&#xff0c;12345. 思路分析 如果…

CORE MVC 过滤器 (筛选器)《2》 TypeFilter、ServiceFilter

TypeFilter、ServiceFilter ServiceFilter vs TypeFilter ServiceFilter和TypeFilter都实现了IFilterFactory ServiceFilter需要对自定义的Filter进行注册&#xff0c;TypeFilter不需要 ServiceFilter的Filter生命周期源自于您如何注册&#xff08;全局、区域&#xff09;&…

vite中sass警告JS API过期

1.问题 在Vite创建项目中引入Sass弹出The legacy JS API is deprecated and will be removed in Dart Sass 2.0.0 - vite中sass警告JS API过期 The legacy JS API is deprecated and will be removed in Dart Sass 2.0.0警告提示表明你当前正在使用的 Dart Sass 版本中&#…

Python画图|渐变背景

Python画图在有些时候&#xff0c;需要使用渐变过度。 在matplotlib官网中&#xff0c;提供了一个为柱状图画渐变背景的案例&#xff0c;我们一同探索一番。 【1】官网教程 点开下述链接&#xff0c;直达官网教程&#xff1a; https://matplotlib.org/stable/gallery/lines…

【Bug】解决 Ubuntu 中 “error: Unable to Find Python3 Executable” 错误

解决 Ubuntu 中 “Unable to Find Python3 Executable” 错误 在 Ubuntu 系统上使用 Python 进行开发时&#xff0c;遇到找不到 python3 可执行文件的错误。 主要问题是无法正常打开终端&#xff08;原生与terminator&#xff09;&#xff0c;找不到python3&#xff0c;且无法…

基于muduo库函数实现protobuf协议的通信

文章目录 先定义具体的业务请求类型2. 实现服务端提供的服务protobuf_server.cppprotobuf_client.cpp 建议先去了解muduo库和protobuf协议&#xff1a; Protobuf库的使用Muduo库介绍及使用 先定义具体的业务请求类型 先使用protobuf库创建我们所要完成的业务请求类型&#xf…

域内用户名枚举 实验

1. 实验网络拓扑 kali: 192.168.72.128win2008: 192.168.135.129 192.168.72.139win7: 192.168.72.149win2012:(DC) 192.168.72.131 2. 简单原理 详细的报文分析在之前写过了&#xff0c;这里简单提一提。 利用的是Kerberos的AS阶段&#xff0c;AS_REP的回显不同&#xff0c…

迷宫中的最短路径:如何用 BFS 找到最近出口【算法模板】

如何通过广度优先搜索&#xff08;BFS&#xff09;求解迷宫问题 在这篇文章中&#xff0c;我们将学习如何使用 广度优先搜索&#xff08;BFS&#xff09; 解决一个典型的迷宫问题&#xff0c;具体是从迷宫的一个入口出发&#xff0c;找到最近的出口。我们将一步步分析 BFS 是如…

初识CyberBattleSim

现在许多企业都在使用AD域服务进行管理&#xff0c;我们现在通俗理解里面蕴含着许多重要资产。 对于这个东西有下列的描述: 1、攻击他能够获得用户权限 2、里面存在许多的计算机资产&#xff0c;当攻击者攻击其中的一台机器&#xff0c;可以通过某种手段在域中的环境横向移动…

golang rpc

RPC&#xff08;Remote Procedure Call&#xff09;远程过程调用&#xff0c;简单的理解是一个节点请求另一个节点提供的服务&#xff0c;对应rpc的是本地过程调用&#xff0c;函数调用是最常用的本地过程调用&#xff0c;将本地过程调用变成远程调用会面临着各种问题。 以两数…