后端学习 - SpringBoot

  • SpringBoot 是整合 Spring 技术栈的一站式框架,是简化 Spring 技术栈的快速开发脚手架
  • 约定大于配置


一 概述

1 第一个 SpringBoot 项目

  1. 导入 maven 依赖
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.3</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>
  1. 创建主程序类,作为启动的入口,注意:启动类的目录一定要在 controller 等目录的至少上一级,或者在 @SpringBootApplication 注解中添加 scanBasePackages 属性
@SpringBootApplication(scanBasePackages = "controller")
public class HelloApplication {
    public static void main(String[] args) {
        SpringApplication.run(HelloApplication.class, args);
    }
}
  1. 创建控制器类以及控制器方法,@RestController = @ResponseBody + @Controller,用于标注控制器类,该控制器的 所有方法 向浏览器返回 控制器方法的返回值
@RestController  // @RestController = @ResponseBody + @Controller,向浏览器返回 该方法的返回值
public class HelloController {
    @RequestMapping("/hello")
    public String hello() {
        return "hello springboot";
    }
}
  1. 运行主程序类的 main 方法即可,无需更多配置

  2. 如果要修改某些配置,在 resources/application.properties 中修改即可,如:server.port=8888

  3. 如果要导出为 jar 包,需要在 maven 配置文件中添加:

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

并执行 maven 的 clean + package 操作
在这里插入图片描述

2 SpringBoot 特性:依赖管理

  • 自定义的 SpringBoot 项目的父项目 spring-boot-starter-parent,实现了依赖管理功能
  • 父项目的父项目 spring-boot-dependencies<properties> 标签声明了几乎所有开发中常用的依赖的版本号,实现自动版本仲裁机制(即:引入依赖默认可以不写版本,但是引入非版本仲裁的 jar,要写版本号)
  • 如果想使用依赖的指定版本,需要在当前项目的 maven 配置文件中的 <properties> 标签声明
1、查看spring-boot-dependencies里面规定当前依赖的版本 用的 key。
2、在当前项目里面重写配置
    <properties>
        <mysql.version>5.1.43</mysql.version>
    </properties>
  • 场景启动器:spring-boot-starter-*,只要引入 starter,这个场景的所有常规需要的依赖都进行自动引入;所有的启动器底层都依赖 SpringBoot 核心依赖
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter</artifactId>
  <version>2.6.3</version>
  <scope>compile</scope>
</dependency>

3 SpringBoot 特性:自动配置

  • 主程序所在包及其下面的所有子包里面的组件(需要 @Component、@Controller… 注解才能称为组件,不加注解无法扫描)都会被默认扫描进来,不用显式地配置组件扫描
  • 各种配置拥有默认值,并按需加载所有自动配置项
  • 主程序类的注解 @SpringBootApplication = @SpringBootConfiguration + @EnableAutoConfiguration + @ComponentScan(主程序类所在包)
  • 自动配置流程:
    (Ⅰ) SpringBoot 加载所有的自动配置类xxxxxAutoConfiguration类,而非组件)
    (Ⅱ) 每个自动配置类按照条件生效,默认绑定配置文件指定的值(xxxxProperties类)
    (Ⅲ) 生效的配置类就会给容器中装配很多组件
    (Ⅳ) 如果要自定义配置,可以选择:创建 @Bean 替换底层组件,或修改组件获取的配置文件(这些文件最终都映射到 application.properties

二 SpringBoot 的 IOC容器

1 组件添加:@Configuration

  • @Configuration 注解的类,相当于原来的 xml 配置文件
  • 配置类本身也是组件
  • 向 IOC 容器中注入 Bean,对 @Configuration 类中的方法使用 @Bean 注解,将方法的返回对象注入容器
  • 默认情况下,注入容器的对象名为被注解的方法名,如果要修改则向 @Bean 中传递参数
@Configuration
class MyConfig {
    @Bean  // 根据方法的返回值,向IOC容器中注入对象,默认情况下的对象名为 jerrymouse
    public Pet jerrymouse() {
        return new Pet("Jerry", 3);
    }
}
  • 代理 Bean 方法:@Configuration(proxyBeanMethod = true) 时(Full 模式),对于 @Bean 注解的方法,如果直接被调用,方法返回的对象是单例的;如果是 @Configuration(proxyBeanMethod = false) (Lite 模式)则非单例
  • Full / Lite 模式与组件依赖问题:
    配置的组件之间无依赖关系,用 Lite 模式,加速容器启动过程,减少判断
    配置类组件之间有依赖关系,用 Full 模式,组件单实例,保证依赖成立

2 组件添加:@Import

  • 对类进行的注解
  • 在 IOC 容器中创建组件,名字为类的全类名
@Import({User.class, Pet.class})
@Configuration(proxyBeanMethods = true)
public class MyConfig {
}

3 组件添加:@Conditional

  • 条件装配:满足指定的条件后进行组件装配,可以注解类和方法
  • 具有一系列的子注解,对应不同的情况
    @ConditionalOnBean(name="..."):IOC容器具有指定 Bean 时执行当前组件的装配
    @ConditionalOnMissingBean(name="..."):IOC容器失去指定 Bean 时执行当前组件的装配

4 引入原生配置文件:@ImportResource

  • 用于注解配置类,导入 Spring 的 xml 配置文件
  • 传递参数为配置文件的路径: @ImportResource("classpath:myspringconfig.xml")

5 配置绑定:@ConfigurationProperties

  • 目的是,使得 Java 读取到 properties 文件中的内容,并且把它封装到 JavaBean 中,以供随时修改并使用,即:JavaBean 和配置文件的绑定
  • @ConfigurationProperties 注解需要填入 prefix 属性,以在配置文件中指定其对象的属性值
@Component
@ConfigurationProperties(prefix = "jjjerry")
public class Pet {
    private String name;
    private int age;
	...
}

// 在 application.properties 配置中:
// jjjerry.name="杰瑞"
// jjjerry.age=10
  • 如果是自定义类,在自定义类上注解 @ConfigurationProperties 即可;如果非自定义类,需要在配置类上额外注解 @EnableConfigurationProperties(Pet.class),它的作用是开启 Pet 的配置绑定功能,并将 Pet 组件自动注册到容器中

6 Lombok 的使用

  • 简化 Bean 的开发:
@Data  // 创建 getter、setter
@AllArgsConstructor  // 有参构造器
@NoArgsConstructor  // 无参构造器
@ToString  // ...
@EqualsAndHashCode  // ...
public class Person {
    private String name;
    private String address;
}
  • @Slf4j 简化日志记录,为注解的类注入了 log 实例:
@Slf4j
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
        log.info(String.valueOf(context.getBean("wubai").toString()));
    }
}

@Configuration
class MyConfiguration {
    @Bean
    public Person wubai() {
        return new Person("wubai", "taipei");
    }
}

三 yaml 配置文件

  • 是一种面向数据的配置文件,推荐在配置组件属性时使用

1 基本语法

  1. 大小写敏感
  2. 使用缩进表示层级关系
  3. 缩进不允许使用tab,只允许空格
  4. 缩进的空格数不重要,只要相同层级的元素左对齐即可
  5. '#'表示注释
  6. 使用 key: value 的形式,注意其中的空格
  7. 字符串无需使用引号标注,使用引号时,单引号的转义字符不起作用,双引号起作用
  8. 详细的语法规则

2 使用示例

创建名为 wubai_wujunlin 的组件,并注入 IOC 容器:

@Component(value = "wubai_wujunlin")  // 组件在 IOC 容器中的名字
@ConfigurationProperties(prefix = "wujunlin")  // 组件在配置文件中的前缀
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
@EqualsAndHashCode
public class Person {
    private String name;
    private String address;
    private List<String> bandMates;
}

依赖的 application.yaml

wujunlin:
  name: 吴俊霖
  address: 台北
  bandMates: [小朱, 大猫, Dino]

3 添加配置提示(自动补全)

在 maven 配置文件添加:

<!--添加自动补全功能-->
 <dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-configuration-processor</artifactId>
	<optional>true</optional>
</dependency>


<!--打包时不包括自动补全-->
 <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.springframework.boot</groupId>
                            <artifactId>spring-boot-configuration-processor</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

四 实例:后端管理系统

1 静态资源的配置与访问

  • 默认静态资源在 resources 目录下的 static 、public… 文件夹内
  • 访问时使用的路径:当前项目根路径/ + 静态资源名
  • 自定义访问静态资源使用的路径:
spring:
  mvc:
    static-path-pattern: "/custom_static_url/**"  
# 访问时: http://localhost:8080/custom_static_url/bf1.png
  • 欢迎页默认为静态资源路径下的 index.html
  • 输入地址 http://localhost:8080 即可访问欢迎页
  • 在静态资源目录下的 favicon.ico 同样会被自动解析

2 配置控制器

  • 为了避免刷新页面导致表单的重复提交,首次成功登陆后,在控制器方法中返回 "redirect:/main.html" 重定向到新的页面
@Controller
public class IndexController {
    @GetMapping(value = {"/login", "/"})
    public String loginPage() {
        return "login";
    }

    @PostMapping("/login")
    public String processLogin(User user, HttpSession session, Model model) {
        if (!user.getUsername().isEmpty() && !user.getPassword().isEmpty()) {  // 省略判断逻辑
            session.setAttribute("loginUser", user);
            return "redirect:/main.html";  // 使用重定向,防止表单的重复提交
        } else {
            model.addAttribute("msg", "账号密码错误");
            return "login";
        }
    }

    @GetMapping("/main.html")  // 直接访问 http://localhost:8080/main.html 仍然需要经过视图解析器才能获取main.html,而不能直接读取静态资源
    public String mainPage(HttpSession session, Model model) {
        if (session.getAttribute("loginUser") != null) {
            return "main";
        } else {
            model.addAttribute("msg", "未登录");
            return "login";
        }
    }
}

3 Thymeleaf 抽取页面的相同元素

官方文档对于 th:insert, th:replace, th:include 的示例

<body>

  <div th:insert="footer :: copy"></div>
  <div th:replace="footer :: copy"></div>
  <div th:include="footer :: copy"></div>
  
</body>

…will result in:
<body>

  <div>
    <footer>
      &copy; 2011 The Good Thymes Virtual Grocery
    </footer>
  </div>

  <footer>
    &copy; 2011 The Good Thymes Virtual Grocery
  </footer>

  <div>
    &copy; 2011 The Good Thymes Virtual Grocery
  </div>
  
</body>

实现步骤

  1. 引入命名空间 <html lang="en" xmlns:th="http://www.thymeleaf.org">
  2. 创建公共部分的 html 页面,使用 th:fragmentid 为公共部分命名
1 使用 fragment 属性命名
<head th:fragment="commonheader">
    <meta charset="UTF-8">
    <title>表格页面的公共部分</title>
    ...
</head>

2 使用 id 属性命名
<div id="leftmenu" class="left-side sticky-left-side">
	...
</div>
  1. 导入公共部分:
1 对于 th:fragment 命名的标签,添加属性 th:xx(replace/insert/include)="公共文件名 :: fragment属性"
<div th:replace="common :: commonheader"></div>

2 对于 id 命名的标签,添加属性 th:xx(replace/insert/include)="公共文件名 :: #id属性"
<div th:replace="common :: #leftmenu"></div>

4 配置拦截器

  • 拦截路径为 /** 时,静态资源的访问也会被拦截,需要为静态资源路径添加放行
  • 配置类:
@Configuration
public class MyConfig implements WebMvcConfigurer {
    // 配置拦截器
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginInterceptor())
                .addPathPatterns("/**")  // 静态请求也会被拦截
                .excludePathPatterns("/", "/login", "/css/**", "/fonts/**", "/images/**", "/js/**");  // 放行不登陆就能访问的页面,和静态资源
    }
}
  • 拦截器的实现:
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        Object user = request.getAttribute("loginUser");
        if (user != null) {
            return true;
        } else {
            // 未登录,跳转到登录页
            // response.sendRedirect("/");  或:
            request.getRequestDispatcher("/").forward(request, response);
            log.warn("未登录!!");
            return false;
        }
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    	//...
        HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        //...
        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
    }
}

在这里插入图片描述

5 文件上传

  1. html 的 from 标签
<form role="form" th:action="@{/upload}" method="post" enctype="multipart/form-data">

	<div class="form-group">
         <label for="exampleInputFile">上传单个文件</label>
         <input type="file" name="singleImage" id="exampleInputFile1">
    </div>
    
    <div class="form-group">
    	<label for="exampleInputFile">上传多个文件,添加 multiple 属性</label>
    	<input type="file" name="multiImage" id="exampleInputFile2" multiple>
    </div>
    
</form>
  1. 控制器方法
    使用 MultipartFile 参数类型获取上传文件,其方法 transferTo(...) 用于保存文件到服务器
    @PostMapping("/upload")
    public String upload(@RequestPart("headerImage") MultipartFile headerImg,
                         @RequestPart("lifeImage") MultipartFile[] lifeImg) throws IOException {
                         
        // 保存上传文件
        if (!headerImg.isEmpty()) {
            String fileName = headerImg.getOriginalFilename();
            headerImg.transferTo(new File("E:\\" + fileName));
        }
        
        // 多个文件用 for 循环上传
        // ...

        return "redirect:/main.html";
    }

6 错误处理

  • SpringBoot 默认的错误处理机制:error/下的4xx,5xx页面会被自动解析,发生错误时,有精确的错误状态码页面就匹配精确,没有就找 4xx / 5xx;如果都没有就触发白页
    在这里插入图片描述

五 注入原生 Web 组件(Servlet,Filter,Listener)

1 使用 Servlet API(推荐)

  • 使用 @WebServlet,@WebFilter,@WebListener 注解对应的类,并在启动类注解 @ServletComponentScan 传入 Web 组件位置
@WebServlet(urlPatterns = "/")
public class MyServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //...
    }
}
@WebFilter(urlPatterns = {"/**"})
public class MyFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("过滤器初始化");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("执行过滤操作");
        filterChain.doFilter(servletRequest, servletResponse);
    }

    @Override
    public void destroy() {
        System.out.println("过滤器销毁");
    }
}
@WebListener
public class MyListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        System.out.println("监听到项目初始化");
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        System.out.println("监听到项目销毁");
    }
}

2 使用 RegistrationBean

  • 无需注解
@Configuration
public class MyRegistConfig {

    @Bean
    public ServletRegistrationBean myServlet(){
        MyServlet myServlet = new MyServlet();
        return new ServletRegistrationBean(myServlet,"/my","/my02");
    }


    @Bean
    public FilterRegistrationBean myFilter(){

        MyFilter myFilter = new MyFilter();
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(myFilter);
        filterRegistrationBean.setUrlPatterns(Arrays.asList("/my","/css/*"));
        return filterRegistrationBean;
    }

    @Bean
    public ServletListenerRegistrationBean myListener(){
        MySwervletContextListener mySwervletContextListener = new MySwervletContextListener();
        return new ServletListenerRegistrationBean(mySwervletContextListener);
    }
}

六 数据访问

1 导入 JDBC 场景

  • 创建项目时,在 Spring Initializer 中勾选 JDBC 即可
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
  • 默认的数据源是 HikariDataSource
  • 常规配置
spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    username: root
    password: 123
    url: jdbc:mysql://localhost:3306/test

2 切换 Druid 数据源

  • 引入依赖
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.2.8</version>
        </dependency>
  • 配置示例
spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    username: root
    password: 123
    url: jdbc:mysql://localhost:3306/test

    druid:
      aop-patterns: com.atguigu.admin.*  #监控SpringBean
      filters: stat,wall     # 底层开启功能,stat(sql监控),wall(防火墙)

      stat-view-servlet:   # 配置监控页功能
        enabled: true
        login-username: admin
        login-password: admin
        resetEnable: false

      web-stat-filter:  # 监控web
        enabled: true
        urlPattern: /*
        exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*'


      filter:  # 配置开启的filter
        stat:    # 对上面filters里面的stat的详细配置
          slow-sql-millis: 1000
          logSlowSql: true
          enabled: true
        wall:
          enabled: true
          config:
            drop-table-allow: false

3 整合 MyBatis


七 单元测试

1 JUnit5

  • JUnit 5 = JUnit Platform(基础框架) + JUnit Jupiter(核心) + JUnit Vintage(向下兼容)
  • JUnit Platform: 在JVM上启动测试框架的基础,不仅支持Junit自制的测试引擎,其他测试引擎也都可以接入
  • JUnit Jupiter: JUnit Jupiter 提供了 JUnit5 的新的编程模型,是 JUnit5 新特性的核心。内部 包含了一个测试引擎,用于在Junit Platform上运行
  • JUnit Vintage: JUnit Vintage提供了兼容JUnit4.x,Junit3.x的测试引擎

2 常用注解

  • @Test :标注方法是测试方法
  • @ParameterizedTest:表示方法是参数化测试
  • @RepeatedTest:重复执行测试方法
  • @DisplayName:为测试类或者测试方法设置展示名称
  • @BeforeEach :在每个单元测试之前执行
  • @AfterEach :在每个单元测试之后执行
  • @BeforeAll :在所有单元测试之前执行,方法需要 static 修饰
  • @AfterAll :在所有单元测试之后执行,方法需要 static 修饰
  • @Tag :表示单元测试类别
  • @Disabled :不执行测试类或测试方法
  • @Timeout :测试方法运行如果超过了指定时间,将会返回错误
  • @ExtendWith :为测试类或测试方法提供扩展类引用

3 断言 assert

  1. 简单断言
方法说明
assertEquals判断两个对象或两个原始类型是否相等
assertEquals判断两个对象或两个原始类型是否相等
assertNotEquals判断两个对象或两个原始类型是否不相等
assertSame判断两个对象引用是否指向同一个对象
assertNotSame判断两个对象引用是否指向不同的对象
assertTrue判断给定的布尔值是否为 true
assertFalse判断给定的布尔值是否为 false
assertNull判断给定的对象引用是否为 null
assertNotNull判断给定的对象引用是否不为 null
  1. 数组断言
  • 通过 assertArrayEquals(...) 方法来判断两个对象或原始类型的数组是否相等
  • 对于对象类型数组,比较的方式是逻辑等于,即调用 equals 方法
@Test
@DisplayName("array assertion")
public void array() {
	assertArrayEquals(new int[]{1, 2}, new int[] {1, 2});
}
  1. 组合断言
  • 要求一系列断言同时满足
  • 使用 lambda 表达式提供方法实参(lambda 表达式对应函数式编程)
@Test
@DisplayName("assert all")
public void all() {
 	assertAll("Math",
    	() -> assertEquals(2, 1 + 1),
    	() -> assertTrue(1 > 0)
 	);
}
  1. 异常断言
  • assertThrows 方法需要的参数:异常类型的 class 属性,需要抛出异常的语句的 lambda 表达式
@Test
@DisplayName("异常测试")
public void exceptionTest() {
    Assertions.assertThrows(ArithmeticException.class, () -> System.out.println(1 % 0));
}
  1. 超时断言
@Test
@DisplayName("超时测试")
public void timeoutTest() {
    Assertions.assertTimeout(Duration.ofMillis(1000), () -> Thread.sleep(500));
}
  1. 快速失败
@Test
@DisplayName("fail")
public void shouldFail() {
 	fail("This should fail");
}

4 假设 assumption

  • 假设作为单元测试的前提条件,如果前提条件不满足则没有进行测试的必要
  • 不满足的断言会使得测试方法失败;不满足的前置条件只会使得测试方法的执行终止,而不会引起测试失败
  • assumeTrueassumFalse 确保给定的条件为 true 或 false,不满足条件会使得测试执行终止
  • assumingThat 的参数是表示条件的布尔值和对应的 Executable 接口的实现对象,只有条件满足时,Executable 对象才会被执行;当条件不满足时,测试执行并不会终止
@DisplayName("前置条件")
public class AssumptionsTest {
 private final String environment = "DEV";
 
 @Test
 public void simpleAssume() {
    assumeTrue(Objects.equals(this.environment, "DEV"));
    assumeFalse(() -> Objects.equals(this.environment, "PROD"));
 }
 
 @Test
 public void assumeThenDo() {
    assumingThat(
       Objects.equals(this.environment, "DEV"),
       () -> System.out.println("In DEV")
    );
 }
}

5 嵌套测试

  • 目的是将测试过程结构化,把相关的测试方法组织在一起
  • 对测试类及其内部类使用 @Nested 注解,实现嵌套测试
@DisplayName("A stack")
class TestingAStackDemo {

    Stack<Object> stack;

	// 最外层测试方法
    @Test
    void isInstantiatedWithNew() {
        new Stack<>();
    }

	// 嵌套第一层测试类
    @Nested
    class WhenNew {
        @BeforeEach
        void createNewStack() {
            stack = new Stack<>();
        }

        @Test
        void isEmpty() {
            assertTrue(stack.isEmpty());
        }

		// 嵌套第二层测试类
        @Nested
        @DisplayName("after pushing an element")
        class AfterPushing {
        
            String anElement = "an element";

            @BeforeEach
            void pushAnElement() {
                stack.push(anElement);
            }

            @Test
            @DisplayName("it is no longer empty")
            void isNotEmpty() {
                assertFalse(stack.isEmpty());
            }
        }
    }
}

八 指标监控

  • 微服务在云上部署以后,都需要对其进行监控、追踪、审计、控制等。SpringBoot 抽取了Actuator 场景,使得每个微服务可获得生产级别的应用监控、审计等功能

1 开启方法

  1. 引入场景依赖
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
  1. 配置暴露端点(endpoint)及暴露方式
management:
  endpoints:
    enabled-by-default: true #暴露所有端点信息
    web:
      exposure:
        include: '*'  #以web方式暴露
  1. 访问 http://localhost:8080/actuator/... 监控对应指标

2 常用端点

  • Health:健康状况
  • Metrics:运行时指标
  • Loggers:日志记录
  • 0
    点赞
  • 4
    收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:数字20 设计师:CSDN官方博客 返回首页
评论

打赏作者

MONA ODYSSEY

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值