mvc 异常处理源码解析(3)

目录

  • 准备
  • 源码跟踪
  • ExceptionHandlerExceptionResolver初始化
    • ExceptionHandlerExceptionResolver注入
    • ExceptionHandlerExceptionResolver中exceptionHandlerAdviceCache初始化
    • ExceptionHandlerMethodResolver中mappedMethods初始化
  • 结尾

准备

  1. 准备一个controller类, 里面抛出一个异常
@RestController
@RequestMapping("/hello")
public class HelloController {@GetMapping("/h")public String hello(HttpServletResponse response) throws ArithmeticException {try {int a = 1 / 0;} catch (Exception e) {throw e;}return "hello";}}
  1. 配置一个全局异常处理类
/*** 异常处理器*/
@RestControllerAdvice
public class BDExceptionHandler {/*** 参数异常* @param e 异常* @return BaseResult*/@ExceptionHandler(ArithmeticException.class)public R doBaseExceptionHandler(ArithmeticException e) {return R.error(e.getMessage());}
}
  1. 接下来访问url, 跟踪源码

源码跟踪

进入到mvc的入口类DispatcherServlet的 doDispatch方法, 可以发现整体逻辑被try catch包围, 当第3步抛出异常之后就会被捕获, dispatchException对象则不为空,最后进入processDispatchResult方法进行处理

	protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {HttpServletRequest processedRequest = request;HandlerExecutionChain mappedHandler = null;boolean multipartRequestParsed = false;WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);try {ModelAndView mv = null;Exception dispatchException = null;try {//1.获取处理器链路//2.执行拦截器前置方法//3.调用controller方法, 返回结果集//4.执行拦截器后置方法}catch (Exception ex) {//dispatchException = ex;}catch (Throwable err) {dispatchException = new NestedServletException("Handler dispatch failed", err);}processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);}catch (Exception ex) {triggerAfterCompletion(processedRequest, response, mappedHandler, ex);}catch (Throwable err) {triggerAfterCompletion(processedRequest, response, mappedHandler,new NestedServletException("Handler processing failed", err));}finally {if (asyncManager.isConcurrentHandlingStarted()) {// Instead of postHandle and afterCompletionif (mappedHandler != null) {mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);}}else {// Clean up any resources used by a multipart request.if (multipartRequestParsed) {cleanupMultipart(processedRequest);}}}}

进入processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);

	private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,@Nullable Exception exception) throws Exception {boolean errorView = false;//判断异常是否为空, 如果不是空则处理if (exception != null) {//处理ModelAndViewDefiningException异常if (exception instanceof ModelAndViewDefiningException) {logger.debug("ModelAndViewDefiningException encountered", exception);mv = ((ModelAndViewDefiningException) exception).getModelAndView();}else {//处理其他所有的异常Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);mv = processHandlerException(request, response, handler, exception);errorView = (mv != null);}}//省略下面代码

进入mv = processHandlerException(request, response, handler, exception);

	protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,@Nullable Object handler, Exception ex) throws Exception {// Success and error responses may use different content typesrequest.removeAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);// Check registered HandlerExceptionResolvers...ModelAndView exMv = null;if (this.handlerExceptionResolvers != null) {//this.handlerExceptionResolvers里面默认有两个处理异常的类for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) {// 遍历,判断使用那个类来处理exMv = resolver.resolveException(request, response, handler, ex);if (exMv != null) {break;}}}if (exMv != null) {//省略代码return exMv;}throw ex;}

看一下上面for循环里面的this.handlerExceptionResolvers对象的结构 :
DefaultErrorAttributes : 没做任何处理, 只是将异常设置到request对象的属性当中;

HandlerExceptionResolverComposite : 里面有三个异常处理类, 分别的作用是 :

ExceptionHandlerExceptionResolver:基于 @ExceptionHandler 配置 HandlerMethod 的 HandlerExceptionResolver 实现类。例如通过 @ControllerAdvice 注解自定义异常处理器,加上@ExceptionHandler注解指定方法所需要处理的异常类型

ResponseStatusExceptionResolver:基于 @ResponseStatus 提供错误响应的 HandlerExceptionResolver 实现类。例如在方法上面添加 @ResponseStatus 注解,指定该方法发生异常时,需要设置的 code 响应码和 reason 错误信息

DefaultHandlerExceptionResolver:默认 HandlerExceptionResolver 实现类,针对各种异常,设置错误响应码。例如 HTTP Method 不支持,则在这个实现类中往响应中设置错误码和错误信息

原文链接:https://blog.csdn.net/Running666/article/details/130786561

在这里插入图片描述
进入HandlerExceptionResolverComposite类的resolveException(); 遍历上面的三种异常处理类, 判断应该使用哪个处理

	public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {if (this.resolvers != null) {//获取, 遍历for (HandlerExceptionResolver handlerExceptionResolver : this.resolvers) {ModelAndView mav = handlerExceptionResolver.resolveException(request, response, handler, ex);if (mav != null) {return mav;}}}return null;}

由于只设置了第一种异常处理方式, 所以异常会由ExceptionHandlerExceptionResolver来处理, 进入handlerExceptionResolver.resolveException()

	public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {if (shouldApplyTo(request, handler)) {prepareResponse(ex, response);//主要的异常处理逻辑ModelAndView result = doResolveException(request, response, handler, ex);if (result != null) {// Print debug message when warn logger is not enabled.if (logger.isDebugEnabled() && (this.warnLogger == null || !this.warnLogger.isWarnEnabled())) {logger.debug("Resolved [" + ex + "]" + (result.isEmpty() ? "" : " to " + result));}logException(ex, request);}return result;}else {return null;}}

进入doResolveException(request, response, handler, ex)方法;

	@Override@Nullableprotected ModelAndView doResolveHandlerMethodException(HttpServletRequest request,HttpServletResponse response, @Nullable HandlerMethod handlerMethod, Exception exception) {//根据controller类和异常来判断由哪个类来处理这个异常ServletInvocableHandlerMethod exceptionHandlerMethod = getExceptionHandlerMethod(handlerMethod, exception);if (exceptionHandlerMethod == null) {return null;}//填充参数处理器if (this.argumentResolvers != null) {exceptionHandlerMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);}//填充返回结果处理器if (this.returnValueHandlers != null) {exceptionHandlerMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);}ServletWebRequest webRequest = new ServletWebRequest(request, response);ModelAndViewContainer mavContainer = new ModelAndViewContainer();ArrayList<Throwable> exceptions = new ArrayList<>();try {//省略代码Object[] arguments = new Object[exceptions.size() + 1];exceptions.toArray(arguments);  // efficient arraycopy call in ArrayListarguments[arguments.length - 1] = handlerMethod;//反射调用异常处理类exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, arguments);}catch (Throwable invocationEx) {// Any other than the original exception (or a cause) is unintended here,// probably an accident (e.g. failed assertion or the like).if (!exceptions.contains(invocationEx) && logger.isWarnEnabled()) {logger.warn("Failure in @ExceptionHandler " + exceptionHandlerMethod, invocationEx);}// Continue with default processing of the original exception...return null;}//省略}

主要逻辑就是看如何找到处理对应异常的方法, 进入getExceptionHandlerMethod(handlerMethod, exception);

进入到了ExceptionHandlerExceptionResolver类中, 在这个类里面有两个关键的属性,exceptionHandlerCache和exceptionHandlerAdviceCache, 两个都是map, exceptionHandlerAdviceCache的初始化下面再说,exceptionHandlerCache的初始化想知道的自己去查一下吧

	private final Map<Class<?>, ExceptionHandlerMethodResolver> exceptionHandlerCache =new ConcurrentHashMap<>(64);private final Map<ControllerAdviceBean, ExceptionHandlerMethodResolver> exceptionHandlerAdviceCache =new LinkedHashMap<>();

exceptionHandlerCache里面存的就是在当前controller里面包含@ExceptionHandler注解的方法, key: controller类, value : 方法包装类

exceptionHandlerAdviceCache里面存的就是标注了@ControllerAdvice注解的类,里面带有@ExceptionHandler注解的方法, key:
@RestControllerAdvice注解标注的类, value: 方法包装类

	@Nullableprotected ServletInvocableHandlerMethod getExceptionHandlerMethod(@Nullable HandlerMethod handlerMethod, Exception exception) {Class<?> handlerType = null;//从exceptionHandlerCache获取异常对应的处理方法, 由于没有配置, 肯定获取不到if (handlerMethod != null) {handlerType = handlerMethod.getBeanType();ExceptionHandlerMethodResolver resolver = this.exceptionHandlerCache.get(handlerType);if (resolver == null) {resolver = new ExceptionHandlerMethodResolver(handlerType);this.exceptionHandlerCache.put(handlerType, resolver);}Method method = resolver.resolveMethod(exception);if (method != null) {return new ServletInvocableHandlerMethod(handlerMethod.getBean(), method);}if (Proxy.isProxyClass(handlerType)) {handlerType = AopUtils.getTargetClass(handlerMethod.getBean());}}//遍历全局的异常处理类, 看是否可以获取到对应的处理方法for (Map.Entry<ControllerAdviceBean, ExceptionHandlerMethodResolver> entry : this.exceptionHandlerAdviceCache.entrySet()) {ControllerAdviceBean advice = entry.getKey();if (advice.isApplicableToBeanType(handlerType)) {ExceptionHandlerMethodResolver resolver = entry.getValue();Method method = resolver.resolveMethod(exception);if (method != null) {return new ServletInvocableHandlerMethod(advice.resolveBean(), method);}}}return null;}

进入Method method = resolver.resolveMethod(exception);, 最终进入到resolveMethodByExceptionType()方法, 在ExceptionHandlerMethodResolver类中

ExceptionHandlerMethodResolver中有两个关键属性mappedMethods,和exceptionLookupCache, 两个也都是map, 两个都是根据异常获取对应的异常处理方法, mappedMethods在程序启动时候初始化, exceptionLookupCache相当于一个缓存,为了提高查询速度

	@Nullablepublic Method resolveMethodByExceptionType(Class<? extends Throwable> exceptionType) {//从缓存中获取处理方法, 如果获取不到,返回null(第一次执行此方法肯定获取不到)Method method = this.exceptionLookupCache.get(exceptionType);if (method == null) {//如果获取不到, 则去mappedMethods获取method = getMappedMethod(exceptionType);//将获取的结果缓存到exceptionLookupCache中this.exceptionLookupCache.put(exceptionType, method);}return (method != NO_MATCHING_EXCEPTION_HANDLER_METHOD ? method : null);}

进入getMappedMethod(exceptionType);

	private Method getMappedMethod(Class<? extends Throwable> exceptionType) {List<Class<? extends Throwable>> matches = new ArrayList<>();//遍历所有可以处理的异常, 判断与抛出的异常是否匹配for (Class<? extends Throwable> mappedException : this.mappedMethods.keySet()) {if (mappedException.isAssignableFrom(exceptionType)) {matches.add(mappedException);}}if (!matches.isEmpty()) {//如果有多个方法可以匹配来处理异常,则进行排序if (matches.size() > 1) {matches.sort(new ExceptionDepthComparator(exceptionType));}//获取第一个方法来处理异常,返回方法return this.mappedMethods.get(matches.get(0));}else {return NO_MATCHING_EXCEPTION_HANDLER_METHOD;}}

到此为止, 已经获取到对应的处理异常的方法了, 在上面的doResolveHandlerMethodException方法中, 最后通过反射调用该方法, 处理异常

接下来讨论一下ExceptionHandlerExceptionResolver类里面的exceptionHandlerCache和exceptionHandlerAdviceCache初始化和
ExceptionHandlerMethodResolver类中的mappedMethods初始化

ExceptionHandlerExceptionResolver初始化

ExceptionHandlerExceptionResolver实现了InitializingBean接口, 猜测初始化肯定在afterPropertiesSet()方法中, 我们至于要查找ExceptionHandlerExceptionResolver是如何注入到springboot容器中的, 及afterPropertiesSet()方法逻辑即可
在这里插入图片描述

ExceptionHandlerExceptionResolver注入

实际上,ExceptionHandlerExceptionResolver并不会成为bean交给Spring容器管理。但是在WebMvcConfigurationSupport初始化过程中,会手动调用afterPropertiesSet()进行默认初始化

还记得上面HandlerExceptionResolverComposite类里面有三个异常处理类吗? 看一下他们是如何注入的

在WebMvcConfigurationSupport类里面的handlerExceptionResolver()方法:

	@Beanpublic HandlerExceptionResolver handlerExceptionResolver(@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager) {List<HandlerExceptionResolver> exceptionResolvers = new ArrayList<>();configureHandlerExceptionResolvers(exceptionResolvers);//如果是空, 在下面的if里面将三个异常处理类添加到集合中if (exceptionResolvers.isEmpty()) {addDefaultHandlerExceptionResolvers(exceptionResolvers, contentNegotiationManager);}extendHandlerExceptionResolvers(exceptionResolvers);HandlerExceptionResolverComposite composite = new HandlerExceptionResolverComposite();composite.setOrder(0);//赋值属性, 最后返回composite.setExceptionResolvers(exceptionResolvers);return composite;}

进入到addDefaultHandlerExceptionResolvers(exceptionResolvers, contentNegotiationManager);

	protected final void addDefaultHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers,ContentNegotiationManager mvcContentNegotiationManager) {//创建ExceptionHandlerExceptionResolver 对象ExceptionHandlerExceptionResolver exceptionHandlerResolver = createExceptionHandlerExceptionResolver();exceptionHandlerResolver.setContentNegotiationManager(mvcContentNegotiationManager);exceptionHandlerResolver.setMessageConverters(getMessageConverters());exceptionHandlerResolver.setCustomArgumentResolvers(getArgumentResolvers());exceptionHandlerResolver.setCustomReturnValueHandlers(getReturnValueHandlers());if (jackson2Present) {exceptionHandlerResolver.setResponseBodyAdvice(Collections.singletonList(new JsonViewResponseBodyAdvice()));}if (this.applicationContext != null) {exceptionHandlerResolver.setApplicationContext(this.applicationContext);}//手动调用afterPropertiesSet()给属性初始化exceptionHandlerResolver.afterPropertiesSet();//添加第一个exceptionResolvers.add(exceptionHandlerResolver);ResponseStatusExceptionResolver responseStatusResolver = new ResponseStatusExceptionResolver();responseStatusResolver.setMessageSource(this.applicationContext);//添加第二个exceptionResolvers.add(responseStatusResolver);//添加第三个exceptionResolvers.add(new DefaultHandlerExceptionResolver());}

所以我们只需要看WebMvcConfigurationSupport是如何注入的就可以了, 实际上他是通过WebMvcAutoConfiguration类来间接注入的
在这里插入图片描述
而WebMvcAutoConfiguration类的注入是通过自动装配来注册的
在这里插入图片描述

ExceptionHandlerExceptionResolver中exceptionHandlerAdviceCache初始化

	@Overridepublic void afterPropertiesSet() {// Do this first, it may add ResponseBodyAdvice beansinitExceptionHandlerAdviceCache();if (this.argumentResolvers == null) {List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);}if (this.returnValueHandlers == null) {List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);}}

进入initExceptionHandlerAdviceCache()方法

	private void initExceptionHandlerAdviceCache() {if (getApplicationContext() == null) {return;}//获取带有ControllerAdvice注解的类List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());for (ControllerAdviceBean adviceBean : adviceBeans) {Class<?> beanType = adviceBean.getBeanType();if (beanType == null) {throw new IllegalStateException("Unresolvable type for ControllerAdviceBean: " + adviceBean);}//构建value对象, 存入到exceptionHandlerAdviceCache中//ExceptionHandlerMethodResolver中mappedMethods初始化逻辑也在其中ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(beanType);if (resolver.hasExceptionMappings()) {this.exceptionHandlerAdviceCache.put(adviceBean, resolver);}if (ResponseBodyAdvice.class.isAssignableFrom(beanType)) {this.responseBodyAdvice.add(adviceBean);}}}

ExceptionHandlerMethodResolver中mappedMethods初始化

mappedMethods的初始化逻辑很简单, 在上面的initExceptionHandlerAdviceCache()方法中我们已经找到了带有ControllerAdvice的所有类, 将类的类型作为参数创建了ExceptionHandlerMethodResolver对象, mappedMethods的初始化就在构造方法中

进入ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(beanType) 方法

	public ExceptionHandlerMethodResolver(Class<?> handlerType) {//获取到handlerType类中所有的带有ExceptionHandler注解的方法,for (Method method : MethodIntrospector.selectMethods(handlerType, EXCEPTION_HANDLER_METHODS)) {//解析ExceptionHandler注解中的属性, 因为可以配置多个异常类型, 所有返回的是集合, 进行遍历for (Class<? extends Throwable> exceptionType : detectExceptionMappings(method)) {//将异常类型作为key, 方法作为value, 存入到mappedMethods中addExceptionMapping(exceptionType, method);}}}private List<Class<? extends Throwable>> detectExceptionMappings(Method method) {List<Class<? extends Throwable>> result = new ArrayList<>();//获取method上的ExceptionHandler注解的所有的异常类型,存入到result中, 返回detectAnnotationExceptionMappings(method, result);if (result.isEmpty()) {for (Class<?> paramType : method.getParameterTypes()) {if (Throwable.class.isAssignableFrom(paramType)) {result.add((Class<? extends Throwable>) paramType);}}}if (result.isEmpty()) {throw new IllegalStateException("No exception types mapped to " + method);}return result;}private void addExceptionMapping(Class<? extends Throwable> exceptionType, Method method) {Method oldMethod = this.mappedMethods.put(exceptionType, method);if (oldMethod != null && !oldMethod.equals(method)) {throw new IllegalStateException("Ambiguous @ExceptionHandler method mapped for [" +exceptionType + "]: {" + oldMethod + ", " + method + "}");}}

结尾

上面已经说了到逻辑代码发生异常之后, 我们自定义的异常处理类如何加载处理这些异常(同时包括系统启动时的一些系统异常), 但是如果抛出一种没有处理类的异常会怎么样呢? 下篇文章在讨论一下

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

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

相关文章

飞桨花滑骨骼点动作识别比赛记 2

基于 PaddleVideo 的花滑骨骼点动作识别 2s-AGCN配置文件节点流配置文件 2s-agcn_ntucs_joint_fsd.yamlMODEL 字段DATASET 字段PIPELINE 和 INFERENCE 字段OPTIMIZER 字段 agcn2s.pygraph输入通道数 骨骼流 Dataset 和 Pipeline配置文件DATASETPIPELINE 源码skeleton.pyskeleto…

渗透测试漏洞原理之---【CSRF跨站请求伪造】

文章目录 1、CSRF概述1.1、基本原理1.1.1、基本概念1.1.2、关键点1.1.3、目标 1.2、CSRF场景1.2.1、银行支付转账1.2.2构造虚假网站1.2.3、场景建模 1.3、CSRF类别1.3.1、POST方式 1.4、CSRF验证1.4.1、CSRF PoC Generator 2、CSRF攻防2.1、CSRF实战2.1.1、与XSS 漏洞相结合 2.…

R语言对综合社会调查GSS数据进行自举法bootstrap统计推断、假设检验、探索性数据分析可视化|数据分享...

全文链接&#xff1a;https://tecdat.cn/?p33514 综合社会调查&#xff08;GSS&#xff09;是由国家舆论研究中心开展的一项观察性研究。自 1972 年以来&#xff0c;GSS 一直通过收集当代社会的数据来监测社会学和态度趋势。其目的是解释态度、行为和属性的趋势和常量。从 197…

Linux学习之lvm删除

umount /mnt/logicvolumntest卸载挂载。 lvremove /dev/vgname/my_lv可以删除逻辑卷&#xff0c;其中vgname是指定逻辑卷所在的卷组名称&#xff0c;my_lv是逻辑卷的名称。 注意&#xff1a;使用lvremove命令会永久删除逻辑卷和其中的数据&#xff0c;因此请在使用之前进行适当…

高并发(水平扩展,垂直扩展)

高并发(水平扩展&#xff0c;垂直扩展) 一、什么是高并发 高并发&#xff08;High Concurrency&#xff09; 是互联网分布式系统架构设计中必须考虑的因素之一&#xff0c;它通常是指&#xff0c; 通过设计保证系统能够同时并行处理很多请求。 高并发相关常用的一些指标有 响…

基于VUE3+Layui从头搭建通用后台管理系统(前端篇)十一:通用表单组件封装实现

一、本章内容 本章实现通用表单组件,根据实体配置识别实体属性,并自动生成编辑组件,实现对应数据填充、校验及保存等逻辑。 1. 详细课程地址: 待发布 2. 源码下载地址: 待发布 二、界面预览 三、开发视频 3.1 B站视频地址:

Hadoop依赖环境配置与安装部署

目录 什么是Hadoop&#xff1f;一、Hadoop依赖环境配置1.1 设置静态IP地址1.2 重启网络1.3 再克隆两台服务器1.4 修改主机名1.5 安装JDK1.6 配置环境变量1.7 关闭防火墙1.8 服务器之间互传资料1.9 做一个host印射1.10 免密传输 二、Hadoop安装部署2.1 解压hadoop的tar包2.2 切换…

LOL提示找不到d3dcompiler_47.dll怎么修复?多个修复方法分享

大家好&#xff01;今天&#xff0c;我将为大家分享一个关于玩游戏时遇到的问题——d3dcompiler_47.dll缺失无法运行的困扰&#xff0c;以及如何修复这个困扰的方法。希望我的分享能够帮助到大家&#xff0c;让我们一起来解决问题吧&#xff01; ​首先&#xff0c;我们来了解一…

c 语言关于未声明变量赋值机制

1. 示例代码&#xff1a; /* 测试输出结果 */ #include <stdio.h>int main(void) {int i 0;i 1, 2, 3;printf("i %d\n", i);return 0; } 2. 运行结果&#xff1a;

Graylog 更改显示的时区(Display timezone)

每个 Graylog 用户都可以配置他们的显示时区。 这会更改用于查看日志消息的时区&#xff0c;但不会更改日志消息的原始时区。 默认情况下&#xff0c;Graylog 显示 UTC 格式的所有时间戳&#xff08;也就是 0:00&#xff09;。就像是下面这样 非Admin账户要更改时区&#xff1…

QT DAY4

一、对话框 消息对话框、字体对话框、颜色对话框、文件对话框 1.1消息对话框 主要分为这四类对话及一种NoIcon无图标对话 而对话框也分为两种实现方式&#xff0c;一种为基于属性分开初始化的方式&#xff0c;这种方式更灵活&#xff0c;更多元&#xff0c;需要对exec的返回值…

八、性能测试

八、性能测试 8.1 性能测试代码 #include"ConcurrentAlloc.h"// ntimes 一轮申请和释放内存的次数 // rounds 轮次 void BenchmarkMalloc(size_t ntimes, size_t nworks, size_t rounds) {std::vector<std::thread> vthread(nworks);std::atomic<size_t&g…

Jetpack Compose 自定义 好看的TabRow Indicator

背景 Jetpack Compose 提供了强大的 Material Design 组件,其中 TabRow 组件可以用于实现 Material Design 规范的选项卡界面。但是默认的 TabRow 样式可能无法满足所有场景,所以我们有时需要自定义 TabRow 的样式。 Jetpack Compose 中使用 TabRow 简单使用 TabRow 一般可以…

如何在小红书进行学习直播

诸神缄默不语-个人CSDN博文目录 因为我是从B站开始的&#xff0c;所以一些直播常识型的东西请见我之前写的如何在B站进行学习直播这一篇。 本篇主要介绍一些小红书之与B站不同之处。 小红书在手机端是可以直接点击“”选择直播的。 文章目录 1. 电脑直播-小红书直播软件2. 电…

【前端demo】背景渐变动画

文章目录 效果过程代码htmlcss 其他demo 效果 效果预览&#xff1a;https://codepen.io/karshey/pen/OJrXZwQ 过程 注意&#xff0c;直接在body上加height:100%可能也会出现height为0的情况&#xff0c;这是需要令html的height:100% 代码 html <!DOCTYPE html> <…

【数据库】关系模型介绍+形式化关系查询语言

目录 第2章 关系模型介绍 2.1 关系数据库的结构 关系 2.2 数据库模式 2.3 码 2.4 模式图 大学数据库的模式图&#xff01;&#xff01;&#xff01; 大学数据库关系模式&#xff01;&#xff01;&#xff01; 2.5 关系查询语言 2.6 关系运算 2. 7 总结 第6章 形式化…

哪个视觉语言模型更优?InstructBLIP、MiniGPT-4?全面评估基准LVLM-eHub告诉你

夕小瑶科技说 原创 作者 | 王思若 LLaMA、GPT-3等大型语言模型实现了对自然语言强大的理解和推理能力&#xff0c;为AI社区构筑了强大的语言基座模型。进而&#xff0c;继续迭代的GPT-4&#xff0c;更是赋予了模型处理图像的视觉能力。 如今&#xff0c;构建强大的多模态模型…

python conda实践 sanic框架gitee webhook实践

import subprocess import hmac import hashlib import base64 from sanic.response import text from sanic import Blueprint from git import Repo# 路由蓝图 hook_blue Blueprint(hook_blue)hook_blue.route(/hook/kaifa, methods["POST"]) async def kaifa(req…

【Unity3D】UI Toolkit容器

1 前言 UI Toolkit简介 中介绍了 UI Builder、样式属性、UQuery&#xff0c;本文将介绍 UI Toolkit 中的容器&#xff0c;主要包含 VisualElement、ScrollView、ListView、UI Toolkit&#xff0c;官方介绍详见→UXML elements reference。 2 VisualElement&#xff08;空容器&…

Gazebo仿真环境下的强化学习实现

Gazebo仿真环境下的强化学习实现 主体源码参照《Goal-Driven Autonomous Exploration Through Deep Reinforcement Learning》 文章目录 Gazebo仿真环境下的强化学习实现1. 源码拉取2. 强化学习实现2.1 环境2.2 动作空间2.3 状态空间2.4 奖励空间2.5 TD3训练 3. 总结 1. 源码…