定制你的【Spring Boot Starter】,加速开发效率

摘要: 本文将介绍如何创建一个自定义的 Spring Boot Starter,让您可以封装常用功能和配置,并在多个 Spring Boot 项目中共享和重用。

1. 简介

Spring Boot Starter 是 Spring Boot 框架中的一种特殊的依赖项,它用于快速启动和配置特定功能的应用程序。Starter 实际上是一个 Maven 或 Gradle 项目,它包含了一组预配置的依赖项、默认的配置信息和自动配置类,以帮助开发者快速集成和使用某项功能。

Spring Boot Starter 通常命名为 spring-boot-starter-*,例如 spring-boot-starter-web 用于启动基本的Web应用程序,spring-boot-starter-data-jpa 用于启动使用JPA的数据访问应用程序。

2.背景

在企业微服务开发中,每个微服务模块可能会有不同的异常处理和错误信息输出方式,这样会导致系统的一致性和可维护性下降。为了解决这个问题,您希望创建一个自定义的 Spring Boot Starter,用于统一处理异常和错误信息,并提供一致的错误响应格式。

3.需求

  1. 统一的异常处理:对于常见的异常类型(如 404、500),提供统一的异常处理方式,并返回一致的错误响应。

  2. 统一的错误响应格式:定义一致的错误响应格式,包括错误代码、错误消息、错误详情等信息。

  3. 可定制化:允许开发人员根据实际情况定制异常处理和错误响应的方式,以满足不同的业务需求。

4.实现

4.1.创建SpringBoot项目

创建完成后项目结构

4.2.添加相关依赖

这里需要注意的是,在build.gradle文件中,我们在这里引入了  id 'java-library'

plugins {
    id 'java'
    // 添加 maven-publish 插件 主要是为了发布项目到 Maven 仓库
    id 'maven-publish'
    // 应用 Java Library 插件,提供 Java 项目的基本构建功能
    id 'java-library'

}

group = 'org.sys_my_start'
version = '1.0-SNAPSHOT'
ext {
    springfoxVersion = '3.2.2'
}

// 配置发布任务
publishing {
    // 定义发布内容
    publications {
        // 定义一个 MavenPublication 类型的发布
        mavenJava(MavenPublication) {
            // 发布内容来源于 Java 组件
            from components.java
            // 定义 Maven 坐标信息
            groupId 'org.sys_my_start' // Maven 项目的组织或公司标识
            artifactId 'com_sys_my_start' // 项目在 Maven 仓库中的唯一标识
            version '1.0-SNAPSHOT' // 项目的版本号,表示开发中的版本
        }
    }
}

repositories {
    mavenCentral()
}

/*  api: 这个依赖声明范围表示该依赖项不仅仅对当前项目的编译和运行时可见,而且对于其他依赖于当前项目的项目也是可见的。
    换句话说,当一个项目依赖于当前项目时,它也将自动获得该依赖项。(也就是最外层的可以访问最内层所引入的依赖)
    上面引入这个 也是为了使用api 因为 gradle 7.0 以后默认不支持 compile语法了废弃了,所以我们这里需要使用api
    id 'java-library'
*/
dependencies {
    api "org.springframework.boot:spring-boot-starter-web:$springfoxVersion"
}

test {
    useJUnitPlatform()
}

4.3.编写Starter 主要逻辑

4.3.1.编写异常处理类

package com.sys.my.start.exception;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;

@ControllerAdvice
public class GlobalExceptionHandler {

    private final ExceptionHandlerProperties properties;

    @Autowired
    public GlobalExceptionHandler(ExceptionHandlerProperties properties) {
        this.properties = properties;
    }

    /**
     * 处理自定义异常
     * @param ex 自定义异常
     * @param request 请求
     * @return ResponseEntity 返回异常信息
     */
    @ExceptionHandler(Exception.class)
    public ResponseEntity<ErrorResponse> handleAnyException(Exception ex, WebRequest request) {
        if (properties.isEnabled()) {
            // 处理自定义异常, 返回异常信息
            ErrorResponse errorResponse = new ErrorResponse("500", ex.getMessage(), request.getDescription(false));
            return new ResponseEntity<>(errorResponse, HttpStatus.INTERNAL_SERVER_ERROR);
        } else {
            // 未启用全局异常处理 返回500
            return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }
}

4.3.2.编写错误响应类

package com.sys.my.start.exception;

/**
 * 定义统一错误相应格式
 */
public class ErrorResponse {
    /**
     * 错误码
     */
    private String errorCode;

    /**
     * 错误信息
     */
    private String errorMessage;

    /**
     * 错误详情
     */
    private String errorDetails;

    public ErrorResponse(String number, String message, String description) {
        this.errorCode = number;
        this.errorMessage = message;
        this.errorDetails = description;
    }

    public String getErrorCode() {
        return errorCode;
    }

    public String getErrorMessage() {
        return errorMessage;
    }

    public String getErrorDetails() {
        return errorDetails;
    }

    public void setErrorCode(String errorCode) {
        this.errorCode = errorCode;
    }

    public void setErrorMessage(String errorMessage) {
        this.errorMessage = errorMessage;
    }

    public void setErrorDetails(String errorDetails) {
        this.errorDetails = errorDetails;
    }
}

4.3.3.编写扫描配置文件类

package com.sys.my.start.exception;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

/**
 * 扫描 exception-handler:
 *      enabled: true
 * 为true时,启用全局异常处理
 */
@Component
@ConfigurationProperties(prefix = "exception-handler")
public class ExceptionHandlerProperties {
    private boolean enabled;

    public boolean isEnabled() {
        return enabled;
    }

    public void setEnabled(boolean enabled) {
        this.enabled = enabled;
    }
}

4.3.4.编写自动配置类

package com.sys.my.start.exception;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.context.annotation.Import;

import java.lang.invoke.MethodHandles;

/**
 * 自动配置类
 * 通过@Import导入相关文件
 * EnableAspectJAutoProxy开启AOP代理自动配置
 * @author 13723
 * @version 1.0
 * 2024/2/16 19:16
 */
@Configuration
@EnableAspectJAutoProxy(
		exposeProxy = true
)
@Import({ExceptionHandlerProperties.class})
public class ErrorHandlerAutoProxy {
	private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
}

4.3.5.编写spring.factories

# 通过SpringBoot的自动注入功能,扫描spring.factories,实现自动注入。
org.springframework.boot.autoconfigure.EnableAutoConfiguration= com.sys.my.start.exception.ErrorHandlerAutoProxy

4.3.6.发布到本地gradle

5.测试

5.1.测试模块引入starter

注意 这里为了保证测试的service模块能够使用到 starter引入springboot依赖,也需要使用api进行调用

// sys_my_service 子项目中的 build.gradle
plugins {
    id 'java-library'
}
dependencies {
    // https://mvnrepository.com/artifact/com.alibaba.fastjson2/fastjson2
    /* json转换 */
    implementation 'com.alibaba.fastjson2:fastjson2:2.0.46'
    /* lombok */
    compileOnly 'org.projectlombok:lombok'
    annotationProcessor 'org.projectlombok:lombok'
    /* 日志类 */
    api 'ch.qos.logback:logback-classic:1.4.14'
    /* 引入自定义jar包 */
    api  group: 'org.sys_my_start', name: 'com_sys_my_start', version: '1.0-SNAPSHOT'
}
sourceSets.main.resources {
    srcDirs = ['src/main/java']
    include '**/*.xml'
}

5.2.编写测试代码

/**
 * @author 13723
 * @version 1.0
 * 2024/2/16 13:32
 */
@RestController
@RequestMapping("/test")
public class TestController {
	private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());

	@Resource
	private TestService testService;
	@RequestMapping("/hello")
	public String hello(){
		return testService.hello();
	}
}
/**
 * @author 13723
 * @version 1.0
 * 2024/2/16 13:33
 */

@Service
public class TestService {
	private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());

	public String hello(){
		throw new RuntimeException("测试统一返回异常!");
	}
}

5.3.测试结果

6.总结

6.1.好处

自定义 Starter 是在 Spring Boot 中实现代码模块化和功能封装的强大工具。它的优势和用途包括:

  • 模块化开发: 自定义 Starter 允许将应用程序的不同功能模块化,使得代码结构更清晰,易于维护和管理。
  • 功能封装: Starter 可以封装特定功能的代码和配置,使得其他开发人员可以轻松地集成和使用这些功能。
  • 提高开发效率: 使用自定义 Starter 可以加速新项目的开发过程,避免重复编写相似的代码,提高开发效率。
  • 标准化配置: Starter 可以定义标准化的配置方式,使得不同项目之间的配置更一致,降低了配置错误的可能性。

通过自定义 Starter,开发团队可以实现代码的复用和标准化,从而提高了代码的复用性和可维护性,降低了开发和维护成本。

6.2.结语

本文介绍了如何创建自定义的 Spring Boot Starter,并详细解释了其优势、用途以及如何提高代码复用性和可维护性。通过自定义 Starter,开发人员可以更轻松地构建模块化的应用程序,并在团队内部实现功能的共享和复用。

鼓励读者在实际项目中尝试创建自己的自定义 Starter,将通用的功能模块化,并通过开源社区与他人分享,促进技术的共享和交流。这将有助于提高团队的开发效率,加速项目的上线和迭代过程,推动软件开发行业的发展。

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

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

相关文章

媒体邀约能干什么?

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。 新的一年首先祝大家好运连连&#xff0c;万事兴龙&#xff01; 媒体邀约能够为企业带来多方面的好处&#xff0c;具体包括&#xff1a; 1. 提升品牌知名度&#xff1a;通过媒体邀约&…

移动机器人的控制逻辑全解析。

你是否曾对那些在工厂中穿梭自如的移动机器人感到好奇&#xff1f;它们是如何准确无误地执行任务的&#xff1f;这一切都归功于移动机器人的控制逻辑&#xff01;今天&#xff0c;就让我们深入探讨一下移动机器人控制逻辑的重点。 一、环境感知与建模技术是移动机器人实现自主导…

【Vue前端】vue使用笔记0基础到高手第2篇:Vue知识点介绍(附代码,已分享)

本系列文章md笔记&#xff08;已分享&#xff09;主要讨论vue相关知识。Vue.js是前端三大新框架&#xff1a;Angular.js、React.js、Vue.js之一&#xff0c;Vue.js目前的使用和关注程度在三大框架中稍微胜出&#xff0c;并且它的热度还在递增。Vue.js是一个轻巧、高性能、可组件…

多元统计分析课程论文-聚类效果评价

数据集来源&#xff1a;Unsupervised Learning on Country Data (kaggle.com) 代码参考&#xff1a;Clustering: PCA| K-Means - DBSCAN - Hierarchical | | Kaggle 基于特征合成降维和主成分分析法降维的国家数据集聚类效果评价 目录 1.特征合成降维 2.PCA降维 3.K-Mean…

开年炸裂-Sora/Gemini

最新人工智能消息 谷歌的新 Gemini 模型 支持多达 1M的Token&#xff0c;可以分析长达一小时的视频 1M Token可能意味着分析700,000 个单词、 30,000 行代码或11 小时的音频、总结、改写和引用内容。 Comment&#xff1a;google公司有夸大的传统&#xff0c;所以真实效果需要上…

开工大吉!秀一下我们假期の战绩

开工大吉&#xff0c;新年新气象 首先祝大家开工大吉&#xff0c;新年新气象。 祝我的粉丝股东们都能&#xff1a;顺利上岸&#xff0c;升职加薪&#xff0c;日进斗金&#xff01; 开工就要冲冲冲&#xff01; 春节假期我是好好放松了&#xff0c;在努力克制自己不要像之前…

《数字图像处理-OpenCV/Python》连载:形态学图像处理

《数字图像处理-OpenCV/Python》连载&#xff1a;形态学图像处理 本书京东 优惠购书链接 https://item.jd.com/14098452.html 本书CSDN 独家连载专栏 https://blog.csdn.net/youcans/category_12418787.html 第 12 章 形态学图像处理 形态学图像处理是基于形状的图像处理&…

java生成pdf

1.pdf预览 2.maven <!--pdf--><dependency><groupId>com.itextpdf</groupId><artifactId>itextpdf</artifactId><version>5.5.9</version></dependency><dependency><groupId>com.itextpdf</groupId>…

python-自动化篇-办公-将PDF文件转存为图片

因工作中的某些奇葩要求&#xff0c;需要将PDF文件的每页内容转存成按顺序编号的图片。用第三方软件或者在线转换也可以&#xff0c;但批量操作还是Python方便&#xff0c;所谓搞定办公自动化&#xff0c;Python出山&#xff0c;一统天下&#xff1b;Python出征&#xff0c;寸草…

中小学信息学奥赛CSP-J认证 CCF非专业级别软件能力认证-入门组初赛模拟题第三套(阅读程序题)

CSP-J入门组初赛模拟题第三套 二、阅读程序题 (程序输入不超过数组或字符串定义的范围&#xff0c;判断题正确填√错误填X;除特殊说明外&#xff0c;判断题 1.5分&#xff0c;选择题3分&#xff0c;共计40分) 第一题 1 #include<iostream> 2 #include<cstdio> …

高校实验室危险化学品如何管理?看了这篇文章让您管理危化品不在难!

采用‘一人一格’负责制&#xff0c;实现网格化、精准化、精细化安全管理可快速、全面、准确地掌控实验室危化品使用信息及危废管理&#xff0c;系统功能涵盖危化品的计划申购、采购入库、领用、退还、统计、查询管理等模块。采用“五双”原则&#xff0c;实现学校对实验室危化…

【多线程】线程的概念与创建

多线程 1. 认识线程&#xff08;Thread&#xff09;线程是什么为啥要有线程进程和线程的区别Java 的线程 和 操作系统线程 的关系 2.第⼀个多线程程序3.创建线程⽅法1 继承 Thread 类⽅法2 实现 Runnable 接⼝方法3 匿名内部类创建 Thread ⼦类对象方法4 匿名内部类创建 Runnab…

线程池工作过程

线程池工作流程 线程池的处理流程总结 线程池的处理流程 当提交一个新任务到线程池时&#xff0c;线程池的处理流程如下&#xff1a; 1、线程池判断核心线程池里的线程是否都在执行任务。如果不是&#xff0c;则创建一个新的工作线程来执行任务。如果核心线程池里的线程都在执…

用Python和OpenCV搭建自己的一维码和QRCode扫描仪(步骤 + 源码)

导 读 本文主要介绍使用Python和OpenCV搭建自己的一维码和QRCode扫描仪&#xff08;步骤 源码&#xff09;。 项目简介 本文我们将创建一个程序来扫描图像中的二维码和条形码。对于这个程序&#xff0c;我们需要三个包&#xff0c;分别是OpenCV、NumPy和pyzbar。大多数 Pyth…

【leetcode题解C++】51.N皇后 and 76.最小覆盖子串

51. N皇后 按照国际象棋的规则&#xff0c;皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。 n 皇后问题 研究的是如何将 n 个皇后放置在 nn 的棋盘上&#xff0c;并且使皇后彼此之间不能相互攻击。 给你一个整数 n &#xff0c;返回所有不同的 n 皇后问题 的解决方…

C#安装CommunityToolkit.Mvvm依赖

这里需要有一定C#基础&#xff0c; 首先找到右边的解决方案&#xff0c;右键依赖项 然后选择nuget管理 这里给大家扩展一下nuget的国内源&#xff08;https://nuget.cdn.azure.cn/v3/index.json&#xff09; 然后搜自己想要的依赖性&#xff0c;比如CommunityToolkit.Mvvm 再点…

学历太低,可以学这5个技术,不但好找工作,工资也挺高的!

前言 我今年23岁&#xff0c;勉强把高中上完了。 大家都说上高中的时候非常辛苦&#xff0c;但在我看来&#xff0c;却不是这样的。 因为那时候根本就没有&#xff0c;把精力放在学习上面&#xff0c;而是经常出去泡网吧。 没办法&#xff0c;一个班级里面&#xff0c;大多…

《苍穹外卖》知识梳理6-缓存商品,购物车功能

苍穹外卖实操笔记六—缓存商品&#xff0c;购物车功能 一.缓存菜品 可以使用redis进行缓存&#xff1b;另外&#xff0c;在实现缓存套餐时可以使用spring cache提高开发效率&#xff1b;   通过缓存数据&#xff0c;降低访问数据库的次数&#xff1b; 使用的缓存逻辑&#…

【STM32 CubeMX】SPI_Flash_W25Q64的操作方法

文章目录 前言一、W25Q64操作方法基本概念1.1 读数据1.2 写使能1.3 读状态1.4 擦除扇区1.5 烧写页 总结 前言 在嵌入式系统开发中&#xff0c;使用外部 SPI Flash 存储器可以为 STM32 微控制器提供额外的存储空间&#xff0c;以存储程序代码、配置数据等。W25Q64 是一款常见的…

洛谷P8627 饮料换购 题解

#题外话&#xff08;第27篇题解&#xff09;&#xff08;本题为普及-难度&#xff09; #先看题目 题目链接https://www.luogu.com.cn/problem/P8627 #思路&#xff08;用while循环&#xff0c;循环到山穷水尽为止&#xff0c;用一个计数的计量&#xff09; #代码 #include …