SpringMVC系列-4 参数解析器

背景:

本文作为SpringMVC系列的第四篇,介绍参数解析器。本文讨论的参数解析表示从HTTP消息中解析出JAVA对象或流对象并传参给Controller接口的过程。
本文内容包括介绍参数解析器工作原理、常见的参数解析器、自定义参数解析器等三部分。其中,原理部分会结合源码进行说明。

1.工作原理

说明:本文重点在于说明参数解析器的工作原理和使用方式,为避免文章过于冗长,会刻意省略对异步请求和文件上传部分的分支逻辑,读者可在理解主线逻辑后自定阅读该部分源码。
源码介绍时,会忽略所有的日志打印以及与主线逻辑无关的try-catch-finnally块

1.1 解析过程

SpringMVC系列-2 HTTP请求调用链 中介绍过:收到http请求后,进入DispatcherServlet的dispatcherServlet方法:

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {//...// 1.preHandleif (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}// 2.call handlermv = ha.handle(processedRequest, response, mappedHandler.getHandler());// 3.postHandlemappedHandler.applyPostHandle(processedRequest, response, mv);//...
}

在执行HandlerInterceptor的preHandle和postHandle之间,会通过ha.handle(processedRequest, response, mappedHandler.getHandler())反射调用Controller接口,跟踪该调用链进入:

public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {//	⚠️1.调用controller接口Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);//	⚠️2.处理返回值setResponseStatus(webRequest);if (returnValue == null) {if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {disableContentCachingIfNecessary(webRequest);mavContainer.setRequestHandled(true);return;}} else if (StringUtils.hasText(getResponseStatusReason())) {mavContainer.setRequestHandled(true);return;}mavContainer.setRequestHandled(false);Assert.state(this.returnValueHandlers != null, "No return value handlers");try {this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);} catch (Exception ex) {if (logger.isTraceEnabled()) {logger.trace(formatErrorForReturnValue(returnValue), ex);}throw ex;}
}

invokeAndHandle方法从逻辑上可以分为两个部分:(1)调用controller接口并获取返回值;(2)处理返回值。
本文关注第一部分:Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
其中:webRequest包装了HTTP请求的request和response对象,mavContainer是MVC对象,providedArgs传入的是null.
跟进invokeForRequest方法:

public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);return doInvoke(args);
}

包括两个步骤:(1)调用getMethodArgumentValues获取参数;(2)将步骤(1)获取的参数传递给doInvoke,通过反射调用Controller接口并返回结果。
跟进getMethodArgumentValues方法:

protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,	Object... providedArgs) throws Exception {MethodParameter[] parameters = getMethodParameters();if (ObjectUtils.isEmpty(parameters)) {return new Object[0];}Object[] args = new Object[parameters.length];for (int i = 0; i < parameters.length; i++) {MethodParameter parameter = parameters[i];parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);args[i] = findProvidedArgument(parameter, providedArgs);if (args[i] != null) {continue;}if (!this.resolvers.supportsParameter(parameter)) {throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));}try {args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);} catch (Exception ex) {throw ex;}}return args;
}

由于入参providedArgs为null, 因此上述逻辑可以简化为:

protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,	Object... providedArgs) throws Exception {MethodParameter[] parameters = getMethodParameters();if (ObjectUtils.isEmpty(parameters)) {return new Object[0];}Object[] args = new Object[parameters.length];for (int i = 0; i < parameters.length; i++) {MethodParameter parameter = parameters[i];parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);if (!this.resolvers.supportsParameter(parameter)) {throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));}try {args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);} catch (Exception ex) {throw ex;}}return args;
}

上述解析参数的逻辑可以分为两步:(1)获取目标接口的参数数组,并判断是否为空(参数为空即不需要处理参数);(2)遍历参数数组,根据参数解析器对每个参数对象依此进行处理,如果没有匹配的参数处理器,则抛出IllegalStateException异常。

这里的参数解析器this.resolvers类型是HandlerMethodArgumentResolverComposite,是一个组合模型,内部维持了一个HandlerMethodArgumentResolver数组:

private final List<HandlerMethodArgumentResolver> argumentResolvers

参数解析最终都会派发给argumentResolvers的各个元素,派发原则是选择第一个满足匹配规则的参数解析器

在后续SpringMVC源码介绍过程中,会发现框架大量使用了组合设计模式;大部分采取匹配+处理的组合手段完成。

因此HandlerMethodArgumentResolver接口需要有两个接口:

public interface HandlerMethodArgumentResolver {boolean supportsParameter(MethodParameter parameter);Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception;
}

supportsParameter方法用于判断该HandlerMethodArgumentResolver是否与参数匹配,resolveArgument方法用于解析参数并返回解析结果。

1.2 初始化过程

ServletInvocableHandlerMethods的resolvers属性
解析过程的核心在于参数解析器,即ServletInvocableHandlerMethod对象中的HandlerMethodArgumentResolverComposite resolvers属性,而每次HTTP调用都会生成一个ServletInvocableHandlerMethod对象,因此关注该对象的resolvers属性如何被初始化即可:

public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter implements BeanFactoryAware, InitializingBean {protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {// ...ServletInvocableHandlerMethod invocableMethod = new ServletInvocableHandlerMethod(handlerMethod);if (this.argumentResolvers != null) {invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);}// ...invocableMethod.invokeAndHandle(webRequest, mavContainer);// ...}
}

如上所示:ServletInvocableHandlerMethod对象的resolvers属性来自RequestMappingHandlerAdapter对象的this.argumentResolvers属性。

RequestMappingHandlerAdapter的argumentResolvers属性
继续跟踪RequestMappingHandlerAdapter的this.argumentResolvers属性初始化过程,需要注意的是RequestMappingHandlerAdapter是全局Bean对象,因此可以从头梳理一下该对象关于argumentResolvers属性的初始化过程。
【1】实例化阶段

@Bean
public RequestMappingHandlerAdapter requestMappingHandlerAdapter(@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,@Qualifier("mvcConversionService") FormattingConversionService conversionService,@Qualifier("mvcValidator") Validator validator) {RequestMappingHandlerAdapter adapter = createRequestMappingHandlerAdapter();adapter.setCustomArgumentResolvers(getArgumentResolvers());// ...return adapter;
}

createRequestMappingHandlerAdapter()返回RequestMappingHandlerAdapter实例后,将getArgumentResolvers()获取的自定义参数解析器设置到this.customArgumentResolvers属性中。
看一下getArgumentResolvers()逻辑:

protected final List<HandlerMethodArgumentResolver> getArgumentResolvers() {this.argumentResolvers = new ArrayList<>();addArgumentResolvers(this.argumentResolvers);return this.argumentResolvers;
}@Override
protected void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {this.configurers.addArgumentResolvers(argumentResolvers);
}

argumentResolvers数据来自于this.configurers,看一下这个属性的初始化过程:

@Configuration(proxyBeanMethods = false)
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();@Autowired(required = false)public void setConfigurers(List<WebMvcConfigurer> configurers) {if (!CollectionUtils.isEmpty(configurers)) {this.configurers.addWebMvcConfigurers(configurers);}}
}

configurers来自于IOC容器中WebMvcConfigurer类型的对象。

因此用户可自定义WebMvcConfigurer对象并将其注入到IOC中,在自定义的WebMvcConfigurer类中通过复写addArgumentResolvers方法可实现自定义参数解析器的添加。

【2】初始化阶段
RequestMappingHandlerAdapter实现了InitializingBean接口,在Bean的初始化阶段中,会调用其afterPropertiesSet()钩子函数:


public void afterPropertiesSet() {// ...if (this.argumentResolvers == null) {List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);}// ...
}

可以看出所有的参数解析器来自有getDefaultArgumentResolvers()方法:

private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>(30);// Annotation-based argument resolutionresolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));resolvers.add(new RequestParamMapMethodArgumentResolver());resolvers.add(new PathVariableMethodArgumentResolver());resolvers.add(new PathVariableMapMethodArgumentResolver());resolvers.add(new MatrixVariableMethodArgumentResolver());resolvers.add(new MatrixVariableMapMethodArgumentResolver());resolvers.add(new ServletModelAttributeMethodProcessor(false));resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(), this.requestResponseBodyAdvice));resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));resolvers.add(new RequestHeaderMapMethodArgumentResolver());resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory()));resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));resolvers.add(new SessionAttributeMethodArgumentResolver());resolvers.add(new RequestAttributeMethodArgumentResolver());// Type-based argument resolutionresolvers.add(new ServletRequestMethodArgumentResolver());resolvers.add(new ServletResponseMethodArgumentResolver());resolvers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));resolvers.add(new RedirectAttributesMethodArgumentResolver());resolvers.add(new ModelMethodProcessor());resolvers.add(new MapMethodProcessor());resolvers.add(new ErrorsMethodArgumentResolver());resolvers.add(new SessionStatusMethodArgumentResolver());resolvers.add(new UriComponentsBuilderMethodArgumentResolver());// Custom argumentsif (getCustomArgumentResolvers() != null) {resolvers.addAll(getCustomArgumentResolvers());}// Catch-allresolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));resolvers.add(new ServletModelAttributeMethodProcessor(true));return resolvers;
}

这里包含了框架内置的参数解析器以及自定义参数解析器,需要注意一下几点:
(1)getCustomArgumentResolvers()来自于【1】实例化阶段中设置的自定义参数解析器;
(2)自定义参数解析器的顺序比较靠后,需要避免被其他参数解析器拦截,supportsParameter方法可以根据参数类型进行匹配。
(3)首位端各存在一个RequestParamMethodArgumentResolver类型的参数解析器,区别是内部useDefaultResolution属性前者是false, 后者是true.

2.常见的参数解析器

2.1 解析器分类

Debug是阅读源码的一个很有效的方式。

Debug
Spring框架默认的消息解析器超过20个, 红框圈选的是常见的参数解析器(本章节重点对着一部分进行介绍),如下所示:在这里插入图片描述
上述26个参数解析器按照匹配条件类型可以分为:
【1】注解类型
根据Controller接口中参数是否拥有指定注解确定使用的参数解析器:
@RequestParam -> RequestParamMethodArgumentResolver, RequestParamMapMethodArgumentResolver
@PathVariable -> PathVariableMethodArgumentResolver, PathVariableMapMethodArgumentResolver
@MatrixVariable -> MatrixVariableMethodArgumentResolver, MatrixVariableMapMethodArgumentResolver
@ModelAttribute -> ModelAttributeMethodProcessor
@RequestBody -> RequestResponseBodyMethodProcessor
@RequestPart -> RequestPartMethodArgumentResolver
@RequestHeader -> RequestHeaderMethodArgumentResolver, RequestHeaderMapMethodArgumentResolver
@CookieValue -> ServletCookieValueMethodArgumentResolver
@Value -> ExpressionValueMethodArgumentResolver
@SessionAttribute -> SessionAttributeMethodArgumentResolver
@RequestAttribute -> RequestAttributeMethodArgumentResolver
上述参数解析器根据是否有对应注解(以及满足特定条件)确定是否匹配参数。

【2】参数类型
根据Controller接口中参数类型确定使用的参数解析器。
ServletResponse及其子类, OutputStream及其子类, Writer及其子类 -> ServletResponseMethodArgumentResolver
HttpEntity, RequestEntity -> HttpEntityMethodProcessor
RedirectAttributes及其子类 -> RedirectAttributesMethodArgumentResolver
Model及其子类 -> ModelMethodProcessor
Errors及其子类 -> ErrorsMethodArgumentResolver
SessionStatus -> SessionStatusMethodArgumentResolver
UriComponentsBuilder, ServletUriComponentsBuilder -> UriComponentsBuilderMethodArgumentResolver

部分参数解析器的supportsParameter方法条件比较复杂,如下所示:
ServletRequestMethodArgumentResolver:
支持的类型较多,如下所示:

@Override
public boolean supportsParameter(MethodParameter parameter) {Class<?> paramType = parameter.getParameterType();return (WebRequest.class.isAssignableFrom(paramType) ||ServletRequest.class.isAssignableFrom(paramType) ||MultipartRequest.class.isAssignableFrom(paramType) ||HttpSession.class.isAssignableFrom(paramType) ||(pushBuilder != null && pushBuilder.isAssignableFrom(paramType)) ||Principal.class.isAssignableFrom(paramType) ||InputStream.class.isAssignableFrom(paramType) ||Reader.class.isAssignableFrom(paramType) ||HttpMethod.class == paramType ||Locale.class == paramType ||TimeZone.class == paramType ||ZoneId.class == paramType);
}

MapMethodProcessor:

public boolean supportsParameter(MethodParameter parameter) {return Map.class.isAssignableFrom(parameter.getParameterType()) &&parameter.getParameterAnnotations().length == 0;
}

参数不能有注解,且参数为Map类型.

ServletModelAttributeMethodProcessor:

@Override
public boolean supportsParameter(MethodParameter parameter) {return (parameter.hasParameterAnnotation(ModelAttribute.class) ||(this.annotationNotRequired && !BeanUtils.isSimpleProperty(parameter.getParameterType())));}

该解析器在实例化时设置的annotationNotRequired为true,因此可以简化为:

@Override
public boolean supportsParameter(MethodParameter parameter) {return parameter.hasParameterAnnotation(ModelAttribute.class) ||!BeanUtils.isSimpleProperty(parameter.getParameterType());}

表示:参数有@ModelAttribute注解或者BeanUtils.isSimpleProperty(parameter.getParameterType())返回false;

public static boolean isSimpleProperty(Class<?> type) {// 如果类型是数据调用isSimpleValueType判读数组的元素return isSimpleValueType(type) || type.isArray() && isSimpleValueType(type.getComponentType());
}public static boolean isSimpleValueType(Class<?> type) {return Void.class != type && Void.TYPE != type && 	(ClassUtils.isPrimitiveOrWrapper(type) || Enum.class.isAssignableFrom(type) || CharSequence.class.isAssignableFrom(type) || Number.class.isAssignableFrom(type) || Date.class.isAssignableFrom(type) || Temporal.class.isAssignableFrom(type) || URI.class == type || URL.class == type || Locale.class == type || Class.class == type);
}

即:参数类型(或参数数组的元素类型)是Void或Void.TYPE或者均不是(Enum,Number,CharSequence,Locale,…)类型的才会匹配。

2.2 常用注解及消息解析器

略(待补充)

3.使用方式

案例从请求url中解析出name和age、从HTTP请求头中解析出token、并获取当前服务器时间,用于构造User对象,并传参给Controller接口。
定义参数类:

@Data
public class User {private String name;private Integer age;private String token;private LocalDateTime time;
}

定义Controller接口:

@GetMapping("/test")
public Object queryByType(User user) {return user;
}

自定义参数解析器:

public class MyArgumentResolver implements HandlerMethodArgumentResolver {@Overridepublic boolean supportsParameter(MethodParameter parameter) {return parameter.getParameterType().equals(User.class);}@Overridepublic Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {User user = new User();HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest();user.setName(request.getParameter("name"));user.setAge(Integer.parseInt(request.getParameter("age")));user.setToken(request.getHeader("token"));user.setTime(LocalDateTime.now());return user;}
}

将参数解析器注册到容器中:

@Configuration
public class BaseWebMvcConfigurer implements WebMvcConfigurer {@Overridepublic void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {argumentResolvers.add(new MyArgumentResolver());}
}

使用postman调用测试结果如下:
在这里插入图片描述

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

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

相关文章

【前段基础入门之】=>CSS3的新增特性!

文章目录 CSS3概述CSS3私有前缀常见浏览器私有前缀 新增盒模型相关属性怪异盒模型拖拽调整盒子大小盒子阴影不透明度 新增背景属性background-originbackground-clipbackground-size多背景图backgorund 复合属性 新增边框属性边框圆角边框外轮廓 新增文本属性文本阴影文本换行文…

接着说说mysqlbinlog解析工具如何做数据恢复

mysqlbinlog解析工具如何做数据恢复 mysqldump逻辑备份全量备份增量备份增量恢复 看腻了就来听听视频演示吧&#xff1a;https://www.bilibili.com/video/BV1Qh4y1Y7Fm/ mysqldump逻辑备份 优点&#xff1a;可备份恢复部分数据缺点&#xff1a;速度慢 mysqlbinlog追binlog日…

NPM 常用命令(九)

目录 1、npm link 1.1 使用语法 1.2 描述 2、npm login 2.1 描述 3、npm logout 3.1 描述 4、npm ls 4.1 使用语法 4.2 描述 5、npm org 5.1 使用语法 5.2 示例&#xff1a; 6、npm outdated 6.1 使用语法 6.2 描述 6.3 示例 7、npm owner 7.1 使用语法 7.2…

jupyter notebook的安装和使用

Jupyter Notebook是基于网页的用于交互计算的应用程序。其可被应用于全过程计算&#xff1a;开发、文档编写、运行代码和展示结果。 它可以一步步的运行出结果&#xff0c;而且可以实时的坐笔记。 一般安装了anaconda会自带jupyter notebook&#xff0c;所以你可以先启动试试能…

【C语言 模拟实现strcat函数】

C语言程序设计笔记---024 C语言之模拟实现strcat函数1、介绍strcat函数2、模拟实现strcat函数3、结语 C语言之模拟实现strcat函数 前言&#xff1a; 通过C语言字符串函数的知识&#xff0c;这篇将对strcat函数进行深入学习底层原理的知识&#xff0c;并模拟实现对应功能。 /知…

ipv6跟ipv4如何通讯

IPv6的128位地址通常写成8组&#xff0c;每组为四个十六进制数的形式。比如:AD80:0000:0000:0000:ABAA:0000:00C2:0002 是一个合法的IPv6地址。这个地址比较长&#xff0c;看起来不方便也不易于书写。零压缩法可以用来缩减其长度。如果几个连续段位的值都是0&#xff0c;那么这…

免费 AI 代码生成器 Amazon CodeWhisperer 初体验

文章作者&#xff1a;浪里行舟 简介 随着 ChatGPT 的到来&#xff0c;不由让很多程序员感到恐慌。虽然我们阻止不了 AI 时代到来&#xff0c;但是我们可以跟随 AI 的脚步&#xff0c;近期我发现了一个神仙 AI 代码生产工具 CodeWhisperer &#xff0c;它是一项基于机器学习的服…

众佰诚:抖音开网店新手怎么做才能做起来

抖音作为国内最热门的短视频平台&#xff0c;其商业价值也日渐凸显。许多商家和个体经营者开始在抖音上开设网店&#xff0c;以此为新的销售渠道。那么&#xff0c;对于新手来说&#xff0c;如何才能在抖音上成功运营网店呢? 首先&#xff0c;明确经营定位。每个电商平台都有其…

小谈设计模式(16)—抽象工厂模式

小谈设计模式&#xff08;16&#xff09;—抽象工厂模式 专栏介绍专栏地址专栏介绍 抽象工厂模式结构抽象工厂&#xff08;AbstractFactory&#xff09;具体工厂&#xff08;ConcreteFactory&#xff09;抽象产品&#xff08;AbstractProduct&#xff09;具体产品&#xff08;C…

FPGA project : fifo_sum

实验目标&#xff1a; col(列) 4 &#xff1b;line(行) 5。相邻三行&#xff0c;按列求和。输出新的数据流。 实现方法&#xff1a; 通过rs232通信协议&#xff0c;输入数据流。第一行存进fifo1&#xff0c;第二行存进fifo2.当输入第三行第一个数据的时候&#xff0c;从fif…

Verilog HDL阻塞赋值和非阻塞赋值笔记

1. module test( input wire clk, input wire b, output reg a, output reg c ); always(posedge clk) begin ab; ca; end endmodule 上面的代码在vivado中综合后的电路为&#xff1a; 2. module test( input wire clk, input wire b, outp…

offer突击训练营,给你一个offer的保障,求职跳槽的看过来!

大家好&#xff0c;我是枫哥&#xff0c;&#x1f31f;阿里云技术专家、&#x1f4dd;资深面试官、&#x1f339;Java跳蚤网课堂创始人。拥有多年一线研发经验&#xff0c;曾就职过科大讯飞、美团网、平安等公司。 目前组建的团队&#xff0c;专注Java技术分享&#xff0c…

一些比较好的行为识别的项目链接

文章目录 1、TSM官方GitHub链接&#xff1a; 链接 2、Temporal Segment Networks (TSN) &#xff0c;这个先备忘一下&#xff0c;GitHub链接&#xff1a;链接 3、openpose python部署 GitHub链接&#xff1a;链接 4、openposeLSTM 打架识别 C部署&#xff1a;GitHub链接

CentOS7.9中使用packstack安装train版本

这里写目录标题 材料准备为什么选择packstack安装静态ip系统配置使用阿里云yum源安装packstack部署openstack 材料准备 ecs云服务器8核心16g内存一台&#xff0c;系统盘100GB&#xff0c;系统CentOS7.9vpc网段&#xff1a;192.168.0.1/24eip一个&#xff0c;带宽5M以上 为什么…

【Java每日一题】— —第二十三题:匿名对象及其使用问题(2023.10.07)

&#x1f578;️Hollow&#xff0c;各位小伙伴&#xff0c;今天我们要做的是第二十三题。 &#x1f3af;问题&#xff1a; 匿名对象的应用场景如下&#xff1a; A:调用方法&#xff0c;仅仅只调用一次的时候。 注意&#xff1a;调用多次的时候&#xff0c;不适合。 那么&#x…

互联网Java工程师面试题·Memcached篇·第一弹

目录 1、Memcached 是什么&#xff0c;有什么作用&#xff1f; 1.1 memcached 服务在企业集群架构中有哪些应用场景&#xff1f; 1.1.1 作为数据库的前端缓存应用 1.1.2 作业集群的 session 会话共享存储 2、Memcached 服务分布式集群如何实现&#xff1f; 3、Memcach…

持续集成部署-k8s-资源调度:Deployment

持续集成部署-k8s-资源调度:Deployment 1. 创建与配置文件解析2. 滚动更新3. 版本回滚4. 扩容与缩容5. 暂停与恢复在 Kubernetes 中, Deployment和 StatefulSet是两种不同的控制器对象,用于管理Pod和容器的创建、删除和更新。它们之间的区别在于资源调度、稳定性和数据持久…

【Blender实景合成】会跳舞的神里绫华

效果预览 本文将介绍Blender用于实景合成的工作流程。 先看效果&#xff1a; 神里绫华爬上了我的办公桌 模型和动作资源准备 角色模型 本次主要使用的是原神游戏中&#xff0c;神里绫华的角色模型&#xff0c;该模型米哈游在模之屋网站上进行开源。 下载地址&#xff1a;ht…

1500*B. Zero Array(贪心数学找规律)

Problem - 1201B - Codeforces 解析&#xff1a; 因为每次减少2&#xff0c;如果总和为奇数肯定无法实现。 特例&#xff0c;如果某个数大于其他所有数的总和&#xff0c;同样无法实现。 其他均可实现。 #include<bits/stdc.h> using namespace std; #define int long l…

六、vpp 流表+负载均衡

草稿&#xff01;&#xff01;&#xff01; vpp node其实就是三个部分 1、plugin init 2、set command 3、function 实现功能&#xff0c;比如这里的流表 今天我们再用VPP实现一个流表的功能 一、流表 1.1流表----plugin init VLIB_REGISTER_NODE 注册流表节点 // 注册流…