聊一聊SpringBoot中的自定义Starter

news/2024/11/8 15:05:49 标签: spring boot, java, spring

前言


自己动手简单的封装、应用一个starter

该starter的作用是被引入后,会对项目中Controller出现的异常做统一的处理及反馈

starter的思想在实际开发过程被大量的应用


一、新建starter项目

使用IDE创建一个maven构建方式的空白项目

1.1pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.lazy.snail</groupId>
    <artifactId>exception-handler-spring-boot-starter</artifactId>
    <version>1.0.0</version>
    <packaging>jar</packaging>

    <properties>
        <java.version>17</java.version>
        <encoding>UTF-8</encoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>2.7.12</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>17</source>
                    <target>17</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>
  • 自定义的starter命名为xxx-spring-boot-starter
  • spring-boot-starter-web依赖的引入是由于开发starter过程中会使用到相关的类或注解,如:@RestControllerAdvice、@ExceptionHandler等
  • 构建安装时(clean install)可以去掉spring-boot-starter-web依赖
  • exception-handler-spring-boot-starter的应用场景就是基于springboot的web项目,所以相关的依赖在应用项目中都会有

1.2自定义一个Api异常

java">package com.lazy.snail.exceptionhandler;

/**
 * @ClassName ApiException
 * @Description TODO
 * @Author lazysnail
 * @Date 2024/11/7 15:06
 * @Version 1.0
 */
public class ApiException extends RuntimeException {
    private int code;
    private String message;

    public ApiException(int code, String message) {
        super(message);
        this.code = code;
        this.message = message;
    }

    public int getCode() {
        return code;
    }

    @Override
    public String getMessage() {
        return message;
    }
}
  • 引入项目中可以根据实际业务抛出该异常

1.3自定义反馈结果

java">package com.lazy.snail.exceptionhandler;

/**
 * @ClassName ApiResponse
 * @Description TODO
 * @Author lazysnail
 * @Date 2024/11/7 15:06
 * @Version 1.0
 */
public class ApiResponse<T> {
    private int status;   // 状态码
    private String message;  // 错误信息或成功提示
    private T data;  // 业务数据

    public ApiResponse(int status, String message, T data) {
        this.status = status;
        this.message = message;
        this.data = data;
    }

    public int getStatus() {
        return status;
    }

    public void setStatus(int status) {
        this.status = status;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }
}

出现异常情况时,简单的封装一个反馈对象

1.4异常处理配置类

java">package com.lazy.snail.exceptionhandler;

import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @ClassName ExceptionHandlerAutoConfiguration
 * @Description TODO
 * @Author lazysnail
 * @Date 2024/11/7 15:32
 * @Version 1.0
 */
@Configuration
public class ExceptionHandlerAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean(GlobalExceptionHandler.class)
    public GlobalExceptionHandler globalExceptionHandler() {
        return new GlobalExceptionHandler();
    }
}

普通的配置类,当springboot启动过程中,容器中没有GlobalExceptionHandler类型的bean时,将创建一个

1.5异常处理类

java">package com.lazy.snail.exceptionhandler;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

/**
 * @ClassName GlobalExceptionHandler
 * @Description TODO
 * @Author lazysnail
 * @Date 2024/11/7 15:06
 * @Version 1.0
 */
@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(ApiException.class)
    public ResponseEntity<ApiResponse<Object>> handleApiException(ApiException ex) {
        // 捕获 ApiException,返回指定的状态码和信息
        ApiResponse<Object> response = new ApiResponse<>(ex.getCode(), ex.getMessage(), null);
        return new ResponseEntity<>(response, HttpStatus.valueOf(ex.getCode()));
    }

    @ExceptionHandler(Exception.class)
    public ResponseEntity<ApiResponse<Object>> handleException(Exception ex) {
        // 捕获其他未处理的异常,返回500状态码
        ApiResponse<Object> response = new ApiResponse<>(500, "Internal Server Error", null);
        return new ResponseEntity<>(response, HttpStatus.INTERNAL_SERVER_ERROR);
    }
}

@RestControllerAdvice

  • Spring 提供的注解,用于定义全局的异常处理器、数据绑定和数据格式化。它是 @ControllerAdvice@ResponseBody 的组合,表示该类中的方法返回的对象会自动被序列化为 JSON 或 XML 格式,直接返回给客户端。
  • @RestControllerAdvice 的作用范围是整个应用的控制层,它会捕获所有标注了 @RequestMapping 的控制器中的异常。

@ExceptionHandler(ApiException.class)

  • 该注解标注的方法用于捕获 ApiException 类型的异常。通过将异常类型作为参数传入,Spring 可以在控制器中抛出 ApiException 时自动调用此方法。
  • @ExceptionHandler 注解的作用是捕获指定类型的异常,使得不同的异常处理逻辑可以分开,实现精细化处理。

@ExceptionHandler(Exception.class)

  • 该注解标注的方法用于捕获 Exception 类型的异常,这意味着它将处理所有未被其他 @ExceptionHandler 方法处理的异常,起到“兜底”作用。
  • 当出现任何未预料的异常时,Spring 会调用这个方法,为这些异常提供一个默认的错误响应,避免未处理的异常暴露到前端。

springfactories_239">1.6spring.factories

# META-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.lazy.snail.exceptionhandler.ExceptionHandlerAutoConfiguration

实现自动配置机制

  • Spring Boot 会在启动时扫描 META-INF/spring.factories 文件中的 EnableAutoConfiguration 配置,加载所有指定的自动配置类。这里指定了 ExceptionHandlerAutoConfiguration 作为自动配置类,因此 Spring Boot 会在启动时自动加载 com.lazy.snail.exceptionhandler.ExceptionHandlerAutoConfiguration

简化项目中的配置

  • 通过将异常处理逻辑配置为自动配置类,可以在不同项目中只需要引入该 starter,就可以自动获得其中定义的异常处理逻辑,而不需要手动配置。模块化的设计方便了复用和维护。

按需加载组件

  • ExceptionHandlerAutoConfiguration 中,通常会定义与异常处理相关的 @Bean,如 GlobalExceptionHandler 或其他配置项。通过这种自动配置机制,Spring Boot 会自动加载这些 Bean,确保全局异常处理器在项目启动时生效。
  • ExceptionHandlerAutoConfiguration 还可以通过条件注解(例如 @ConditionalOnMissingBean)判断是否需要创建某些 Bean,可以在主项目中进行定制,避免重复配置或冲突。

支持条件加载

  • Spring Boot 的自动配置类通常可以配置成启用或禁用。通过在 ExceptionHandlerAutoConfiguration 中使用条件注解(如 @ConditionalOnProperty),可以控制自动配置的加载,比如通过属性开关来启用或禁用异常处理功能。

二、构建打包

2.1命令行

mvn clean install

2.2IDE

image-20241107194541834

2.3说明

clean:执行清理操作

  • mvn clean 命令会删除项目的 target 目录,这个目录包含了之前构建生成的所有文件(如 .class 文件、JAR 包、WAR 包等)。这一步的作用是确保项目在全新的环境下进行构建,避免使用上一次构建中遗留的文件。

install:构建并安装项目

  • mvn install 命令会触发 Maven 的完整构建流程(编译、测试、打包等),并将生成的构件(例如 JAR、WAR 文件)安装到本地 Maven 仓库(默认在 ~/.m2/repository 目录下,也可自定义)。安装到本地仓库后,其他 Maven 项目可以通过依赖管理使用该构件。
  • 具体来说,install 命令会顺序执行以下几个阶段:
    • validate:检查项目是否正确,所有必要的信息是否完整。
    • compile:编译源代码。
    • test:运行测试代码。
    • package:将编译后的代码打包成 JAR、WAR 等文件。
    • install:将打包后的文件安装到本地 Maven 仓库,以便其他项目可以使用。

2.4结果

本地仓库中打包好了exception-handler-spring-boot-starter

image-20241107194810347

三、应用

springboot_299">3.1新建一个springboot项目

3.1.1pom文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.12</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.lazy.snail</groupId>
    <artifactId>SpringBoot</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>SpringBoot</name>
    <description>SpringBoot</description>
    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>com.lazy.snail</groupId>
            <artifactId>exception-handler-spring-boot-starter</artifactId>
            <version>1.0.0</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-log4j2</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>
  • pom中引入了exception-handler-spring-boot-starter

3.1.2测试controller

java">package com.lazy.snail.controller;

import com.lazy.snail.exceptionhandler.ApiException;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @ClassName TestController
 * @Description TODO
 * @Author lazysnail
 * @Date 2024/11/7 15:44
 * @Version 1.0
 */
@RestController
public class TestController {

    @GetMapping("/test")
    public String test() {
        // 手动抛出一个自定义的异常
        throw new ApiException(400, "Bad Request");
    }

    @GetMapping("/error")
    public String error() {
        int a = 1/0;
        return "error";
    }

}
  • "/test"模拟ApiException
  • "/error"模拟不可预见的异常

3.2postman测试结果

  • /test

image-20241107200144601

  • /error

image-20241107200227738

四、总结

4.1Starter的作用

  1. 简化配置

    通过自动配置,减少了手动配置的工作量,特别是在常见功能(如数据库连接、消息队列等)的配置上。

  2. 模块化功能

    可以将某些通用功能封装到独立的模块中(例如日志、缓存、事务、消息队列等),使得其他项目可以通过引入该 starter 直接使用。

  3. 增强可复用性

    将项目中的一些通用代码和配置抽象成 starter,方便在多个项目中复用,提升开发效率。

4.2Starter的简要开发流程

  1. 定义功能模块

    明确 starter 要解决的具体功能,例如统一的异常处理、日志管理、缓存配置等。

  2. 创建自动配置类

    在starter中编写自动配置类(通常使用 @Configuration 注解)。这个类包含自动配置所需的 bean 和逻辑,比如 @Bean 定义一些默认的配置或功能。

    使用 @Conditional 注解来控制某些配置是否生效,例如 @ConditionalOnProperty 可根据配置文件中的属性来启用某些功能。

  3. 提供依赖项

    确定starter所需的依赖项,并将其添加到 pom.xml 中。例如,如果starter需要连接数据库,就需要引入相关的数据库驱动。

  4. 包装和发布

    将 starter 打包为一个可发布的 JAR 文件,并发布到本地或远程 Maven 仓库。

    在META-INF/spring.factories中注册自动配置类,使得Spring Boot能够自动识别和加载该starter。

4.3Starter的应用场景

  1. 统一配置和功能封装:

    统一的异常处理:将异常处理封装到starter中,其他项目只需引入即可自动获得一致的异常处理机制。

    统一的日志配置:将日志配置封装为starter,让不同的项目能够使用统一的日志配置和日志格式。

    统一的响应格式:提供统一的 API 响应封装类,确保所有项目的返回格式一致。

    统一的安全配置:例如身份认证、授权、权限管理等功能的配置封装。

  2. 通用功能模块:

    缓存管理:封装Spring Cache的配置,统一管理缓存的实现,减少每个项目单独配置的工作量。

    数据库连接配置:封装常见的数据源配置,如 HikariCP 或 DBCP2,简化数据源的初始化和管理。

    消息队列配置:封装对消息队列(如 RabbitMQ、Kafka)的配置,简化消息队列的使用和管理。

  3. 跨项目共享功能:

    在多个项目中有相同的需求时,使用 starter 可以将功能抽象出来,减少每个项目重复编写相同代码的工作。比如,团队内部有多个微服务,可能都需要统一的异常处理和响应格式,这时可以开发一个starter让每个项目都能复用这部分功能。

  4. 支持不同的模块或服务:

    适用于微服务架构中,每个微服务通过引入不同的starter来简化配置和功能实现。例如,可以为每个服务创建一个starter,统一管理日志、异常处理、监控等功能。

  5. 简化常见配置的开发和维护:

    健康检查、审计日志、事务管理、定时任务 等常见功能可以通过starter提供自动化配置,无需重复编写。


http://www.niftyadmin.cn/n/5744048.html

相关文章

独立站 API 接口的性能优化策略

一、缓存策略* 数据缓存机制 内存缓存&#xff1a;利用内存缓存系统&#xff08;如 Redis 或 Memcached&#xff09;来存储频繁访问的数据。例如&#xff0c;对于商品信息 API&#xff0c;如果某些热门商品的详情&#xff08;如价格、库存、基本描述等&#xff09;被大量请求…

Java后台生成指定路径下创建指定名称的文件

1.Java后台生成指定路径下创建指定名称的CSV文件 /*** <生成csv文件>* param filePath 文件路径名称* param fileName 文件名称* param colNameList 标题数据信息* param dataList CSV的文件数据* return filePathfileName* throws*/public static File genera…

【AIGC】如何通过ChatGPT轻松制作个性化GPTs应用

博客主页&#xff1a; [小ᶻZ࿆] 本文专栏: AIGC | GPTs应用实例 文章目录 &#x1f4af;前言&#x1f4af;什么是GPTsGPTs的工作原理GPTs的优势GPTs的应用前景总结 &#x1f4af;创建GPTS应用的基本流程进入GPTs创建界面方式一&#xff1a;按照引导完成生成创建GPTs方式二…

AI时代,通才可能会占据更有利的地位

在AI时代&#xff0c;通才不仅有生存的可能&#xff0c;而且根据多个参考内容&#xff0c;他们实际上可能占据更有利的地位。以下几点解释了为什么通才在人工智能时代具有重要性和生存空间&#xff1a; 适应性和灵活性&#xff1a;通才因其广泛的知识基础和跨领域的技能&#x…

爬虫学习6

JSON&#xff08;JavaScript Object Notation&#xff09;和JSONP&#xff08;JSON with Padding&#xff09;是两个不同的概念&#xff0c;它们的主要区别在于用途和实现方式&#xff1a; ### JSON&#xff08;JavaScript Object Notation&#xff09; 1. **定义&#xff1a…

在数据抓取的时候,短效IP比长效IP有哪些优势?

在数据抓取领域&#xff0c;代理IP的选择对于任务的成功率和效率至关重要。短效IP和长效IP各有其特点和适用场景&#xff0c;但在数据抓取过程中&#xff0c;短效IP因其独特的优势而受到青睐。本文将和大家一起探讨短效IP在数据抓取中相比长效IP的优势。 短效IP的定义与特点 …

Odoo | 免费开源ERP:汽车及零配件行业信息化解决方案

文 / 开源智造 Odoo亚太金牌服务 概述 围绕汽车行业产业链上下游企业的整体业务主线&#xff0c;提供面向汽车主机厂整车个性化制造解决方案&#xff0c;产业链上下游一体化协同解决方案&#xff0c;数字化精益制造解决方案、全价值链质量管理解决方案&#xff0c;数字化运营解…

停车共享小程序ssm+论文源码调试讲解

2 系统关键技术 2.1 微信小程序 微信小程序&#xff0c;简称小程序&#xff0c;英文名Mini Program&#xff0c;是一种全新的连接用户与服务的方式&#xff0c;可以快速访问、快速传播&#xff0c;并具有良好的使用体验。 小程序的主要开发语言是JavaScript&#xff0c;它与普…