SpringMVC系列-6 异常处理器

news2025/1/13 2:57:29

背景

本文作为 SpringMVC系列 的第六篇,介绍SpringMVC的异常处理器。内容包括异常处理器的使用方式、实现原理和内置异常处理器的装配过程。

1.使用方式

自定义异常类,用于异常处理器:

public class ClientException extends RuntimeException {
    public ClientException(String message) {
        super(message);
    }
}

定义处理ClientException异常的逻辑:

@RestControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(ClientException.class)
    public ResponseEntity<?> handleClientException(ClientException exception) {
        return ResponseEntity.status(400).body(exception.getMessage());
    }
}

@RestControllerAdvice用于向SpringMVC注册异常处理器,@ExceptionHandler用于声明异常类对应的处理逻辑。

@RestController
@RequestMapping("/api")
public class DemoController {
    @GetMapping("/demo")
    public String demo(HttpServletRequest request) {
        if (Strings.isEmpty(request.getHeader("Authorization"))) {
            throw new ClientException("Auth failed.");
        }
        return "success";
    }
}

Note: 当请求头中携带"Authorization"字段,则返回"success",否则抛出ClientException异常。

postman模拟,不携带Authorization头域:
在这里插入图片描述

postman模拟,携带Authorization头域:
在这里插入图片描述

2.异常处理过程

说明1:异常请求和文件上传功能不是本文关注的重点,为突出主线逻辑,本文会可以略去对该部分的介绍。
说明2: 本文以SpringMVC系列1-5为前提,对相同部分不再介绍。

当请求经过Tomcat进入DispatcherServlet中后,线程进入以下逻辑:

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
	//...
	try {
		try {
			if (!mappedHandler.applyPreHandle(processedRequest, response)) {
				return;
			}
			mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
			mappedHandler.applyPostHandle(processedRequest, response, mv);
		} 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 {
	//... 
	}
}

从整体结构上看,代码包含两层try-catch,分别对Exeception和Throwable进行捕获,以保证内层异常不会向外传播;即无论当HTTP请求处理过程是否有异常,processDispatchResult方法都会被执行;区别是,如果处理正常时,dispatchException对象为空。
进入processDispatchResult方法:

private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv, @Nullable Exception exception) throws Exception {
      boolean errorView = false;
      if (exception != null) {
          if (exception instanceof ModelAndViewDefiningException) {//...}
          else {
              Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
              // 异常处理逻辑
              mv = processHandlerException(request, response, handler, exception);
              errorView = (mv != null);
          }
      }
      if (mv != null && !mv.wasCleared()) {//...} else {//日志打印...}
      if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {//...处理异步请求}
      if (mappedHandler != null) {
          // 调用拦截器的afterCompletion方法[HTTP请求正常处理过程也会调用]
          mappedHandler.triggerAfterCompletion(request, response, null);
      }
  }

Note: processDispatchResult方法的核心逻辑在processHandlerException方法,对processHandlerException方法进行逻辑提取得到:

protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) throws Exception {
     ModelAndView exMv = null;
     //遍历List<HandlerExceptionResolver> handlerExceptionResolvers属性调用元素HandlerExceptionResolver对象的resolveException方法,直到返回的ModelAndView对象不为空;[标注1]
     if (this.handlerExceptionResolvers != null) {
         for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) {
             exMv = resolver.resolveException(request, response, handler, ex);
             if (exMv != null) {
                 break;
             }
         }
     }
     if (exMv != null) {
         if (exMv.isEmpty()) {
             request.setAttribute(EXCEPTION_ATTRIBUTE, ex);
             return null;
         }
     }
     //遍历完handlerExceptionResolvers后,exMv仍然为null,则继续抛出该异常[标注2]
     throw ex;
 }

Note:
[1] handlerExceptionResolvers属性与异常处理器的关系图如下所示:
按需遍历DefaultErrorAttributes和HandlerExceptionResolverComposite,其中DefaultErrorAttributes仅对HttpServletRequest对象添加属性,返回的ModelAndView为空对象。即处理逻辑在HandlerExceptionResolverComposite.
[2] 遍历完handlerExceptionResolvers后,exMv仍然为null,则将该异常抛出给Tomcat,由Tomcat处理

private void exception(Request request, Response response, Throwable exception) {
    request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, exception);
    response.setStatus(500);
    response.setError();
}

最后返回的HTTP响应的状态码为500.

继续HandlerExceptionResolverComposite的介绍:

public class HandlerExceptionResolverComposite implements HandlerExceptionResolver, Ordered {
     private List<HandlerExceptionResolver> resolvers;

     // ...
     
     @Override
     @Nullable
     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;
     }
 }

HandlerExceptionResolverComposite是一个组合类型,内部维护了一个List<HandlerExceptionResolver>类型的属性resolvers,异常解析任务委托给了resolvers属性。resolvers属性在初始化时确定了成员,包含ExceptionHandlerExceptionResolverResponseStatusExceptionResolverDefaultHandlerExceptionResolver. 顺序确定了其优先级,即ExceptionHandlerExceptionResolver优先级最高。
ExceptionHandlerExceptionResolver
用于处理使用@ControllerAdvice注解的类中的@ExceptionHandler方法抛出的异常。当控制器方法抛出异常时,Spring MVC会查找@ControllerAdvice注解的类中是否有匹配的@ExceptionHandler方法,
如果有,则使用ExceptionHandlerExceptionResolver来处理异常。
ResponseStatusExceptionResolver
用于处理使用@ResponseStatus注解的异常。当控制器方法抛出使用@ResponseStatus注解标注的异常时,Spring MVC会使用ResponseStatusExceptionResolver来处理异常。
DefaultHandlerExceptionResolver:用于处理其他未被处理的异常。当控制器方法抛出其他未被处理的异常时,Spring MVC会使用DefaultHandlerExceptionResolver来处理异常。

Note: 第一章中自定义的异常处理器就关联在ExceptionHandlerExceptionResolver对象中。
进入ExceptionHandlerExceptionResolver的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);
       // 日志打印...
       return result;
   } else {
       return null;
   }
}

shouldApplyTo方法是否应该处理,prepareResponse进行预处理,doResolveException实际进行异常解析。在装配时确定了ExceptionHandlerExceptionResolver对象的mappedHandlersmappedHandlerClasses属性为空,因此shouldApplyTo方法默认返回true(其他两个解析器相同);prepareResponse方法用于对响应头添加标记;因preventResponseCaching为false, 不会进行操作.

进入doResolveException方法:

protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerMethod handlerMethod, Exception exception) {
       // 获取ServletInvocableHandlerMethod对象,如果返回为空表示没有异常没有匹配的处理器
       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();
       
       Throwable cause = exception.getCause();
       // 根据异常是否设置了cause属性进行区分调用重载的invokeAndHandle方法;二者主体逻辑相同,仅在参数解析阶段存在区别。
       if (cause != null) {
           // 反射调用目标方法, 调用自定义异常解析器中匹配的方法
           exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception, cause, handlerMethod);
       } else {
           exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception, handlerMethod);
       }
       return new ModelAndView();
   }

上述流程与调用Controller接口过程较为相似,相同部分不再说明。请参考SpringMVC系列其他文章。核心逻辑在于getExceptionHandlerMethod方法如何构造ServletInvocableHandlerMethod;
删除getExceptionHandlerMethod方法中与异常解析无关的逻辑:

protected ServletInvocableHandlerMethod getExceptionHandlerMethod(@Nullable HandlerMethod handlerMethod, Exception exception) {  
 	// 获取异常所在Controller类,如果被代理了,返回原始类型
    Class<?> handlerType = handlerMethod.getBeanType();
    if (Proxy.isProxyClass(handlerType)) {
        handlerType = AopUtils.getTargetClass(handlerMethod.getBean());
    }

    // 从exceptionHandlerAdviceCache属性中获取异常解析器(遍历、匹配、处理)标注[1]
    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) {
                // 标注[2]
                return new ServletInvocableHandlerMethod(advice.resolveBean(), method);
            }
        }
    }
    return null;
}

Note:
[1] 匹配根据异常类型进行,this.exceptionHandlerAdviceCache属性中包含了异常类型与异常处理方法的映射关系。 如:

@RestControllerAdvice
public class MyClientExceptionHandler {
    @ExceptionHandler(ClientException1.class)
    public ResponseEntity<?> handleClientException1(ClientException1 exception) {
        System.out.println(exception);
        return ResponseEntity.status(400).body("client error 1");
    }
    
    @ExceptionHandler(ClientException2.class)
    public ResponseEntity<?> handleClientException2(ClientException2 exception) {
        System.out.println(exception);
        return ResponseEntity.status(400).body("client error 2");
    }
}

@RestControllerAdvice
public class MyServerExceptionHandler {
    @ExceptionHandler(ServerException1.class)
    public ResponseEntity<?> handleServerException1(ServerException1 exception) {
        System.out.println(exception);
        return ResponseEntity.status(400).body("server error 1");
    }
    
    @ExceptionHandler(ServerException2.class)
    public ResponseEntity<?> handleServerException2(ServerException2 exception) {
        System.out.println(exception);
        return ResponseEntity.status(400).body("server error 2");
    }
}

则:this.exceptionHandlerAdviceCache保存了如下信息:
MyClientExceptionHandler实例 -> [ClientException1类型 -> handleClientException1方法, ClientException2类型 -> handleClientException2方法]
MyServerExceptionHandler实例 -> [ServerException1类型 -> handleServerException1方法, ServerException2类型 -> handleServerException2方法]
使得可以通过异常类型,如ClientException1找到MyClientExceptionHandler实例以及handleClientException1方法信息。
诚然从this.exceptionHandlerAdviceCache结构中获取信息不够友好,框架为此添加了中间变量和缓存。

[2] new ServletInvocableHandlerMethod(advice.resolveBean(), method);
通过advice.resolveBean()method构造ServletInvocableHandlerMethod对象返回。
其中:advice.resolveBean()表示自定义的Bean对象(@ControllerAdvice注解的对象),如MyClientExceptionHandlerMyServerExceptionHandler
method表示Controller接口对应的方法。
跟踪new ServletInvocableHandlerMethod(advice.resolveBean(), method)进入ServletInvocableHandlerMethod父类的构造器:

public HandlerMethod(Object bean, Method method) {
	this.bean = bean;
	this.beanFactory = null;
	this.beanType = ClassUtils.getUserClass(bean);
	this.method = method;
	this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
	this.parameters = initMethodParameters();
	evaluateResponseStatus();
	this.description = initDescription(this.beanType, this.method);
}

这里的bridgedMethod属性是后续反射调用的方法实例,来源于自定义异常解析器中的方法,
MyClientExceptionHandlerhandleClientException1方法或handleClientException2方法。
temp9:
最后看一下ExceptionHandlerExceptionResolverexceptionHandlerAdviceCache属性的初始化过程,
该属性保存了异常到异常处理方法的映射关系。
ExceptionHandlerExceptionResolver实现了InitializingBean接口,即被注册到IOC容器前会执行的afterPropertiesSet方法:

@Override
public void afterPropertiesSet() {
	// 设置exceptionHandlerAdviceCache属性[标注1]
	initExceptionHandlerAdviceCache();
	
	// 设置框架默认的参数解析器
	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);
	}
}

Note:
initExceptionHandlerAdviceCache方法进行关键逻辑提取后,得到:

private void initExceptionHandlerAdviceCache() {
	// 从IOC容器中获取被@ControllerAdvice注解的Bean对象
	List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
	// 遍历+判断+添加
	for (ControllerAdviceBean adviceBean : adviceBeans) {
		Class<?> beanType = adviceBean.getBeanType();
		// 构造ExceptionHandlerMethodResolver对象[标注1]
		ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(beanType);
		if (resolver.hasExceptionMappings()) {
			this.exceptionHandlerAdviceCache.put(adviceBean, resolver);
		}
	}
}

进入new ExceptionHandlerMethodResolver(beanType):

public ExceptionHandlerMethodResolver(Class<?> handlerType) {
	// 获取该类中所有被@ExceptionHandler注解的方法[遍历+判断+添加]
	for (Method method : MethodIntrospector.selectMethods(handlerType, EXCEPTION_HANDLER_METHODS)) {
		// 获取方法中所有的异常参数
		for (Class<? extends Throwable> exceptionType : detectExceptionMappings(method)) {
			// 添加到mappedMethods属性中保存[标记1]
			addExceptionMapping(exceptionType, method);
		}
	}
}

Note:
当某种类型的异常第一次匹配时,从mappedMethods属性中获取(获取后,关联关系保存在缓存中),后续从缓存中获取。自定义异常处理器时使用@ControllerAdvice,也常使用@RestControllerAdvice注解。@RestControllerAdvice是@ControllerAdvice的子类,等价于@RestControllerAdvice+@ResponseBody
即异常解析方法放回的结果也会经过 RequestResponseBodyMethodProcessor 处理,参考SpringMVC系列-5 消息转换器.

3.异常处理器注册

最后,再关心一下SpringBoot是如何将HandlerExceptionResolverComposite注册到框架中的。

spring-boot-autoconfigure模块的spring.factories中有如下定义:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
	org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration

项目启动时,通过SpringBoot自动装配机制向IOC容器注入WebMvcAutoConfiguration对象;WebMvcAutoConfiguration类定义如下所示:

@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class})
@ConditionalOnMissingBean({WebMvcConfigurationSupport.class})
@AutoConfigureOrder(-2147483638)
@AutoConfigureAfter({DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class, ValidationAutoConfiguration.class})
public class WebMvcAutoConfiguration {
	// ...
	
    @Configuration(proxyBeanMethods = false)
    public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware {
        // ...
    }
}

其中,EnableWebMvcConfiguration通过@Configuration注解方式导入,该类(的子类)通过@Bean向容器中导入HandlerExceptionResolver对象,涉及代码如下所示:

@Bean
public HandlerExceptionResolver handlerExceptionResolver(@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager) {
	List<HandlerExceptionResolver> exceptionResolvers = new ArrayList<>();
	configureHandlerExceptionResolvers(exceptionResolvers);
	if (exceptionResolvers.isEmpty()) {
		// 获取默认的异常处理器[标注1]
		addDefaultHandlerExceptionResolvers(exceptionResolvers, contentNegotiationManager);
	}
	extendHandlerExceptionResolvers(exceptionResolvers);
	HandlerExceptionResolverComposite composite = new HandlerExceptionResolverComposite();
	composite.setOrder(0);
	composite.setExceptionResolvers(exceptionResolvers);
	return composite;
}

Note:
handlerExceptionResolver方法的逻辑可以拆成两部分:创建exceptionResolvers对象,使用exceptionResolvers构造HandlerExceptionResolverComposite对象。框架装配的ExceptionHandlerExceptionResolver、ResponseStatusExceptionResolver、DefaultHandlerExceptionResolver是通过addDefaultHandlerExceptionResolvers方法获取得到。

protected final void addDefaultHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers, ContentNegotiationManager mvcContentNegotiationManager) {
	// 1.添加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);
	}
	exceptionHandlerResolver.afterPropertiesSet();
	exceptionResolvers.add(exceptionHandlerResolver);

	// 2.添加ResponseStatusExceptionResolver对象
	ResponseStatusExceptionResolver responseStatusResolver = new ResponseStatusExceptionResolver();
	responseStatusResolver.setMessageSource(this.applicationContext);
	exceptionResolvers.add(responseStatusResolver);
	
	// 3.添加ResponseStatusExceptionResolver对象
	exceptionResolvers.add(new DefaultHandlerExceptionResolver());
}

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

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

相关文章

宝塔安装MySQL数据库,并内网穿透实现公网远程访问

宝塔安装MySQL数据库&#xff0c;并内网穿透实现公网远程访问 文章目录 宝塔安装MySQL数据库&#xff0c;并内网穿透实现公网远程访问前言1.Mysql服务安装2.创建数据库3.安装cpolar3.2 创建HTTP隧道 4.远程连接5.固定TCP地址5.1 保留一个固定的公网TCP端口地址5.2 配置固定公网…

操作系统——页表、快表 + 地址转化过程(王道视频p47、p48、p49)

1.页表的基本概念&#xff1a; &#xff08;1&#xff09;part1: &#xff08;2&#xff09;part2: (备注&#xff1a;默认——逻辑页 和 物理页 的页大小是相同的&#xff01;&#xff01;&#xff01;) 2.在没有 快表的case下&#xff0c;进行逻辑地址 、 物理地址转换的流…

如何使用Codesys编程

在实际项目中&#xff0c;使用了两套TLC6伺服系统&#xff0c;不仅是因为TLC6具有电子凸轮功能&#xff0c;而且TLC6伺服驱动器内置PLC&#xff0c;使实现系统功能的编程更简单方便&#xff0c;且由于是内部编程&#xff0c;使系统响应更迅速。 TLC6内置PLC的编程环境为符合IEC…

C语言--输出格式控制(printf函数)

格式输出函数printf printf(格式控制&#xff0c;输出表列) 基本用法 格式字符功能例子d输出一个有符号的十进制整数printf("%d %d",12,-56);c输出一个字符 char ch a; printf("%c",ch); s输出一个字符串printf("%s","oh my god&…

Go学习第十六章——Gin文件上传与下载

Go web框架——Gin文件上传与下载 1. 文件上传1.1 入门案例&#xff08;单文件&#xff09;1.2 服务端保存文件的几种方式SaveUploadedFileCreateCopy 1.3 读取上传的文件1.4 多文件上传 2. 文件下载2.1 快速入门2.2 前后端模式下的文件下载2.3 中文乱码问题 1. 文件上传 1.1 …

年度最佳榜单之 10 大免费数据恢复软件

我们经常会因为不小心从硬盘驱动器、SD 卡和数码相机等存储设备中删除重要数据而丢失它们。还有许多其他原因&#xff0c;如格式化、病毒攻击和不当操作都可能导致数据丢失。在本文中&#xff0c;我向您推荐年度最佳数据恢复软件列表&#xff0c;以帮助恢复丢失的数据。 10 大免…

强大易于编辑的流程图组织图绘制工具draw.io Mac苹果中文版

draw.io可以绘制多种类型的图表&#xff0c;包括但不限于流程图、组织结构图、网络图、UML图、电气工程图等。draw.io提供了丰富的图形元素和编辑功能&#xff0c;使用户能够轻松地创建和编辑各种复杂的图表。同时&#xff0c;该软件还支持多种导出格式&#xff0c;方便用户在不…

Openssl数据安全传输平台017:客户端在Linux上的编译与调试

客户端代码在widows上编译&#xff0c;除了protobuf找不到目录&#xff0c;其他的基本没有什么问题。 然后打开虚拟机&#xff0c;项目文件已经在/home/projects目录下了 进入项目文件&#xff0c;对代码进行编译 第一次 // 找不到protobuf g *.cpp *.cc -ljson -lpthread -…

计算机中了locked勒索病毒怎么办,locked勒索病毒解密,数据恢复

当下网络技术飞速发展&#xff0c;但同样带来的网络安全威胁也不断增加&#xff0c;其中较为明显的威胁就是locked勒索病毒&#xff0c;自从今年以来&#xff0c;很多企业的计算机都遭受到了locked勒索病毒攻击&#xff0c;导致企业的计算机系统瘫痪。通过云天数据恢复工程师对…

【LeetCode每日一题合集】2023.10.23-2023.10.29(简单的一周)

文章目录 2678. 老人的数目&#xff08;简单遍历模拟&#xff09;1155. 掷骰子等于目标和的方法数&#xff08;动态规划&#xff09;2698. 求一个整数的惩罚数&#xff08;预处理dfs回溯&#xff09;2520. 统计能整除数字的位数&#xff08;简单模拟&#xff09;1465. 切割后面…

✨✨✨if __name__ == “__main__“ 在 Python 中意味着什么?

✨✨✨if __name__ "__main__" 在 Python 中意味着什么&#xff1f; 1. 原理2. 总结参考 在阅读其他人的代码时&#xff0c;你可能遇到过 Python 的 if__name__ “main” 习惯用法。这篇博客将了解有关 Python if name “main” 习惯用法的所有信息。 1. 原理 这…

linux 模块安装与卸载

文章目录 模块实现编译模块的 makefile编译报错解决模块编译日志自动化模块安装模块卸载 模块实现 新建 my_module.c 文件 #include <linux/types.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/module.h>static int __ini…

python随手小练8(南农作业题)

题目1: 输入3 门课程 a,b,c 的成绩,求 3 门成绩的总和平均值(整数,四舍五人)以及最高和最低值。如果3门课程考试成绩分别以权重 0.50.3 和0.2计人总评成绩(整数先求总和再四舍五入),则最终总评成绩是多少? 具体操作&#xff1a; a float(input("a:")) b float(in…

基于Java的体育竞赛成绩管理系统设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09; 代码参考数据库参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作者&am…

qt高精度定时器的使用停止线程应用

##线程停止 //线程停止应用 public: explicit WorkerThread(QObject *parent 0) :QThread(parent), m_bStopped(false){qDebug() << "Worker Thread : " << QThread::currentThreadId();}~WorkerThread(){stop();quit();wait();}void stop() {qDebug()…

JEnv使用初体验

Java多版本控制器初体验 1、前言 由于公司项目使用jdk8版本&#xff0c;而日常学习会使用其他版本例如jdk17等&#xff0c;往常都是修改环境配置目录实现。 2、下载资料 链接&#xff1a;https://pan.baidu.com/s/1UqzHv8K8WBu-75Ysyc_h3A 提取码&#xff1a;ra6a 3、安装 …

Java中的类你了解多少(每日一练)

文章目录 类的加载方式类的加载流程类的生命周期加载验证准备解析初始化类的销毁 类加载器有哪些什么是双亲委派模型&#xff1f; 类的加载方式 开发工具可以将java源代码编译为class字节码&#xff0c;类加载器加载class字节码&#xff0c;将字节码中的内容分配到内存中&#…

Go学习第十八章——Gin日志与第三方工具Logrus

Go web框架——Gin日志与第三方工具Logrus Gin日志功能1 基础介绍1.1 快速入门1.2 日志基础使用1.3 将日志输出到文件 2 定义路由格式3 修改日志级别4 修改日志格式 第三方日志工具logrus1 快速入门1.1 安装1.2 使用 2 基本功能使用2.1 设置日志输出格式2.2 设置日志输出位置2.…

TYWZOJ 种树苗 待定题解

文章目录 题目描述输入格式输出格式样例样例输入样例输出 数据范围与提示思路与部分实现完整代码 题目描述 在游戏 Minecraft 中&#xff0c;玩家可以通过种树来使木材再生。玩家需要将树苗种在泥土上&#xff0c;然后等待它长成大树&#xff0c;期间可以利用骨粉来催熟树苗。…

[NSSCTF 2nd] web刷题记录

文章目录 php签到MyBox非预期解预期解 php签到 源代码 <?phpfunction waf($filename){$black_list array("ph", "htaccess", "ini");$ext pathinfo($filename, PATHINFO_EXTENSION);foreach ($black_list as $value) {if (stristr($ext, …