当前位置: 首页 > news >正文

SpringBoot 全局异常处理用法及原理

SpringBoot 全局异常处理用法及原理

Springboot或springMVC项目中, 我们一般会设置一个全局异常处理, 来对异常进行兜底。 业务代码执行过程中抛出的异常, 如果业务逻辑没有主动捕获,那么异常就会一直往上抛,最后进入全局异常处理逻辑。

本文和大家探讨SpringBoot 全局异常处理用法及原理, 整体分为三个部分:第一部分,讲spring如何处理一个http请求异常;第二部分, 讲全局异常处理的几种方法; 第三部分,探究一下全局异常处理的底层原理。

1、springboot如何处理一个http请求异常

SpingBoot中,web请求由DispatcherServlet类的doDispatch方法来处理,如果处理过程抛出了异常,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 {processedRequest = checkMultipart(request);multipartRequestParsed = (processedRequest != request);// Determine handler for the current request.mappedHandler = getHandler(processedRequest);if (mappedHandler == null || mappedHandler.getHandler() == null) {noHandlerFound(processedRequest, response);return;}// Determine handler adapter for the current request.HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());// code omitted// Actually invoke the handler.mv = ha.handle(processedRequest, response, mappedHandler.getHandler());if (asyncManager.isConcurrentHandlingStarted()) {return;}applyDefaultViewName(processedRequest, mv);mappedHandler.applyPostHandle(processedRequest, response, mv);}catch (Exception ex) {dispatchException = ex;}catch (Throwable err) {// As of 4.3, we're processing Errors thrown from handler methods as well,// making them available for @ExceptionHandler methods and other scenarios.dispatchException = new NestedServletException("Handler dispatch failed", err);}// this is where thrown exception is handled  处理异常的地方processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);}// code omitted}}

processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException)方法是异常处理的入口,然后委派给processHandlerException(request, response, handler, exception)方法来处理。

	/*** Handle the result of handler selection and handler invocation, which is* either a ModelAndView or an Exception to be resolved to a ModelAndView.* */private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {boolean errorView = false;if (exception != null) {if (exception instanceof ModelAndViewDefiningException) {logger.debug("ModelAndViewDefiningException encountered", exception);mv = ((ModelAndViewDefiningException) exception).getModelAndView();}else {// get handler from handlerchain, in most cases the hanlder is a HanlderMethodObject handler = (mappedHandler != null ? mappedHandler.getHandler() : null);// delegate to this method  委派给processHandlerExceptionmv = processHandlerException(request, response, handler, exception);errorView = (mv != null);}}// code omitted
	protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,Object handler, Exception ex) throws Exception {// Check registered HandlerExceptionResolvers...ModelAndView exMv = null;// 重点代码for (HandlerExceptionResolver handlerExceptionResolver : this.handlerExceptionResolvers) {exMv = handlerExceptionResolver.resolveException(request, response, handler, ex);if (exMv != null) {break;}}if (exMv != null) {if (exMv.isEmpty()) {request.setAttribute(EXCEPTION_ATTRIBUTE, ex);return null;}// We might still need view name translation for a plain error model...if (!exMv.hasView()) {exMv.setViewName(getDefaultViewName(request));}if (logger.isDebugEnabled()) {logger.debug("Handler execution resulted in exception - forwarding to resolved error view: " + exMv, ex);}WebUtils.exposeErrorRequestAttributes(request, ex, getServletName());return exMv;}throw ex;}

可以看出, 这里有一组异常解析器,按照优先级由高到低排列。遍历异常解析器, 逐一调用resolveException方法来处理异常, 异常一旦得到处理,就break,这样就保证了,优先级高的处理器优先处理异常。 关于springboot如何处理http请求异常,了解这么多就够了。

至于这些handlerExceptionResolvers是怎么注册的? 第三部分会解释。

2、自定义全局异常处理的三种方法

2.1 实现HandlerExceptionResolver接口(不推荐)

看过第一部分,我们知道springboot的http请求异常由一组异常解析器来处理, 那么我们自然可以创建自己的异常解析器,然后把它加到现有的解析器中。
Spring已经为我们提供了抽象类AbstractHandlerExceptionResolver(Abstract base class for HandlerExceptionResolver implementations), 我们可以选择继承AbstractHandlerExceptionResolver来创建自己的全局异常解析器。

例如,

@Component  //autowired
public class MyExceptionResolver extends AbstractHandlerExceptionResolver {@Override   //异常解析器的顺序, 数值越小,表示优先级越高public int getOrder() {return -1;}@Override  // write your exception-handle codeprotected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {System.out.println("hello from MyExceptionResolver");if(ex.instanceOf(BusinessException)){// ...// business code// ...}}
}

这里MyExceptionResolver继承了AbstractHandlerExceptionResolver, 并重写了getOrder和doResolveException方法。在doResolveException中, 我根据异常的类型, 做相应的处理。

从图片可以看出来MyExceptionResolver注册进来了, 而且因为让getOrder方法返回了-1, 所以MyExceptionResolver排在了最前面,可以优先处理异常。

但在实际应用中,几乎没见过使用这种方法,这里提出来主要是加深大家对原理的理解。

2.2. @ExceptionHandler + BaseController(不推荐)

第二种方法, 是使用@ExceptionHandler注解 + BaseController。
例如,

@Controller
public class HelloWorldController {private final Logger logger = LoggerFactory.getLogger(HelloWorldController.class);@Autowiredprivate HelloWorldService helloWorldService;@RequestMapping(value = "/hello/{name:.+}", method = RequestMethod.GET)public ModelAndView hello(@PathVariable("name") String name) throws Exception {logger.debug("hello--> {}", name);throw new BusinessException();}@ExceptionHandler({BusinessException.class})  // 这里可以定义要处理的一组异常类型private ModelAndView handleException(Exception e){System.out.println("hello from in  controller");// handle BusinessException}@ExceptionHandler({BusinessException1.class, BusinessException2.class})  // 这里可以定义要处理的异常类型private ModelAndView handleException1(Exception e){System.out.println("hello from in  controller");// handle BusinessException1 and BusinessException2}
}

这里,在HelloWorldController中, 定义了两个由@ExceptionHandler注释的方法来处理异常。如果HelloWorldController的hello方法(或者其他方法)执行过程中, 抛出BusinessException类型的异常, 那么会被handleException方法捕获到; 如果抛出了BusinessException1类型或是BusinessException2类型的异常,则会被handleException1方法捕获到。

通过@ExceptionHandler注解的方式, 我们可以通过让不同的方法处理不同的异常。

但是这种方式是不是意味着, 我们需要在每个controller中都要定义自己@ExceptionHandler方法呢? 如果各自处理自己的异常, 那叫什么全局异常处理呢?

为此, 我们其实可以写一个BaseController, 将全局、通用的异常处理方法写在BaseController里, 需要进行全局异常处理的Controller继承BaseController。而个性化的异常处理,则写在具体的controller里。

最后看一下ExceptionHandler的代码

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ExceptionHandler {/*** Exceptions handled by the annotated method. If empty, will default to any* exceptions listed in the method argument list.* 是一个数组, 表示要处理的一组异常类型。 如果为空的话, 会处理注解所修饰方法参数代表的异常类型。*/Class<? extends Throwable>[] value() default {};}

方法二, 不如方法三灵活,几乎也没有人用。

2.3 @ExceptionHandler + @ControllerAdvice(推荐)

第三种方法是使用@ExceptionHandler注解 + @ControllerAdvice注解。通过给ExceptionHandler注解传入参数或者给方法添加异常类型的参数,可以让方法处理指定类型的一组异常。

@ControllerAdvice
public class GlabalExceptionHandler {@ExceptionHandler(BusinessException.class)private void handleException(Exception e){System.out.println("hello from glabal exception handler");// handle BusinessException}@ExceptionHandler({BusinessException1.class, BusinessException2.class})  // 这里可以定义要处理的一组异常类型private ModelAndView handleException1(Exception e){System.out.println("hello from glabal exception handler");// handle BusinessException1 and BusinessException2}
}

这里对于所有controller,如果有方法抛出BusinessException类型的异常, 会走到handleException方法; 如果抛出了BusinessException1类型或是BusinessException2类型的异常,则会被handleException1方法处理。

ControllerAdvice提供了灵活的方式, 来指定对哪些controller来进行异常处理。
看一下ControllerAdvice的代码

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface ControllerAdvice {/*** basePackages的别名,功能同下。*/@AliasFor("basePackages")String[] value() default {};/*** 指定一组包名。* 可以指定一组包名。 这些包(包括子包)里的所有controller,都会拥有异常处理的能力*/@AliasFor("value")String[] basePackages() default {};.../**** 指定一组类, controller如果可以赋值给其中任意一个类(controller就是该类或者是该类的子类,对于接口来说也是一样), 则该controller会拥有异常处理的能力。*/Class<?>[] assignableTypes() default {};...}

实际应用中,多采用这种方法。

3、原理

我们进一步讨论第二部分的三种用法的原理。

3.1. 实现HandlerExceptionResolver接口的原理

我们在第一部分中已经提到,在org.springframework.web.servlet.DispatcherServlet#processHandlerException方法中,会有一组异常解析器来解析异常,而且第一部分末尾留下了一个问题:这一组handlerExceptionResolvers是怎么注册的?

/*** This implementation calls {@link #initStrategies}.*/@Overrideprotected void onRefresh(ApplicationContext context) {initStrategies(context);}/*** Initialize the strategy objects that this servlet uses.* <p>May be overridden in subclasses in order to initialize further strategy objects.*/protected void initStrategies(ApplicationContext context) {initMultipartResolver(context);initLocaleResolver(context);initThemeResolver(context);initHandlerMappings(context);initHandlerAdapters(context);//调用初始异常处理器方法initHandlerExceptionResolvers(context);initRequestToViewNameTranslator(context);initViewResolvers(context);initFlashMapManager(context);}
/*** Initialize the HandlerExceptionResolver used by this class.* <p>If no bean is defined with the given name in the BeanFactory for this namespace,* we default to no exception resolver.*/private void initHandlerExceptionResolvers(ApplicationContext context) {this.handlerExceptionResolvers = null;if (this.detectAllHandlerExceptionResolvers) {// Find all HandlerExceptionResolvers in the ApplicationContext, including ancestor contexts.// 最关键一段代码,这里Map<String, HandlerExceptionResolver> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerExceptionResolver.class, true, false);if (!matchingBeans.isEmpty()) {this.handlerExceptionResolvers = new ArrayList<HandlerExceptionResolver>(matchingBeans.values());// We keep HandlerExceptionResolvers in sorted order.AnnotationAwareOrderComparator.sort(this.handlerExceptionResolvers);}}else {try {HandlerExceptionResolver her =context.getBean(HANDLER_EXCEPTION_RESOLVER_BEAN_NAME, HandlerExceptionResolver.class);this.handlerExceptionResolvers = Collections.singletonList(her);}catch (NoSuchBeanDefinitionException ex) {// Ignore, no HandlerExceptionResolver is fine too.}}// Ensure we have at least some HandlerExceptionResolvers, by registering// default HandlerExceptionResolvers if no other resolvers are found.if (this.handlerExceptionResolvers == null) {this.handlerExceptionResolvers = getDefaultStrategies(context, HandlerExceptionResolver.class);if (logger.isDebugEnabled()) {logger.debug("No HandlerExceptionResolvers found in servlet '" + getServletName() + "': using default");}}}

最终是在initHandlerExceptionResolvers方法完成异常解析器的初始化, 关键代码用中文注释标出。 这里首先会从spring容器中找出所有HandlerExceptionResolver类型(包括子类)的bean, 然后按照order进行排序。这样,我们自定义的异常解析器就会被spring注册并放到指定的位置。

3.2. @ExceptionHandler + Controller、@ExceptionHandler + @ControllerAdvice的原理

我们把方法二和方法三放到一起说。
Spring是依靠自带的ExceptionHandlerExceptionResolver这个异常解析器来支持这两种方式的。DispatcherServlet初始化时,会把ExceptionHandlerExceptionResolver注册到handlerExceptionResolvers中。弄清楚了ExceptionHandlerExceptionResolver就清楚了方法二和方法三的原理。

以下是ExceptionHandlerExceptionResolver的关键代码:

@Override
protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request,HttpServletResponse response, HandlerMethod handlerMethod, Exception exception) {//这里是关键ServletInvocableHandlerMethod exceptionHandlerMethod = getExceptionHandlerMethod(handlerMethod, exception);if (exceptionHandlerMethod == null) {return null;}//省略其它代码...
}

实际调用代码

protected ServletInvocableHandlerMethod getExceptionHandlerMethod(HandlerMethod handlerMethod, Exception exception) {Class<?> handlerType = null;if (handlerMethod != null) {// Local exception handler methods on the controller class itself.// To be invoked through the proxy, even in case of an interface-based proxy.handlerType = handlerMethod.getBeanType();//首先尝试从exceptionHandlerCache里找ExceptionHandlerMethodResolverExceptionHandlerMethodResolver 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);}// For advice applicability check below (involving base packages, assignable types// and annotation presence), use target class instead of interface-based proxy.if (Proxy.isProxyClass(handlerType)) {handlerType = AopUtils.getTargetClass(handlerMethod.getBean());}}//遍历ControllerAdvice去找异常处理方法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;
}

可以看出来:
首先会从exceptionHandlerCache中去找handlerMethod所属bean的class对应的ExceptionHandlerMethodResolver, 如果找不到则new一个ExceptionHandlerMethodResolver并缓存起来。 然后从ExceptionHandlerMethodResolver去找该exception对应的异常处理方法。
先上一张图,直观感受一下,来自第二部分方法二的例子:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZE6KRKbq-1676102871044)(/img/image-20230211160403740.png)]

ExceptionHandlerMethodResolver中存的是各个Exception到各个异常处理方法映射。

我们再看一下new ExceptionHandlerMethodResolver(handlerType)的实现, 不详细说了, 简单说一下关键点, 直接写在代码注释里。

/*** A constructor that finds {@link ExceptionHandler} methods in the given type.* @param handlerType the type to introspect*/
public ExceptionHandlerMethodResolver(Class<?> handlerType) {//找出handlerType(这里就是对应的Controller)里所有有@ExceptionHandler注解的方法,然后遍历方法for (Method method : MethodIntrospector.selectMethods(handlerType, EXCEPTION_HANDLER_METHODS)) {//	从一个异常处理方法中提取出处理的异常,然后遍历异常for (Class<? extends Throwable> exceptionType : detectExceptionMappings(method)) {//添加异常到方法的映射addExceptionMapping(exceptionType, method);}}
}
/*** Extract exception mappings from the {@code @ExceptionHandler} annotation first,* and then as a fallback from the method signature itself.*/
@SuppressWarnings("unchecked")
private List<Class<? extends Throwable>> detectExceptionMappings(Method method) {List<Class<? extends Throwable>> result = new ArrayList<Class<? extends Throwable>>();//先从注解中找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()) {//	有 @Exceptionhandler但是找不到任何异常类型, 会抛出异常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 + "}");}
}

看到这里, 相信方法二的原理大家应该清楚了。

接着往下看getExceptionHandlerMethod方法。 简单说就是遍历所有被@ControllerAdvice注解的bean, 如果该bean适用于本Controller,则去匹配异常处理方法。
关键看一下exceptionHandlerAdviceCache是如何初始化的?

private void initExceptionHandlerAdviceCache() {if (getApplicationContext() == null) {return;}if (logger.isDebugEnabled()) {logger.debug("Looking for exception mappings: " + getApplicationContext());}//找出所有被@controllerAdvice注解的bean, 然后排序List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());AnnotationAwareOrderComparator.sort(adviceBeans);for (ControllerAdviceBean adviceBean : adviceBeans) {//遍历bean, 然后解析其中的异常处理方法。 上面已经介绍过了。ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(adviceBean.getBeanType());if (resolver.hasExceptionMappings()) {this.exceptionHandlerAdviceCache.put(adviceBean, resolver);if (logger.isInfoEnabled()) {logger.info("Detected @ExceptionHandler methods in " + adviceBean);}}if (ResponseBodyAdvice.class.isAssignableFrom(adviceBean.getBeanType())) {this.responseBodyAdvice.add(adviceBean);if (logger.isInfoEnabled()) {logger.info("Detected ResponseBodyAdvice implementation in " + adviceBean);}}}
}

写到这里方法三的原理大家也应该明白了吧。

总结一下, 对于某一个controller,如果既有采用方法二配置的异常处理,也有方法三的全局异常配置, 那么方法二优先。也就是说, 如果在controller里已经找到了异常处理方法,则不会再去controlleradvicebean中找。 只有当controller里没有对应的处理方法,才会去 controlleradvicebean找。

相关文章:

SpringBoot 全局异常处理用法及原理

SpringBoot 全局异常处理用法及原理 Springboot或springMVC项目中&#xff0c; 我们一般会设置一个全局异常处理&#xff0c; 来对异常进行兜底。 业务代码执行过程中抛出的异常&#xff0c; 如果业务逻辑没有主动捕获&#xff0c;那么异常就会一直往上抛&#xff0c;最后进入…...

浏览器中HTTP请求流程是如何处理的

HTTP 是一种允许浏览器向服务器获取资源的协议&#xff0c;是 Web 的基础&#xff0c;通常由浏览器发起请求&#xff0c;用来获取不同类型的文件&#xff0c;例如 HTML 文件、CSS 文件、JavaScript 文件、图片、视频等。 浏览器端发起 HTTP 请求流程&#xff1a; 1. 构建请求…...

【Pytorch项目实战】之语义分割:U-Net、UNet++、U2Net

文章目录博主精品专栏导航一、前言1.1、什么是图像分割&#xff1f;1.2、语义分割与实例分割的区别1.3、语义分割的上下文信息1.4、语义分割的网络架构二、网络 数据集2.1、经典网络的发展史&#xff08;模型详解&#xff09;2.2、分割数据集下载三、算法详解3.1、U-Net3.1.1、…...

七、插件机制

Interceptor MyBatis 插件模块中最核心的接口就是 Interceptor 接口&#xff0c;它是所有 MyBatis 插件必须要实现的接口&#xff0c;其核心定义如下&#xff1a; public interface Interceptor {// 插件实现类中需要实现的拦截逻辑Object intercept(Invocation invocation) …...

kmp算法

前缀函数 π[i]max⁡k0,⋯,i{k∣s[0,⋯,k−1]s[i−(k−1),⋯,i]}\pi\left[i\right] \max\limits_{k 0,\cdots, i}\left\{k|s\left[0,\cdots,k-1\right] s\left[i-\left(k-1\right) ,\cdots, i\right]\right\} π[i]k0,⋯,imax​{k∣s[0,⋯,k−1]s[i−(k−1),⋯,i]} 简单来说…...

【Python】正则表达式简单教程

0x01 正则表达式概念及符号含义 掌握正则表达式&#xff0c;只需要记住不同符号所表示的含义&#xff0c;以及对目标对象模式&#xff08;或规律&#xff09;的正确概括。 1、基础内容 字符匹配 在正则表达式中&#xff0c;如果直接给出字符&#xff0c;就是精确匹配。\d 匹…...

SAP ABAP Odata

GetEntity和GetEntitys GetEntitys 创建Odata Project 导入结构 选择需要的字段 设定Key 勾选字段的creatable、updatable、sortable、nullable、filterable属性值。 再依上述步骤创建ZPOITEM结构和实体集 3. 创建ZPOHEADER和ZPOITEM的Association 两个实体集的关联字段&…...

Android native ASAN 排查内存泄漏

一、概述 android 对native - c/c 的调试和排查是比较难受的一件事。我看周遭做window &#xff0c; linux 甚至ios的调试排查起c的代码都比较方便。习惯了app开发去熟悉native是各种痛苦&#xff0c;最主要是排查问题上。后续有时间打算整理下native 的错误排查使用&#xff…...

Django项目开发

一.认识NoSQL 1.SQL 关系型数据库 结构化: 定义主键&#xff0c;无符号型数据等关联的&#xff1a;结构化表和表之间的关系通过外键进行关联&#xff0c;节省存储空间SQL查询&#xff1a;语法固定 SELECT id,name,age FROM tb_user WHERE id1 ACID 2.NoSQL 非关系型数据库 Re…...

Debezium系列之:深入理解Debezium Server和Debezium Server实际应用案例详解

Debezium系列之:深入理解Debezium Server和Debezium Server实际应用案例详解 一、认识Debezium Server二、下载Debezium Server三、解压Debezium Server四、查看Debezium Server目录五、Debezium Server配置六、Debezium Server启动输出样式七、源配置八、格式配置九、Transfo…...

IDE2022源码编译tomcat

因为学习需要&#xff0c;我需要源码编译运行tomcat对其源码进行一个简单的追踪分析。由于先前并未接触过java相关的知识&#xff0c;安装阻力巨大。最后请教我的开发朋友才解决了最后的问题。将其整理出来&#xff0c;让大家能够快速完成相关的部署。本文仅解决tomcat-8.5.46版…...

214 情人节来袭,电视剧 《点燃我温暖你》李峋同款 Python爱心表白代码,赶紧拿去用吧

大家好&#xff0c;我是徐公&#xff0c;六年大厂程序员经验&#xff0c;今天为大家带来的是动态心形代码&#xff0c;电视剧 《点燃我温暖你》同款的&#xff0c;大家赶紧看看&#xff0c;拿去向你心仪的对象表白吧&#xff0c;下面说一下灵感来源。 灵感来源 今天&#xff…...

数据库范式

基本概念 函数依赖 x→yx\rightarrow yx→y&#xff0c;当确定xxx的时候&#xff0c;yyy也可以确定 例&#xff1a; 学号→\rightarrow→姓名&#xff0c;当知道了学号&#xff0c;就知道了学生姓名 学号,课程号→\rightarrow→成绩&#xff0c;当知道了学号和课程号&#xff…...

CUDA中的底层驱动API

文章目录CUDA底层驱动API1. Context2. Module3. Kernel Execution4. Interoperability between Runtime and Driver APIs5. Driver Entry Point Access5.1. Introduction5.2. Driver Function Typedefs5.3. Driver Function Retrieval5.3.1. Using the driver API5.3.2. Using …...

【博客616】prometheus staleness对PromQL查询的影响

prometheus staleness对PromQL查询的影响 1、prometheus staleness 官方文档的解释&#xff1a; 概括&#xff1a; 运行查询时&#xff0c;将独立于实际的当前时间序列数据选择采样数据的时间戳。这主要是为了支持聚合&#xff08;sum、avg 等&#xff09;等情况&#xff0c…...

多传感器融合定位十三-基于图优化的建图方法其二

多传感器融合定位十二-基于图优化的建图方法其二3.4 预积分方差计算3.4.1 核心思路3.4.2 连续时间下的微分方程3.4.3 离散时间下的传递方程3.5 预积分更新4. 典型方案介绍4.1 LIO-SAM介绍5. 融合编码器的优化方案5.1 整体思路介绍5.2 预积分模型设计Reference: 深蓝学院-多传感…...

linux 服务器线上问题故障排查

一 线上故障排查概述 1.1 概述 线上故障排查一般从cpu,磁盘,内存,网络这4个方面入手; 二 磁盘的排查 2.1 磁盘排查 1.使用 df -hl 命令来查看磁盘使用情况 2.从读写性能排查:iostat -d -k -x命令来进行分析 最后一列%util可以看到每块磁盘写入的程度,而rrqpm/s以及…...

Sandman:一款基于NTP协议的红队后门研究工具

关于Sandman Sandman是一款基于NTP的强大后门工具&#xff0c;该工具可以帮助广大研究人员在一个安全增强型网络系统中执行红队任务。 Sandman可以充当Stager使用&#xff0c;该工具利用了NTP&#xff08;一个用于计算机时间/日期同步协议&#xff09;从预定义的服务器获取并…...

【SSL/TLS】准备工作:HTTPS服务器部署:Nginx部署

HTTPS服务器部署&#xff1a;Nginx部署1. 准备工作2. Nginx服务器YUM部署2.1 直接安装2.2 验证3. Nginx服务器源码部署3.1 下载源码包3.2 部署过程4. Nginx基本操作4.1 nginx常用命令行4.2 nginx重要目录1. 准备工作 1. Linux版本 [rootlocalhost ~]# cat /proc/version Li…...

微搭低代码从入门到精通11-数据模型

学习微搭低代码&#xff0c;先学习基本操作&#xff0c;然后学习组件的基本使用。解决了前端的问题&#xff0c;我们就需要深入学习后端的功能。后端一般包括两部分&#xff0c;第一部分是常规的数据库的操作&#xff0c;包括增删改查。第二部分是业务逻辑的编写&#xff0c;在…...

React hook之useRef

React useRef 详解 useRef 是 React 提供的一个 Hook&#xff0c;用于在函数组件中创建可变的引用对象。它在 React 开发中有多种重要用途&#xff0c;下面我将全面详细地介绍它的特性和用法。 基本概念 1. 创建 ref const refContainer useRef(initialValue);initialValu…...

VB.net复制Ntag213卡写入UID

本示例使用的发卡器&#xff1a;https://item.taobao.com/item.htm?ftt&id615391857885 一、读取旧Ntag卡的UID和数据 Private Sub Button15_Click(sender As Object, e As EventArgs) Handles Button15.Click轻松读卡技术支持:网站:Dim i, j As IntegerDim cardidhex, …...

Zustand 状态管理库:极简而强大的解决方案

Zustand 是一个轻量级、快速和可扩展的状态管理库&#xff0c;特别适合 React 应用。它以简洁的 API 和高效的性能解决了 Redux 等状态管理方案中的繁琐问题。 核心优势对比 基本使用指南 1. 创建 Store // store.js import create from zustandconst useStore create((set)…...

质量体系的重要

质量体系是为确保产品、服务或过程质量满足规定要求&#xff0c;由相互关联的要素构成的有机整体。其核心内容可归纳为以下五个方面&#xff1a; &#x1f3db;️ 一、组织架构与职责 质量体系明确组织内各部门、岗位的职责与权限&#xff0c;形成层级清晰的管理网络&#xf…...

镜像里切换为普通用户

如果你登录远程虚拟机默认就是 root 用户&#xff0c;但你不希望用 root 权限运行 ns-3&#xff08;这是对的&#xff0c;ns3 工具会拒绝 root&#xff09;&#xff0c;你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案&#xff1a;创建非 roo…...

OPenCV CUDA模块图像处理-----对图像执行 均值漂移滤波(Mean Shift Filtering)函数meanShiftFiltering()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 在 GPU 上对图像执行 均值漂移滤波&#xff08;Mean Shift Filtering&#xff09;&#xff0c;用于图像分割或平滑处理。 该函数将输入图像中的…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...

人机融合智能 | “人智交互”跨学科新领域

本文系统地提出基于“以人为中心AI(HCAI)”理念的人-人工智能交互(人智交互)这一跨学科新领域及框架,定义人智交互领域的理念、基本理论和关键问题、方法、开发流程和参与团队等,阐述提出人智交互新领域的意义。然后,提出人智交互研究的三种新范式取向以及它们的意义。最后,总结…...

【Redis】笔记|第8节|大厂高并发缓存架构实战与优化

缓存架构 代码结构 代码详情 功能点&#xff1a; 多级缓存&#xff0c;先查本地缓存&#xff0c;再查Redis&#xff0c;最后才查数据库热点数据重建逻辑使用分布式锁&#xff0c;二次查询更新缓存采用读写锁提升性能采用Redis的发布订阅机制通知所有实例更新本地缓存适用读多…...

uniapp 开发ios, xcode 提交app store connect 和 testflight内测

uniapp 中配置 配置manifest 文档&#xff1a;manifest.json 应用配置 | uni-app官网 hbuilderx中本地打包 下载IOS最新SDK 开发环境 | uni小程序SDK hbulderx 版本号&#xff1a;4.66 对应的sdk版本 4.66 两者必须一致 本地打包的资源导入到SDK 导入资源 | uni小程序SDK …...