从开源项目中学习如何自定义 Spring Boot Starter 小组件

前言

今天参考的开源组件Graceful Response——Spring Boot接口优雅响应处理器。

具体用法可以参考github以及官方文档。

image-20231214090633021

基本使用

引入Graceful Response组件

项目中直接引入如下maven依赖,即可使用其相关功能。

        <dependency><groupId>com.feiniaojin</groupId><artifactId>graceful-response</artifactId><version>{latest.version}</version></dependency>

开始使用

配置文件可不配置,直接全部默认配置

graceful-response:response-style: 1  # 配置响应格式类型(0 or 1)后续介绍print-exception-in-global-advice: true # 在全局异常处理器中打印异常,默认不打印

一切之前需在主启动类添加该注解开启功能:@EnableGracefulResponse。(后续介绍)

Controller直接返回我们查询的对象即可,Graceful Response会帮我们封装响应格式(响应格式可配置

Controller

@RestController
public class HelloController {@Resourceprivate UserMapper userMapper;@GetMapping("/userList")public List<User> userList() {return userMapper.selectList(null);}
}

调用接口:

image-20231214113454576

默认成功/失败状态码和提示消息也都是可以配置的,是不是感觉非常的神奇(一切皆可配)

image-20231214114109561

其他具体用法见官网。

废话不多说,接下来我们直接步入主题探究下自定义starter组件核心步骤

自定义starter组件核心步骤

首先明确一下,自定义starter组件的三个核心步骤:

  1. 自定义注解
  2. 结合Spring AOP实现注解逻辑
  3. 实现starter组件自动装配以及可配置

接下来看开源组件都是怎么做的吧~

看看源码目录结构(麻雀虽小但五脏俱全):

image-20231214103700335

0. 注解使用

@ExceptionMapper注解为例,介绍一下相关功能及实现原理。

Graceful Response引入@ExceptionMapper注解,通过该注解将异常和错误码关联起来,这样Service方法就不需要再维护Response的响应码了,直接抛出业务异常,由Graceful Response进行异常和响应码的关联。 @ExceptionMapper的用法如下:

自定义业务类异常

/*** NotFoundException的定义,使用@ExceptionMapper注解修饰* code:代表接口的异常码* msg:代表接口的异常提示*/
@ExceptionMapper(code = "1404", msg = "找不到对象")
public class NotFoundException extends RuntimeException {}

Service接口定义

public interface QueryService {UserInfoView queryOne(Query query);
}

Service接口实现

@Service
public class QueryServiceImpl implements QueryService {@Resourceprivate UserInfoMapper mapper;public UserInfoView queryOne(Query query) {UserInfo userInfo = mapper.findOne(query.getId());if (Objects.isNull(userInfo)) {// 这里直接抛自定义异常throw new NotFoundException();}// ……后续业务操作}
}

当Service层的queryOne方法抛出NotFoundException时,Graceful Response会进行异常捕获,并将NotFoundException对应的异常码和异常信息封装到统一的响应对象中,最终接口返回以下JSON(默认响应格式

{"status": {"code": "1404","msg": "找不到对象"},"payload": {}
}

使用起来十分方便,接下来我们看下具体实现原理。

1. 自定义注解

首先看下注解定义:

/*** 异常映射注解.*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface ExceptionMapper {/*** 异常对应的错误码.** @return 异常对应的错误码*/String code() default "ERROR";/*** 异常信息.** @return 异常对应的提示信息*/String msg() default "Poor network quality!";/*** 异常信息是否支持替换* 仅当msgReplaceable==ture,且异常实例的message不为空时才能替换*/boolean msgReplaceable() default false;
}

2. 结合Spring AOP实现注解逻辑(核心)

配置类

配置类GracefulResponseProperties跟可配置项相关,所有配置项都映射为该类

@ConfigurationProperties(prefix = "graceful-response") // 配置前缀
public class GracefulResponseProperties {/*** 在全局异常处理器中打印异常,默认不打印*/private boolean printExceptionInGlobalAdvice = false;/*** 默认的Response实现类名称,配置了responseClassFullName,则responseStyle不生效*/private String responseClassFullName;/*** responseStyle的风格,responseClassFullName为空时才会生效* responseStyle==null或者responseStyle==0,Response风格为 DefaultResponseImplStyle0* responseStyle=1,Response风格为 DefaultResponseImplStyle1*/private Integer responseStyle;/*** 默认的成功返回码 默认0*/private String defaultSuccessCode = DefaultConstants.DEFAULT_SUCCESS_CODE;/*** 默认的成功提示 默认OK*/private String defaultSuccessMsg = DefaultConstants.DEFAULT_SUCCESS_MSG;/*** 默认的失败码 默认1*/private String defaultErrorCode = DefaultConstants.DEFAULT_ERROR_CODE;/*** 默认的失败提示 默认error*/private String defaultErrorMsg = DefaultConstants.DEFAULT_ERROR_MSG;/*** Validate异常码,不提供的话默认DefaultConstants.DEFAULT_ERROR_CODE*/private String defaultValidateErrorCode = DefaultConstants.DEFAULT_ERROR_CODE;/*** 例外包路径*/private List<String> excludePackages;/*** 不使用@ExceptionMapper和@ExceptionAliasFor修饰的原生异常* 是否使用异常信息Throwable类的detailMessage进行返回* originExceptionUsingDetailMessage=false,则msg=defaultErrorMsg*/private Boolean originExceptionUsingDetailMessage = false;// getter / setter
}

设置响应状态

ResponseStatusFactory:响应状态工厂(仅定义code和msg),默认实现成功默认响应、失败默认响应以及自定义code、msg响应状态

默认实现类:

// 响应状态
public interface ResponseStatus {void setCode(String code);String getCode();void setMsg(String msg);String getMsg();
}// 默认响应状态
public class DefaultResponseStatus implements ResponseStatus {private String code;private String msg;public DefaultResponseStatus() {}public DefaultResponseStatus(String code, String msg) {this.code = code;this.msg = msg;}@Overridepublic void setCode(String code) {this.code = code;}@Overridepublic String getCode() {return code;}@Overridepublic void setMsg(String msg) {this.msg = msg;}@Overridepublic String getMsg() {return msg;}
}
// 核心实现类
public class DefaultResponseStatusFactoryImpl implements ResponseStatusFactory {@Resourceprivate GracefulResponseProperties properties;// 默认成功响应转态@Overridepublic ResponseStatus defaultSuccess() {DefaultResponseStatus defaultResponseStatus = new DefaultResponseStatus();defaultResponseStatus.setCode(properties.getDefaultSuccessCode());defaultResponseStatus.setMsg(properties.getDefaultSuccessMsg());return defaultResponseStatus;}// 默认失败响应转态@Overridepublic ResponseStatus defaultError() {DefaultResponseStatus defaultResponseStatus = new DefaultResponseStatus();defaultResponseStatus.setCode(properties.getDefaultErrorCode());defaultResponseStatus.setMsg(properties.getDefaultErrorMsg());return defaultResponseStatus;}// 自定code、msg状态@Overridepublic ResponseStatus newInstance(String code, String msg) {return new DefaultResponseStatus(code, msg);}
}

设置响应格式

ResponseFactory:根据配置项,设置响应格式

默认实现类:

public class DefaultResponseFactory implements ResponseFactory {private final Logger logger = LoggerFactory.getLogger(DefaultResponseFactory.class);private static final Integer RESPONSE_STYLE_0 = 0;private static final Integer RESPONSE_STYLE_1 = 1;@Resourceprivate ResponseStatusFactory responseStatusFactory;@Resourceprivate GracefulResponseProperties properties;@Overridepublic Response newEmptyInstance() {try {String responseClassFullName = properties.getResponseClassFullName();// 配置了Response的全限定名,即实现Response接口,用配置的进行返回if (StringUtils.hasLength(responseClassFullName)) {Object newInstance = Class.forName(responseClassFullName).getConstructor().newInstance();return (Response) newInstance;} else {// 没有配Response的全限定名,则创建DefaultResponsereturn generateDefaultResponse();}} catch (Exception e) {throw new RuntimeException(e);}}// 响应格式判断private Response generateDefaultResponse() {Integer responseStyle = properties.getResponseStyle();// 未配置或者配置0if (Objects.isNull(responseStyle) || RESPONSE_STYLE_0.equals(responseStyle)) {return new DefaultResponseImplStyle0();} else if (RESPONSE_STYLE_1.equals(responseStyle)) {return new DefaultResponseImplStyle1();} else {logger.error("不支持的Response style类型,responseStyle={}", responseStyle);throw new IllegalArgumentException("不支持的Response style类型");}}@Overridepublic Response newInstance(ResponseStatus responseStatus) {Response bean = this.newEmptyInstance();bean.setStatus(responseStatus);return bean;}@Overridepublic Response newSuccessInstance() {Response emptyInstance = this.newEmptyInstance();emptyInstance.setStatus(responseStatusFactory.defaultSuccess());return emptyInstance;}@Overridepublic Response newSuccessInstance(Object payload) {Response bean = this.newSuccessInstance();bean.setPayload(payload);return bean;}@Overridepublic Response newFailInstance() {Response bean = this.newEmptyInstance();bean.setStatus(responseStatusFactory.defaultError());return bean;}}

对应配置文件中两种响应格式:

image-20231214140225763

默认响应格式:

public interface Response {void setStatus(ResponseStatus statusLine);ResponseStatus getStatus();void setPayload(Object payload);Object getPayload();
}

image-20231214142333843

默认格式实现类:

{"status": {"code": "","msg": ""},"payload": {}
}
public class DefaultResponseImplStyle0 implements Response {private ResponseStatus status;private Object payload = Collections.emptyMap();public DefaultResponseImplStyle0() {}public DefaultResponseImplStyle0(Object payload) {this.payload = payload;}@Overridepublic void setStatus(ResponseStatus responseStatus) {this.status = responseStatus;}@Overridepublic ResponseStatus getStatus() {return status;}@Overridepublic void setPayload(Object obj) {this.payload = obj;}@Overridepublic Object getPayload() {return payload;}
}

style设置为1,响应格式:

{"code": "1404","msg": "找不到对象""data": {}
}

另一种响应实现类

public class DefaultResponseImplStyle1 implements Response {private String code;private String msg;private Object data = Collections.emptyMap();@Overridepublic void setStatus(ResponseStatus statusLine) {this.code = statusLine.getCode();this.msg = statusLine.getMsg();}@Override@JsonIgnorepublic ResponseStatus getStatus() {return null;}@Overridepublic void setPayload(Object payload) {this.data = payload;}// 这里直接把 payload 忽略了(因此这种响应格式中没有payload字段)@Override@JsonIgnorepublic Object getPayload() {return null;}public String getCode() {return code;}public void setCode(String code) {this.code = code;}public String getMsg() {return msg;}public void setMsg(String msg) {this.msg = msg;}public Object getData() {return data;}public void setData(Object data) {this.data = data;}
}

核心处理逻辑类

核心处理逻辑类:全局异常处理

/*** 全局异常处理.*/
@ControllerAdvice
@Order(200)
public class GlobalExceptionAdvice implements ApplicationContextAware {private final Logger logger = LoggerFactory.getLogger(GlobalExceptionAdvice.class);@Resourceprivate ResponseStatusFactory responseStatusFactory;@Resourceprivate ResponseFactory responseFactory;private ExceptionAliasRegister exceptionAliasRegister;// 配置类@Resourceprivate GracefulResponseProperties properties;/*** 异常处理逻辑. 【统一捕获所有异常】** @param throwable 业务逻辑抛出的异常* @return 统一返回包装后的结果*/@ExceptionHandler({Throwable.class})@ResponseBody public Response exceptionHandler(Throwable throwable) {// 配置项是否打印异常信息(默认false)if (properties.isPrintExceptionInGlobalAdvice()) {logger.error("Graceful Response:GlobalExceptionAdvice捕获到异常,message=[{}]", throwable.getMessage(), throwable);}ResponseStatus statusLine;if (throwable instanceof GracefulResponseException) {statusLine = fromGracefulResponseExceptionInstance((GracefulResponseException) throwable);} else {// 校验异常转自定义异常statusLine = fromExceptionInstance(throwable);}// 设置完code、msg之后直接返回return responseFactory.newInstance(statusLine);}private ResponseStatus fromGracefulResponseExceptionInstance(GracefulResponseException exception) {String code = exception.getCode();if (code == null) {code = properties.getDefaultErrorCode();}return responseStatusFactory.newInstance(code,exception.getMsg());}private ResponseStatus fromExceptionInstance(Throwable throwable) {Class<? extends Throwable> clazz = throwable.getClass();// 【直接获取抛出异常上的注解】ExceptionMapper exceptionMapper = clazz.getAnnotation(ExceptionMapper.class);// 1.有@ExceptionMapper注解,直接设置结果的状态if (exceptionMapper != null) {boolean msgReplaceable = exceptionMapper.msgReplaceable();//异常提示可替换+抛出来的异常有自定义的异常信息if (msgReplaceable) {String throwableMessage = throwable.getMessage();if (throwableMessage != null) {return responseStatusFactory.newInstance(exceptionMapper.code(), throwableMessage);}}return responseStatusFactory.newInstance(exceptionMapper.code(),exceptionMapper.msg());}// 2.有@ExceptionAliasFor异常别名注解,获取已注册的别名信息if (exceptionAliasRegister != null) {ExceptionAliasFor exceptionAliasFor = exceptionAliasRegister.getExceptionAliasFor(clazz);if (exceptionAliasFor != null) {return responseStatusFactory.newInstance(exceptionAliasFor.code(),exceptionAliasFor.msg());}}ResponseStatus defaultError = responseStatusFactory.defaultError();// 3. 原生异常 + originExceptionUsingDetailMessage=true// 如果有自定义的异常信息,原生异常将直接使用异常信息进行返回,不再返回默认错误提示if (properties.getOriginExceptionUsingDetailMessage()) {String throwableMessage = throwable.getMessage();if (throwableMessage != null) {defaultError.setMsg(throwableMessage);}}return defaultError;}@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.exceptionAliasRegister = applicationContext.getBean(ExceptionAliasRegister.class);}
}

非空返回值封装处理类(ResponseBodyAdvice)

ResponseBodyAdvice:泛型T为响应data类型,直接Object即可

@ControllerAdvice
@Order(value = 1000)
public class NotVoidResponseBodyAdvice implements ResponseBodyAdvice<Object> {private final Logger logger = LoggerFactory.getLogger(NotVoidResponseBodyAdvice.class);@Resourceprivate ResponseFactory responseFactory;@Resourceprivate GracefulResponseProperties properties;/*** 路径过滤器*/private static final AntPathMatcher ANT_PATH_MATCHER = new AntPathMatcher();/*** 只处理不返回void的,并且MappingJackson2HttpMessageConverter支持的类型.** @param methodParameter 方法参数* @param clazz           处理器* @return 是否支持*/@Overridepublic boolean supports(MethodParameter methodParameter,Class<? extends HttpMessageConverter<?>> clazz) {Method method = methodParameter.getMethod();// method为空、返回值为void、非JSON,直接跳过if (Objects.isNull(method)|| method.getReturnType().equals(Void.TYPE)|| !MappingJackson2HttpMessageConverter.class.isAssignableFrom(clazz)) {logger.debug("Graceful Response:method为空、返回值为void、非JSON,跳过");return false;}// 有ExcludeFromGracefulResponse注解修饰的,也跳过if (method.isAnnotationPresent(ExcludeFromGracefulResponse.class)) {if (logger.isDebugEnabled()) {logger.debug("Graceful Response:方法被@ExcludeFromGracefulResponse注解修饰,跳过:methodName={}", method.getName());}return false;}// 配置了例外包路径,则该路径下的controller都不再处理List<String> excludePackages = properties.getExcludePackages();if (!CollectionUtils.isEmpty(excludePackages)) {// 获取请求所在类的的包名String packageName = method.getDeclaringClass().getPackage().getName();if (excludePackages.stream().anyMatch(item -> ANT_PATH_MATCHER.match(item, packageName))) {logger.debug("Graceful Response:匹配到excludePackages例外配置,跳过:packageName={},", packageName);return false;}}logger.debug("Graceful Response:非空返回值,需要进行封装");return true;}// 满足上述条件,封装返回对象(只要是非Void返回值,不做任何配置都会封装返回结果)@Overridepublic Object beforeBodyWrite(Object body,MethodParameter methodParameter,MediaType mediaType,Class<? extends HttpMessageConverter<?>> clazz,ServerHttpRequest serverHttpRequest,ServerHttpResponse serverHttpResponse) {if (body == null) {return responseFactory.newSuccessInstance();} else if (body instanceof Response) {return body;} else {// 设置data数据return responseFactory.newSuccessInstance(body);}}}

3. 实现starter组件自动装配以及可配置

全局自动装配类

该类可以将我们自定义的所有类全部加载到Spring容器中。

/*** 全局返回值处理的自动配置.*/
@Configuration
// 将外部配置文件中的属性注入到配置类中(将配置类加载到Spring容器中)
@EnableConfigurationProperties(GracefulResponseProperties.class)
public class AutoConfig {@Bean// 这个注解很是重要@ConditionalOnMissingBean(value = GlobalExceptionAdvice.class)public GlobalExceptionAdvice globalExceptionAdvice() {return new GlobalExceptionAdvice();}@Bean@ConditionalOnMissingBean(value = ValidationExceptionAdvice.class)public ValidationExceptionAdvice validationExceptionAdvice() {return new ValidationExceptionAdvice();}@Bean@ConditionalOnMissingBean(NotVoidResponseBodyAdvice.class)public NotVoidResponseBodyAdvice notVoidResponseBodyAdvice() {return new NotVoidResponseBodyAdvice();}@Bean@ConditionalOnMissingBean(VoidResponseBodyAdvice.class)public VoidResponseBodyAdvice voidResponseBodyAdvice() {return new VoidResponseBodyAdvice();}@Bean@ConditionalOnMissingBean(value = {ResponseFactory.class})public ResponseFactory responseBeanFactory() {return new DefaultResponseFactory();}@Bean@ConditionalOnMissingBean(value = {ResponseStatusFactory.class})public ResponseStatusFactory responseStatusFactory() {return new DefaultResponseStatusFactoryImpl();}@Beanpublic ExceptionAliasRegister exceptionAliasRegister() {return new ExceptionAliasRegister();}@Beanpublic Init init(){return new Init();}
}

注解启动全局结果处理入口

通过元注解 @Import(AutoConfig.class) 实际上将 AutoConfig 这个配置类引入到标注了 @EnableGracefulResponse 注解的类中。

引入该组件,只有在某个类上添加了 @EnableGracefulResponse 注解时,AutoConfig 中定义的相关 Bean 才会被注册到 Spring 容器中。可以方便地启用特定功能或配置。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfig.class)
public @interface EnableGracefulResponse {
}

SpringBoot主启动类,添加该注解开启功能:

image-20231214153605858

另一种方式META-INF/spring.factories

如果想引入该组件就直接开启所有功能,可以不用上述全局启动注解

直接在组件项目的 resources 中创建 META-INF 目录,并在此目录下创建一个 spring.factories 文件,将starter组件的自动配置类的类路径写在文件上即可。

resourcesMETA-INFspring.factories
// 直接将自动装配类全限定名放入该文件即可
com.feiniaojin.gracefulresponse.AutoConfig

Spring Boot项目启动时,会扫描外部引入的Jar中的META-INF/spring.factories文件,将文件中配置的类信息装配到Spring容器中)【用的是Java的SPI机制,还没研究后续再说吧】

这样只要引入该组件,组件功能也就集成进来了。【这种不够灵活,视情况用哪种方式】(建议:能用全局启动注解就用全局启动注解)

配置智能提示

组件中记得添加该依赖:

       <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId><version>${spring.boot.version}</version><optional>true</optional></dependency>

组件中添加上述依赖可以用于处理配置类(@ConfigurationProperties 注解标注的类)以及生成相关的元数据。

IDE 支持: 提供更好的集成开发环境(IDE)支持。通过生成的配置元数据,IDE 可以在配置文件中提供智能提示,我们可以更方便地编辑配置。

引入该组件,在修改配置类可以有智能提示

如下:

image-20231214114109561

该文章只介绍了该组件部分功能,有兴趣可以自行研究呀~

最后看一下组件的目录结构(学习一下):

image-20231214153359444

小结

自定义starter组件的三个核心步骤:

  1. 自定义注解
  2. 注解结合AOP实现逻辑
  3. 自动装配和可配置(配置类、自动装配类以及全局启动注解/spring.factories文件

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

天翼云盘秒变硬盘分享

https://cloud.189.cn/web/share?codeAvUnqaj6NNza&#xff08;访问码&#xff1a;wf4r&#xff09;y 以下介绍为作者开发的单机版软件&#xff0c;可用于Windows环境中将天翼云盘挂载为本地硬盘&#xff0c;确实可以达到本地硬盘的使用感知&#xff0c;对于多终端数据副本一…

【动态规划】【广度优先搜索】LeetCode:2617 网格图中最少访问的格子数

本文涉及的基础知识点 二分查找算法合集 动态规划 题目 给你一个下标从 0 开始的 m x n 整数矩阵 grid 。你一开始的位置在 左上角 格子 (0, 0) 。 当你在格子 (i, j) 的时候&#xff0c;你可以移动到以下格子之一&#xff1a; 满足 j < k < grid[i][j] j 的格子 (i,…

持续集成交付CICD:CentOS 7 安装SaltStack

目录 一、理论 1.SaltStack 二、实验 1.主机一安装master 2.主机二安装第一台minion 3.主机三安装第二台minion 4.测试SaltStack 三、问题 1.CentOS 8 如何安装SaltStack 一、理论 1.SaltStack &#xff08;1&#xff09;概念 SaltStack是基于python开发的一套C/S自…

Nginx 服务器安装及配置文件详解

1. 安装nginx 1.1 选择稳定版本 我们编译安装nginx来定制自己的模块&#xff0c;机器CentOS 6.2 x86_64。首先安装缺少的依赖包&#xff1a; # yum -y install gcc gcc-c make libtool zlib zlib-devel openssl openssl-devel pcre pcre-devel 这些软件包如果yum上没有的话…

一篇文章讲透TCP/IP协议

1 OSI 7层参考模型 2 实操连接百度 nc连接百度2次&#xff0c;使用命令netstat -natp查看就会重新连接一次百度 请求百度 3 三次握手、socket 应用层协议控制长连接和短连接 应用层协议->传输控制层&#xff08;TCP UDP&#xff09;->TCP&#xff08; 面向连接&am…

Numpy 实现C4.5决策树

C4.5 信息增益比实现决策树 信息增益比 g R ( D , A ) g ( D , A ) H ( D ) g_{R}(D, A)\frac{g(D, A)}{H(D)} gR​(D,A)H(D)g(D,A)​ 其中&#xff0c; g ( D , A ) g(D,A) g(D,A)是信息增益&#xff0c; H ( D ) H(D) H(D)是数据集 D D D的熵 代码实现 import numpy as …

SQLE 3.0 部署实践

来自 1024 活动的投稿系列 第一篇《SQLE 3.0 部署实践》 . 作者&#xff1a;张昇&#xff0c;河北东软软件有限公司高级软件工程师&#xff0c;腾讯云社区作者。 爱可生开源社区出品&#xff0c;原创内容未经授权不得随意使用&#xff0c;转载请联系小编并注明来源。 本文共 32…

Axure自定义元件

目录 1.processOne的使用 ​编辑2.自定义元件的使用、 2.1如何自定义一个元件 2.2使用自定义元件 导语&#xff1a; Axure是绘制原型图的软件&#xff0c;但是我们很多时候不知道&#xff0c;画哪一个板块&#xff0c;所以流程图的绘制也是非常重要的 1.processOne的使用…

Vue2.x源码:new Vue()做了啥

例子1new Vue做了啥?new Vue做了啥,源码解析 initMixin函数 初始化 – 初始化Vue实例的配置initLifecycle函数 – 初始化生命周期钩子函数initEvents – 初始化事件系统初始化渲染 initRender初始化inject选项 例子1 <div id"app"><div class"home&…

【Jenkins】节点 node、凭据 credentials、任务 job

一、节点 node Jenkins在安装并初始化完成后&#xff0c;会有一个主节点&#xff08;Master Node&#xff09;&#xff0c;默认情况下主节点可以同时运行的任务数是2&#xff0c;可以在节点配置中修改&#xff08;系统管理/节点和云管理&#xff09;。 Jenkins中的节点&#…

实战——Mac M2 安装mat工具

线上环境出现内存飙升的情况&#xff0c;需要工具定位问题发生点就需要用到mat工具了&#xff0c;之前都是在intel芯片环境上安装的&#xff0c;现在换了m2芯片&#xff0c;导致出现了问题&#xff0c;经过一系列调研都解决了&#xff0c;特此记录下&#xff0c;以备后查 开发…

Linux汇编语言编程-汇编语言

术语 Figure 3-13. 8086 Computer (Partial Model) reg 代表寄存器。 它可以是表 3.13 中列出的任何寄存器。 imm 代表立即数【immediate】&#xff08;可以理解为字面量&#xff0c;常量&#xff09;。 术语“立即数【immediate】”用于指代直接由十进制或十六进制表示形式给…

认识缓存,一文读懂Cookie,Session缓存机制。

&#x1f3c6;作者简介&#xff0c;普修罗双战士&#xff0c;一直追求不断学习和成长&#xff0c;在技术的道路上持续探索和实践。 &#x1f3c6;多年互联网行业从业经验&#xff0c;历任核心研发工程师&#xff0c;项目技术负责人。 &#x1f389;欢迎 &#x1f44d;点赞✍评论…

SAP ABAP 面试题交流

1.列举AT事件并说明其作用&#xff0c;AT事件中的工作区有何不同&#xff1f; AT FIRST 循环loop中执行第一条数据 AT LAST 循环loop中执行最后一条数据 AT NEW 循环loop中指定字段&#xff08;包含指定字段&#xff09;记录与上一条记录不一致数据执行 AT END OF 循环loo…

2019年第八届数学建模国际赛小美赛D题安全选举的答案是什么解题全过程文档及程序

2019年第八届数学建模国际赛小美赛 D题 安全选举的答案是什么 原题再现&#xff1a; 随着美国进入一场关键性的选举&#xff0c;在确保投票系统的完整性方面进展甚微。2016年总统大选期间&#xff0c;唐纳德特朗普因被指控受到外国干涉而入主白宫&#xff0c;这一问题再次成为…

深度学习中的高斯分布

1 高斯分布数学表达 1.1 什么是高斯分布 高斯分布(Gaussian Distribution)又称正态分布(Normal Distribution)。高斯分布是一种重要的模型&#xff0c;其广泛应用与连续型随机变量的分布中&#xff0c;在数据分析领域中高斯分布占有重要地位。高斯分布是一个非常常见的连续概…

微信小程序uniapp记住密码

记住密码功能 在请求登录接口成功后&#xff0c;我们需要判断用户是否勾选记住密码&#xff0c;如果是&#xff0c;则将记住密码状态、账号信息存入本地。 下次登录时&#xff0c;获取本地的记住密码状态&#xff0c;如果为true则获取本地存储的账号信息&#xff0c;将信息回填…

ES中根据主键_id查询记录

一、需求 es中_type&#xff1a;_doc&#xff0c;想要根据主键_id查询记录 二、实现 复合查询中使用语句查询http://192.168.1.1/_doc/1

SpringSecurity6从入门到上天系列第八篇:SpringSecurity当中的默认登录页面是如何产生的?

&#x1f609;&#x1f609; 欢迎加入我们的学习交流群呀&#xff01; ✅✅1&#xff1a;这是孙哥suns给大家的福利&#xff01; ✨✨2&#xff1a;我们免费分享Netty、Dubbo、k8s、Mybatis、Spring等等很多应用和源码级别的高质量视频和笔记资料&#xff0c;你想学的我们这里都…

龙迅LT2611UXC 双PORT LVDS转HDMI(2.0)+音频

描述&#xff1a; LT2611UXC是一个高性能的LVDS到HDMI2.0的转换器&#xff0c;用于STB&#xff0c;DVD应用程序。 LVDS输入可配置为单端口或双端口&#xff0c;有1个高速时钟通道&#xff0c;3~4个高速数据通道&#xff0c;最大运行1.2Gbps/通道&#xff0c;可支持高达9.6Gbp…