【SpringBoot笔记21】SpringBoot框架使用AOP + 自定义注解实现请求日志记录

news2024/11/30 10:52:28

这篇文章,主要介绍SpringBoot框架使用AOP + 自定义注解实现请求日志记录。

目录

一、SpringBoot记录日志

1.1、环境搭建

1.2、配置FastJson

1.3、自定义LogRecord注解

1.4、定义日志实体类

1.5、创建HttpRequestUtil工具类

1.6、定义AOP切面

1.7、编写测试类

1.8、运行测试


一、SpringBoot记录日志

1.1、环境搭建

  • 搭建SpringBoot工程。
  • 引入【spring-boot-starter-parent】依赖。
  • 引入【spring-boot-starter-web】依赖。
  • 引入【spring-boot-starter-aop】依赖。
  • 引入【fastjson】依赖。
<!-- 引入 SpringBoot 父工程依赖 -->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.3.0.RELEASE</version>
</parent>

<!-- 引入 web 依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <!-- 排除 jackson 依赖 -->
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-json</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<!-- 引入 aop 依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- 引入 fastjson 依赖 -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.77</version>
</dependency>

1.2、配置FastJson

SpringBoot集成FastJson可以看下我之前的笔记【【SpringBoot笔记07】SpringBoot框架集成FastJson处理数据】。

package com.spring.boot.demo.config;
 
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
 
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
 
/**
 * @author ZhuYouBin
 * @version 1.0.0
 * @Date: 2022/11/02 12:47
 * @Description FastJson 配置类
 */
@Configuration
public class CustomFastJsonConfig {
 
    @Bean
    public HttpMessageConverters fastjsonHttpMessageConverters() {
        // 创建 FastJsonHttpMessageConverter 消息转换器对象
        FastJsonHttpMessageConverter fastJsonHttpMessageConverter = new FastJsonHttpMessageConverter();
 
        // 创建 FastJsonConfig 配置类对象
        FastJsonConfig fastJsonConfig = new FastJsonConfig();
        // 设置编码字符集
        fastJsonConfig.setCharset(StandardCharsets.UTF_8);
        // 设置日期格式
        fastJsonConfig.setDateFormat("yyyy-MM-dd HH:mm:ss");
 
        // 设置序列化特征: SerializerFeature 是一个枚举,可以选择不同的序列化特征
        SerializerFeature[] serializerFeatures = new SerializerFeature[] {
                // WriteNullStringAsEmpty: 如果字符串等于 null,那么会被序列化成空字符串 ""
                SerializerFeature.WriteNullStringAsEmpty,
                // WriteNullNumberAsZero: 如果数字等于 null,那么会被序列化成 0
                SerializerFeature.WriteNullNumberAsZero,
                // WriteNullBooleanAsFalse: 如果布尔类型等于 null,那么会被序列化成 false
                SerializerFeature.WriteNullBooleanAsFalse,
                // PrettyFormat: 美化JSON
                SerializerFeature.PrettyFormat
        };
        fastJsonConfig.setSerializerFeatures(serializerFeatures);
 
        // 配置添加到消息转换器里面
        fastJsonHttpMessageConverter.setDefaultCharset(StandardCharsets.UTF_8);
        fastJsonHttpMessageConverter.setFastJsonConfig(fastJsonConfig);
 
        // 设置响应JSON格式数据
        List<MediaType> mediaTypeList = new ArrayList<>();
        mediaTypeList.add(MediaType.APPLICATION_JSON); // JSON 格式数据
        // 设置消息转换器支持的格式
        fastJsonHttpMessageConverter.setSupportedMediaTypes(mediaTypeList);
 
        // 返回消息转换器
        return new HttpMessageConverters(fastJsonHttpMessageConverter);
    }
 
}

1.3、自定义LogRecord注解

  • 这里我们自定义一个@LogRecord注解,该注解使用在方法上面,用于标记AOP切面会拦截这个方法,并且记录请求日志信息。
package com.spring.boot.demo.anno;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author ZhuYouBin
 * @version 1.0.0
 * @Date: 2022/11/02 12:47
 * @Description 自定义日志注解
 */
// 注解可以保留到运行期间
@Retention(RetentionPolicy.RUNTIME)
// 注解使用在方法上面
@Target(ElementType.METHOD)
public @interface LogRecord {

    /**
     * 操作名称
     */
    String opName();
    
    /**
     * 描述信息
     */
    String desc() default "";
}

1.4、定义日志实体类

为了能够收集请求日志的信息,这里定义一个日志实体类来保存每一次请求的日志信息。

package com.spring.boot.demo.pojo;

import java.io.Serializable;

/**
 * @author ZhuYouBin
 * @version 1.0.0
 * @Date: 2022/11/2 22:45
 * @Description 日志实体类
 */
public class LogRecordEntity implements Serializable {
    /** 日志唯一标识 */
    private String id;
    /** 操作名称 */
    private String opName;
    /** 请求路径 */
    private String path;
    /** 请求方式 */
    private String method;
    /** 请求IP地址 */
    private String requestIp;
    /** 全限定类名称 */
    private String qualifiedName;
    /** 请求入参 */
    private String inputParam;
    /** 请求出参 */
    private String outputParam;
    /** 异常信息 */
    private String errorMsg;
    /** 请求开始时间 */
    private String requestTime;
    /** 请求响应时间 */
    private String responseTime;
    /** 接口耗时,单位:ms */
    private String costTime;
    /** 请求是否成功 */
    private String status;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getOpName() {
        return opName;
    }

    public void setOpName(String opName) {
        this.opName = opName;
    }

    public String getPath() {
        return path;
    }

    public void setPath(String path) {
        this.path = path;
    }

    public String getMethod() {
        return method;
    }

    public void setMethod(String method) {
        this.method = method;
    }

    public String getRequestIp() {
        return requestIp;
    }

    public void setRequestIp(String requestIp) {
        this.requestIp = requestIp;
    }

    public String getQualifiedName() {
        return qualifiedName;
    }

    public void setQualifiedName(String qualifiedName) {
        this.qualifiedName = qualifiedName;
    }

    public String getInputParam() {
        return inputParam;
    }

    public void setInputParam(String inputParam) {
        this.inputParam = inputParam;
    }

    public String getOutputParam() {
        return outputParam;
    }

    public void setOutputParam(String outputParam) {
        this.outputParam = outputParam;
    }

    public String getErrorMsg() {
        return errorMsg;
    }

    public void setErrorMsg(String errorMsg) {
        this.errorMsg = errorMsg;
    }

    public String getRequestTime() {
        return requestTime;
    }

    public void setRequestTime(String requestTime) {
        this.requestTime = requestTime;
    }

    public String getResponseTime() {
        return responseTime;
    }

    public void setResponseTime(String responseTime) {
        this.responseTime = responseTime;
    }

    public String getCostTime() {
        return costTime;
    }

    public void setCostTime(String costTime) {
        this.costTime = costTime;
    }

    public String getStatus() {
        return status;
    }

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

    @Override
    public String toString() {
        return "LogRecordEntity{" +
                "id='" + id + '\'' +
                ", opName='" + opName + '\'' +
                ", path='" + path + '\'' +
                ", method='" + method + '\'' +
                ", requestIp='" + requestIp + '\'' +
                ", qualifiedName='" + qualifiedName + '\'' +
                ", inputParam='" + inputParam + '\'' +
                ", outputParam='" + outputParam + '\'' +
                ", errorMsg='" + errorMsg + '\'' +
                ", requestTime='" + requestTime + '\'' +
                ", responseTime='" + responseTime + '\'' +
                ", costTime='" + costTime + '\'' +
                ", status='" + status + '\'' +
                '}';
    }
}

1.5、创建HttpRequestUtil工具类

  • 创建一个获取HTTP请求和响应对象的工具类,在SpringBoot框架中,可以通过RequestContextHolder类获取到HTTP请求属性对象,通过该对象可以获取到Request、Response对象。
package com.spring.boot.demo.util;

import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @author ZhuYouBin
 * @version 1.0.0
 * @Date: 2022/11/2 23:03
 * @Description HTTP请求的工具类,用于获取Request、Response相关信息
 */
public final class HttpRequestUtil {

    /**
     * 从 SpringBoot 中获取 Request 请求对象
     * @return 返回当前请求的 Request 对象
     */
    public static HttpServletRequest getRequest() {
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        if (requestAttributes == null) {
            return null;
        }
        ServletRequestAttributes attributes = (ServletRequestAttributes) requestAttributes;
        return attributes.getRequest();
    }

    /**
     * 从 SpringBoot 中获取 Response 请求对象
     * @return 返回当前请求的 Response 对象
     */
    public static HttpServletResponse getResponse() {
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        if (requestAttributes == null) {
            return null;
        }
        ServletRequestAttributes attributes = (ServletRequestAttributes) requestAttributes;
        return attributes.getResponse();
    }

}

1.6、定义AOP切面

package com.spring.boot.demo.config;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.spring.boot.demo.anno.LogRecord;
import com.spring.boot.demo.pojo.LogRecordEntity;
import com.spring.boot.demo.util.HttpRequestUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Method;
import java.util.UUID;

/**
 * @author ZhuYouBin
 * @version 1.0.0
 * @Date: 2022/11/2 12:52
 * @Description 自定义日志切面
 */
// 标记当前类是一个切面类
@Aspect
// 将当前类放入IOC容器
@Component
public class LogAspect {

    /**
     * 创建线程局部变量
     */
    private ThreadLocal<LogRecordEntity> threadLocal = new ThreadLocal<>();

    /**
     * 定义切入点,这里我们使用AOP切入自定义【@LogRecord】注解的方法
     */
    @Pointcut("@annotation(com.spring.boot.demo.anno.LogRecord)")
    public void pointCut() {}

    /**
     * 前置通知,【执行Controller方法之前】执行该通知方法
     */
    @Before("pointCut()")
    public void beforeAdvice() {
        System.out.println("前置通知......"); // TODO delete
    }

    /**
     * 后置通知,【Controller方法执行完成,返回方法的返回值之前】执行该通知方法
     */
    @After("pointCut()")
    public void afterAdvice() {
        System.out.println("后置通知......"); // TODO delete
    }

    /**
     * 环绕通知,执行Controller方法的前后执行
     * @param joinPoint 连接点
     */
    @Around("pointCut()")
    public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
        // 请求开始时间
        String requestTime = String.valueOf(System.currentTimeMillis());
        System.out.println("环绕通知之前....."); // TODO delete
        // 获取当前请求对象
        HttpServletRequest request = HttpRequestUtil.getRequest();
        if (request == null) {
            return null;
        }
        // 获取请求相关信息
        LogRecordEntity entity = new LogRecordEntity();
        entity.setId(UUID.randomUUID().toString().replace("-", ""));
        entity.setPath(request.getRequestURI());
        entity.setMethod(request.getMethod());
        entity.setRequestIp(request.getRemoteHost());
        entity.setRequestTime(requestTime);

        // 反射获取调用方法
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        if (method.isAnnotationPresent(LogRecord.class)) {
            // 获取注解信息
            LogRecord annotation = method.getAnnotation(LogRecord.class);
            entity.setOpName(annotation.opName());
        }
        // 获取全限定类名称
        String name = method.getName();

        // 获取请求参数
        String inputParam = JSONObject.toJSONString(joinPoint.getArgs());
        entity.setInputParam(inputParam);
        // 设置局部变量
        threadLocal.set(entity);

        // 调用Controller方法
        Object ret = joinPoint.proceed();
        System.out.println("环绕通知之后....."); // TODO delete
        return ret;
    }

    /**
     * 返回值通知,Controller执行完成之后,返回方法的返回值时候执行
     * @param ret 返回值的名称
     */
    @AfterReturning(pointcut = "pointCut()", returning = "ret")
    public Object afterReturning(Object ret) {
        System.out.println("返回值通知......ret=" + ret); // TODO delete
        // 获取日志实体对象
        LogRecordEntity entity = this.getEntity();
        String outputParam = JSON.toJSONString(ret);
        entity.setOutputParam(outputParam); // 保存响应参数
        entity.setStatus("成功"); // 设置成功标识
        // TODO 这里就可以做一些持久胡操作,例如:保存日志到数据表里面
        // 一定要删除 ThreadLocal 变量
        threadLocal.remove();
        System.out.println(entity); // TODO delete
        return ret;
    }

    /**
     * 异常通知,当Controller方法执行过程中出现异常时候,执行该通知
     * @param ex 异常名称
     */
    @AfterThrowing(pointcut = "pointCut()", throwing = "ex")
    public void throwingAdvice(Throwable ex) {
        System.out.println("异常通知......"); // TODO delete
        // 获取日志实体对象
        LogRecordEntity entity = this.getEntity();
        StringWriter errorMsg = new StringWriter();
        ex.printStackTrace(new PrintWriter(errorMsg, true));
        entity.setErrorMsg(errorMsg.toString()); // 保存响应参数
        entity.setStatus("失败"); // 设置成功标识
        // TODO 这里就可以做一些持久胡操作,例如:保存日志到数据表里面
        // 一定要删除 ThreadLocal 变量
        threadLocal.remove();
        System.out.println(entity); // TODO delete
    }

    /****************************************************/

    private LogRecordEntity getEntity() {
        // 获取局部变量
        LogRecordEntity entity = threadLocal.get();
        long start = Long.parseLong(entity.getRequestTime());
        long end = System.currentTimeMillis();
        // 获取响应时间、耗时
        entity.setCostTime((end - start) + "ms");
        entity.setResponseTime(String.valueOf(end));
        return entity;
    }
}

1.7、编写测试类

package com.spring.boot.demo.controller;

import com.spring.boot.demo.anno.LogRecord;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author ZhuYouBin
 * @version 1.0.0
 * @Date: 2022/11/2 22:58
 * @Description
 */
@RestController
@RequestMapping("/api/aop")
public class LogController {

    @LogRecord(opName = "测试日志", desc = "测试日志描述内容")
    @GetMapping("/log")
    public String demo() {
        System.out.println("开始执行业务逻辑代码......");
        return "success.";
    }

    @LogRecord(opName = "测试日志", desc = "测试日志描述内容")
    @GetMapping("/error")
    public String error() {
        System.out.println("开始执行业务逻辑代码......");
        int i = 10 / 0;
        return "success.";
    }

}

1.8、运行测试

启动工程,浏览器分别访问两个地址【http://127.0.0.1:8080/api/aop/log】和【http://127.0.0.1:8080/api/aop/error】,查看控制台日志输出。

到此,SpringBoot利用AOP和自定义注解实现日志记录就成功啦。

综上,这篇文章结束了,主要介绍SpringBoot框架使用AOP + 自定义注解实现请求日志记录。

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

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

相关文章

arm服务器运行onlyoffice

公司的arm服务器上运行onlyoffice报错 是x86_64架构的镜像在arm服务器下不兼容 需要下载兼容arm架构的docker镜像 在dockerhub上 onlyoffice地址 Docker Hubhttps://hub.docker.com/r/onlyoffice/documentserver 下载兼容arm架构的镜像版本 # docker --version Docker ver…

《从0开始写一个微内核操作系统》5-页表映射

ChinOS https://github.com/jingjin666/GN-base/tree/chinos 页表需要多少空间 在第4节中&#xff0c;我们了解到&#xff0c;每一级页表实际上就是一个512大小的unsigned long数组&#xff0c;一个页表本身占用512*84K空间 /* PGD */ /* 每个ENTRY包含512G内存区域 */ typed…

JavaWeb传统商城(MVC三层架构)的促销功能模块【进阶版】

文章目录一.JavaWeb商城项目的促销功能模块【进阶版】开发过程记录1.1 项目背景1.2 需求分析1.3 开发流程/顺序二.促销页面(0.1颗星)2.1 需求介绍2.2 JSP页面2.3效果展示三,商品详情页面(0.2颗星)3.1 需求介绍和效果图3.2 数据库分析3.2 Servlet层3.3 Service层3.4 DAO层3.5 JS…

一本通1064;奥运奖牌计数

#include <iostream> using namespace std; int main() {int n, Jin, Yin, Tong;int JinSum 0, YinSum 0, TongSum 0, sum;cin >> n;for (int i 1; i < n; i) // 循环n次{cin >> Jin >> Yin >> Tong; // 输入一天获得的金银铜牌数JinSum …

InfluxDB学习记录(三)——influxdb的flux语法

什么是Flux Flux 是 InfluxData 的功能性数据脚本语言&#xff0c;设计用于查询、分析和处理数据&#xff0c;它是InfluxQL 和其他类似 SQL 的查询语言的替代品。 每个 Flux 查询都需要以下内容&#xff1a; 数据源时间范围数据过滤器 Flux代码示例 from(bucket:"example…

重装系统后打印机状态已暂停如何恢复

​当我们在使用打印机打印文件的时候&#xff0c;有时候会发现打印机状态已暂停&#xff0c;打印不下去了&#xff0c;这时候怎么恢复呢&#xff0c;其实只需要取消掉打印暂停就可以了&#xff0c;下面就和大家讲讲重装系统后打印机状态已暂停如何恢复吧。 打印机状态已暂停怎…

【前端】Vue+Element UI案例:通用后台管理系统-Home组件:卡片、表格

文章目录目标代码0.布局1.左上User卡片2.左下table卡片2.1数据&#xff1a;TableData.js2.2table2.3代码优化&#xff1a;循环3.右上数据卡片3.1数据&#xff1a;CountData3.2结构3.3布局3.4样式总代码Home.vue参考目标 红框内部分都是卡片&#xff0c;鼠标悬停会有阴影左下是表…

java计算机毕业设计基于安卓Android的天文观星系统app uniapp 小程序

项目介绍 信息技术的发展带来了大量的数据内容,在这些数据中,想要找到自己需要的只有通过搜索引擎。如今,通过百度去查找信息成为大众的首选,然而在经济利益的驱动下,许多百度来的信息都是商业内容,很难找到真实有用的实际信息。在互联网中平台,天文信息交流和资源共享是一个非…

【每日训练】进制转换

目录 题目链接&#xff1a; 测试用例&#xff1a; 解析&#xff1a; 程序&#xff1a; 题目链接&#xff1a; 进制转换_牛客题霸_牛客网 (nowcoder.com) 测试用例&#xff1a; 解析&#xff1a; 题目描述&#xff1a; 输入一个十进制数&#xff0c;转化为对应输入的几进制数…

微服务及其在app自动化领域的应用

微服务是一种软件开发技术- 面向服务的体系结构&#xff08;SOA&#xff09;架构样式的一种变体&#xff0c;它提倡将单一应用程序划分成一组小的服务&#xff0c;服务之间互相协调、互相配合&#xff0c;为用户提供最终价值。每个服务运行在其独立的进程中&#xff0c;服务与服…

5款可视化工具优缺点比对,谁赢了?

利用Excel表格进行汇报&#xff0c;底下坐着的领导可能会看起来眼花缭乱&#xff0c;但如果是以图表可视化的形式展现出来&#xff0c;那可简洁明了多了&#xff0c;不仅仅可以看到某个项目近几个月的走势&#xff0c;并且还能知道之后的决策。 可视化图表用什么工具做&#xf…

FP8训练调研

FP8训练调研 一、FP8训练相关技术要点总结 1、基于块的累加技术&#xff0c;减小低精度数之间相加的累积误差 2、随机舍入技术代替四舍五入&#xff0c;降低舍入误差 3、混合FP8技术&#xff0c;用1-4-3进行前向&#xff0c;1-5-2进行反向 4、设置指数偏移&#xff0c;使F…

windows搭建WebDAV服务,并内网穿透公网访问【无公网IP】

自己用Windows Server搭建了家用NAS主机&#xff0c;WebDAV的文件共享方式当然也是必不可少的。 本文使用的是WIN10 专业版。 1. 安装IIS必要WebDav组件 1.1 打开控制面板&#xff0c;查看方式改为“类别”&#xff0c;进入“程序”&#xff0c;“启用或关闭Windows功能” 1…

数据结构链表之无头单向循环链表的实现

文章目录前言1.链表的相关介绍1.什么是节点2.链表和顺序表的对比3.链表的种类2.链表的实现1.节点的定义和创建2.链表的相关函数接口的实现1.链表的创建2.数据的插入头插尾插指定位置插入3.数据的删除头删尾删指定位置删除4.打印显示节点数据5.数据查找6.链表销毁3.总结前言 之…

2022新版加壳工具-支持.NET虚拟化加密

.NET 虚拟化保护 .NET 程序的保护技术在对抗中不断演进&#xff0c;出现了控制流混淆、名称混淆、文件加壳、动态方法、JIT 加密等保护技术&#xff0c;这些保护技术都有其各自的优缺点&#xff0c;虽然组合起来也能达到一定的效果&#xff0c;但近几年已经流传出一些脱壳机和…

单点架构、集群架构、服务化架构、SOA、微服务到底有什么联系和关系?

本篇参考总结 IT老齐的学习笔记 原视频SOA面向服务架构 原视频智慧城市实践指南 &#xff08;书籍-SOA概述&#xff09; 最近我在负责研发智慧园区的智慧平台产品&#xff0c;目前需求阶段和设计阶段已经完成&#xff0c;正式开始开发阶段&#xff0c;但是作为一个算法类学习者…

redies基本数据结构

nosql数据库 和sql结构的数据库 1.结构化的&#xff0c;不设置表之间的结构 2.没有主外键约束&#xff0c;之间没有关联 3.nosql&#xff0c;其他的数据库不能使用redies的语法 4.没有事务&#xff0c;不符合ACID 5.redies存储在内存中&#xff0c;速度非常快 是一个键值…

Docker入门学习:基本概念、安装、命令、简单使用

前言 一、基本概念 1、Docker镜像 镜像就是一个文件&#xff0c;例如我们的应用镜像、环境镜像&#xff08;例如nginx、mysql等&#xff09;&#xff0c;镜像是创建Docker容器的基础。 2、Docker容器 Docker容器类似于一个沙箱&#xff08;例如做支付的时候的支付宝的沙箱…

【Java|golang】1668. 最大重复子字符串

给你一个字符串 sequence &#xff0c;如果字符串 word 连续重复 k 次形成的字符串是 sequence 的一个子字符串&#xff0c;那么单词 word 的 重复值为 k 。单词 word 的 最大重复值 是单词 word 在 sequence 中最大的重复值。如果 word 不是 sequence 的子串&#xff0c;那么重…

系统学习SpringFramework:SpringBean的注入方式

本篇内容包括&#xff1a;Spring 容器简介&#xff08;什么是容器、容器如何工作&#xff09;、SpringBean 注入方式&#xff08;SpringBean 注入方式分类、Autowiring 自动绑定&#xff09;以及 获取 Spring Bean 的工具类&#xff01; 一、Spring 容器 1、什么是容器 Sprin…