从开源项目中学习如何自定义 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.mzph.cn/news/222293.shtml

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

相关文章

螺旋矩阵算法(leetcode第59题)

题目描述&#xff1a; 给你一个正整数 n &#xff0c;生成一个包含 1 到 n2 所有元素&#xff0c;且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。示例 1&#xff1a;输入&#xff1a;n 3 输出&#xff1a;[[1,2,3],[8,9,4],[7,6,5]] 示例 2&#xff1a;输入&#…

电商API代码如何接入写

要接入一个API&#xff0c;通常需要遵循以下步骤&#xff1a; 1. 【了解API文档】&#xff1a;首先&#xff0c;需要了解你想要接入的API的文档。这些文档通常会包含关于如何使用该API的重要信息&#xff0c;比如基本的请求格式、可用的端点&#xff08;endpoints&#xff09;…

Process On在线绘制流程图

目录 一.ProcessOn 1.1.介绍 1.2.直接网上使用 二.绘制门诊流程图 三.绘制住院流程图 四.绘制药库采购入库流程图 五.绘制OA会议流程图 今天就到这里了哦!!!希望能帮到你哦&#xff01;&#xff01;&#xff01; 一.ProcessOn 1.1.介绍 ProcessOn&#xff08;流程&#…

Linux,Web网站服务(一)

1.准备工作 为了避免发生端口冲突&#xff0c;程序冲突等现象&#xff0c;建议卸载使用RPM方式安装的httpd [rootnode01 ~]# rpm -e http --nodeps 挂载光盘到/mnt目录 [rootnode01 ~]# mount /dev/cdrom /mnt Apache的配置及运行需要apr.pcre等软件包的支持&#xff0c;因此…

牛客网SQL训练2—SQL基础进阶

文章目录 一、基本查询二、数据过滤三&#xff1a;函数四&#xff1a;分组聚合五&#xff1a;子查询六&#xff1a;多表连接七&#xff1a;组合查询八&#xff1a;技能专项-case when使用九&#xff1a;多表连接-窗口函数十&#xff1a;技能专项-having子句十一&#xff1a;技能…

Web信息收集,互联网上的裸奔者

Web信息收集&#xff0c;互联网上的裸奔者 1.资产信息收集2.域名信息收集3.子域名收集4.单点初步信息收集网站指纹识别服务器类型(Linux/Windows)网站容器(Apache/Nginx/Tomcat/IIS)脚本类型(PHP/JSP/ASP/ASPX)数据库类型(MySQL/Oracle/Accees/SqlServer) 5.单点深入信息收集截…

Linux-----10、查找命令

# 查找命令 # 1、 命令查找 Linux下一切皆文件&#xff01; which 命令 &#xff1a;找出命令的绝对路径 whereis 命令 &#xff1a;找出命令的路径以及文档手册信息 [rootheima ~]# which mkdir /usr/bin/mkdir[rootheima ~]# whereis mkdir mkdir: /usr/bin/mkdir /usr/…

一款计算机顶会爬取解析系统 paper info

一款计算机顶会爬取解析系统 paper info 背景项目实现的功能 技术方案架构设计项目使用的技术选型 使用方法本地项目部署使用ChatGPT等大模型创建一个ChatGPT助手使用阿里云 顶会数据量 百度网盘pfd文件json文件 Q&A github链接 &#xff1a;https://github.com/codebricki…

(反序列化)小记录

目录 [CISCN 2023 华北]ez_date 绕过MD5和sha1强相关绕过 date()绕过 payload [FSCTF 2023]ez_php [CISCN 2023 华北]ez_date <?php error_reporting(0); highlight_file(__FILE__); class date{public $a;public $b;public $file;public function __wakeup(){if(is_a…

【快刊】Springer旗下2区,仅1个月左右录用,国人极其友好!

计算机类 • 好刊解读 今天小编带来Springer旗下计算机领域好刊&#xff0c;如您有投稿需求&#xff0c;可作为重点关注&#xff01;后文有相关领域真实发表案例&#xff0c;供您投稿参考~ 01 期刊简介 ✅出版社&#xff1a;Springer ✅影响因子&#xff1a;4.0-5.0 ✅期刊…

docker小白第三天

docker小白第三天 docker为什么会比虚拟机快 1、docker有着比虚拟机更少的抽象层。不需要Hypervisor实现硬件资源虚拟化&#xff0c;运行在docker容器上的程序直接使用的都是实际物理机的硬件资源&#xff0c;因此在CPU、内存利用率上docker将会在效率上有明显优势。 2、dock…

Vray批量云渲染怎么设置?Vray批量云渲染教程

V-Ray&#xff0c;作为业界知名的渲染引擎&#xff0c;其内置的Batch Render&#xff08;批处理渲染&#xff09;功能让艺术家和设计者们能够高效率地处理多场景或多视角的渲染任务。通过这一功能&#xff0c;用户可以设置一系列渲染队列&#xff0c;无需监督即可自动完成整个渲…

【期末向】“我也曾霸榜各类NLP任务”-bert详解

预训练语言模型 预训练语言模型于 2015 年被首次提出&#xff08;Dai & Le,2015&#xff09;。首先我们要了解一下什么是预训练模型&#xff0c;举个例子&#xff0c;假设我们有大量的维基百科数据&#xff0c;那么我们可以用这部分巨大的数据来训练一个泛化能力很强的模型…

C语言编程经典100例——21至30例

目录 第 21 例 第 22 例 第 23 例 第 24 例 第 25 例 第 26 例 第 27 例 第 28 例 第 29 例 第 30 例 第 21 例 程序源码&#xff1a; /* 题目&#xff1a;猴子吃桃问题&#xff1a;猴子第一天摘下若干个桃子&#xff0c;当即吃了一半&#xff0c;还不过瘾&#xf…

渗透测试框架——Cobalt Strike

渗透测试框架——Cobalt Strike Cobalt Strike是一款非常成熟的渗透测试框架。Cobalt Strike在3.0版本之前是基于Metasploit框架工作的&#xff0c;可以使用Metasploit的漏洞库。从3.0版本开始&#xff0c;Cobalt Strike不再使用Metasploit的漏洞库&#xff0c;成为一个独立的…

JavaEE:多线程(1):线程是啥?怎么创建和操作?

进程的问题 本质上来说&#xff0c;进程可以解决并发编程的问题 但是有些情况下进程表现不尽如人意 1. 如果请求很多&#xff0c;需要频繁的创建和销毁进程的时候&#xff0c;此时使用多进程编程&#xff0c;系统开销就会很大 2. 一个进程刚刚启动的时候&#xff0c;需要把…

自动化测试Selenium node 配置

查看自己chrome浏览器的版本 下载chromedriver对应版本&#xff0c;下载当前版本中最大版本。 https://npm.taobao.org/mirrors/chromedriver 安装java jdk &#xff0c;版本至少1.7, 并配置jdk环境变量 以下2个文件放在同一个目录下 Cmd地址切换到第四点目录下&#xff0c;然…

Android开发的技术与开发流程

目录 引言 1. Android开发环境搭建 1. 安装Java Development Kit&#xff08;JDK&#xff09; 2. 安装Android Studio 3. 配置虚拟设备&#xff08;可选&#xff09; 4. 创建你的第一个Android项目 5. 连接实体设备&#xff08;可选&#xff09; 2. Android基础知识 1…

影响云渲染质量的几大要素是什么?影响云渲染质量的主要原因有?

对于3D渲染从业者而言&#xff0c;实现高效和高质量的渲染是一个常见的挑战。由于三维场景的复杂性&#xff0c;相关计算和处理通常需要大量的计算能力和存储&#xff0c;尤其是当面对着高分辨率图像、详细的动画或全局光照效果等要求时&#xff0c;渲染时间往往会大幅增加。针…

了解构造函数原型对象的语法特征,掌握 JavaScript 中面向对象编程的实现方式,基于面向对象编程思想实现 DOM 操作的封装。(第三天)

有什么不懂可以去看我前两天的笔记 https://blog.csdn.net/weixin_70007095/article/details/134905674 目录 有什么不懂可以去看我前两天的笔记 JavaScript 进阶 - 第3天笔记 编程思想 面向过程 面向对象 构造函数 原型对象 constructor 属性 对象原型 原型继承 原型链 JavaSc…