该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读

Spring 版本:5.2.4.RELEASE

该系列其他文档请查看:《精尽 Spring MVC 源码分析 - 文章导读》

HandlerMapping 组件

HandlerMapping 组件,请求的处理器匹配器,负责为请求找到合适的 HandlerExecutionChain 处理器执行链,包含处理器(handler)和拦截器们(interceptors)

  • handler 处理器是 Object 类型,可以将其理解成 HandlerMethod 对象(例如我们使用最多的 @RequestMapping 注解所标注的方法会解析成该对象),包含了方法的所有信息,通过该对象能够执行该方法

  • HandlerInterceptor 拦截器对处理请求进行增强处理,可用于在执行方法前、成功执行方法后、处理完成后进行一些逻辑处理

由于 HandlerMapping 组件涉及到的内容比较多,考虑到内容的排版,所以将这部分内容拆分成了四个模块,依次进行分析:

  • 《HandlerMapping 组件(一)之 AbstractHandlerMapping》
  • 《HandlerMapping 组件(二)之 HandlerInterceptor 拦截器》
  • 《HandlerMapping 组件(三)之 AbstractHandlerMethodMapping》
  • 《HandlerMapping 组件(四)之 AbstractUrlHandlerMapping》

HandlerMapping 组件(一)之 AbstractHandlerMapping

先来回顾一下在 DispatcherServlet 中处理请求的过程中哪里使用到 HandlerMapping 组件,可以回到《一个请求的旅行过程》中的 DispatcherServlet 的 doDispatch 方法中看看,如下:

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
// ... 省略相关代码
// Determine handler for the current request.
// <3> 获得请求对应的 HandlerExecutionChain 对象(HandlerMethod 和 HandlerInterceptor 拦截器们)
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) { // <3.1> 如果获取不到,则根据配置抛出异常或返回 404 错误
noHandlerFound(processedRequest, response);
return;
}
// ... 省略相关代码
} @Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
// 遍历 handlerMappings 组件们
for (HandlerMapping mapping : this.handlerMappings) {
// 通过 HandlerMapping 组件获取到 HandlerExecutionChain 对象
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
// 不为空则直接返回
return handler;
}
}
}
return null;
}

通过遍历 HandlerMapping 组件们,根据请求获取到对应 HandlerExecutionChain 处理器执行链。注意,这里是通过一个一个的 HandlerMapping 组件去进行处理,如果找到对应 HandlerExecutionChain 对象则直接返回,不会继续下去,所以初始化的 HandlerMapping 组件是有一定的先后顺序的,默认是BeanNameUrlHandlerMapping -> RequestMappingHandlerMapping

HandlerMapping 接口

org.springframework.web.servlet.HandlerMapping 接口,请求的处理器匹配器,负责为请求找到合适的 HandlerExecutionChain 处理器执行链,包含处理器(handler)和拦截器们(interceptors),代码如下:

public interface HandlerMapping {

	String BEST_MATCHING_HANDLER_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingHandler";

	String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE = HandlerMapping.class.getName() + ".pathWithinHandlerMapping";

	String BEST_MATCHING_PATTERN_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingPattern";

	String INTROSPECT_TYPE_LEVEL_MAPPING = HandlerMapping.class.getName() + ".introspectTypeLevelMapping";

	String URI_TEMPLATE_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".uriTemplateVariables";

	String MATRIX_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".matrixVariables";

	String PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE = HandlerMapping.class.getName() + ".producibleMediaTypes";

	/**
* 获得请求对应的处理器和拦截器们
*/
@Nullable
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}

类图

HandlerMapping 接口体系的结构如下:

  • 蓝色框 AbstractHandlerMapping 抽象类,实现了“为请求找到合适的 HandlerExecutionChain 处理器执行链”对应的的骨架逻辑,而暴露 getHandlerInternal(HttpServletRequest request) 抽象方法,交由子类实现。

  • AbstractHandlerMapping 的子类,分成两派,分别是:

    • 黄色框 AbstractUrlHandlerMapping 系,基于 URL 进行匹配。例如 《基于 XML 配置的 Spring MVC 简单的 HelloWorld 实例应用》 ,当然,目前这种方式已经基本不用了,被 @RequestMapping 等注解的方式所取代。不过,Spring MVC 内置的一些路径匹配,还是使用这种方式。
    • 红色框 AbstractHandlerMethodMapping 系,基于 Method 进行匹配。例如,我们所熟知的 @RequestMapping 等注解的方式。
  • 绿色框的 MatchableHandlerMapping 接口,定义了“判断请求和指定 pattern 路径是否匹配”的方法。

初始化过程

在 DispatcherServlet 的 initHandlerMappings(ApplicationContext context) 方法,会在 onRefresh 方法被调用,初始化 HandlerMapping 组件,方法如下:

private void initHandlerMappings(ApplicationContext context) {
// 置空 handlerMappings
this.handlerMappings = null; // <1> 如果开启探测功能,则扫描已注册的 HandlerMapping 的 Bean 们,添加到 handlerMappings 中
if (this.detectAllHandlerMappings) {
// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
// 扫描已注册的 HandlerMapping 的 Bean 们
Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context,
HandlerMapping.class, true, false);
// 添加到 handlerMappings 中,并进行排序
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList<>(matchingBeans.values());
// We keep HandlerMappings in sorted order.
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
}
// <2> 如果关闭探测功能,则获得 Bean 名称为 "handlerMapping" 对应的 Bean ,将其添加至 handlerMappings
else {
try {
HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm);
}
catch (NoSuchBeanDefinitionException ex) {
// Ignore, we'll add a default HandlerMapping later.
}
}
// Ensure we have at least one HandlerMapping, by registering
// a default HandlerMapping if no other mappings are found.
/**
* <3> 如果未获得到,则获得默认配置的 HandlerMapping 类
* {@link org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping}
* {@link org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping}
*/
if (this.handlerMappings == null) {
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
if (logger.isTraceEnabled()) {
logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
"': using default strategies from DispatcherServlet.properties");
}
}
}
  1. 如果“开启”探测功能,则扫描已注册的 HandlerMapping 的 Bean 们,添加到 handlerMappings 中,默认开启

  2. 如果“关闭”探测功能,则获得 Bean 名称为 "handlerMapping" 对应的 Bean ,将其添加至 handlerMappings

  3. 如果未获得到,则获得默认配置的 HandlerMapping 类,调用 getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) 方法,就是从 DispatcherServlet.properties 文件中读取 HandlerMapping 的默认实现类,如下:

    org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
    org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping

    可以看到对应的是 BeanNameUrlHandlerMapping 和 RequestMappingHandlerMapping 对象

AbstractHandlerMapping

org.springframework.web.servlet.handler.AbstractHandlerMapping,实现 HandlerMapping、Ordered、BeanNameAware 接口,继承 WebApplicationObjectSupport 抽象类

该类是 HandlerMapping 接口的抽象基类,实现了“为请求找到合适的 HandlerExecutionChain 处理器执行链”对应的的骨架逻辑,而暴露 getHandlerInternal(HttpServletRequest request) 抽象方法,交由子类实现

WebApplicationObjectSupport 抽象类,提供 applicationContext 属性的声明和注入。

构造方法

public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport implements HandlerMapping, Ordered, BeanNameAware {

	/**
* 默认处理器
*/
@Nullable
private Object defaultHandler; /**
* URL 路径工具类
*/
private UrlPathHelper urlPathHelper = new UrlPathHelper(); /**
* 路径匹配器
*/
private PathMatcher pathMatcher = new AntPathMatcher(); /**
* 配置的拦截器数组.
*
* 在 {@link #initInterceptors()} 方法中,初始化到 {@link #adaptedInterceptors} 中
*
* 添加方式有两种:
* 1. {@link #setInterceptors(Object...)} 方法
* 2. {@link #extendInterceptors(List)} 方法
*/
private final List<Object> interceptors = new ArrayList<>(); /**
* 初始化后的拦截器 HandlerInterceptor 数组
*/
private final List<HandlerInterceptor> adaptedInterceptors = new ArrayList<>(); private CorsConfigurationSource corsConfigurationSource = new UrlBasedCorsConfigurationSource(); private CorsProcessor corsProcessor = new DefaultCorsProcessor(); private int order = Ordered.LOWEST_PRECEDENCE; // default: same as non-Ordered /**
* 当前 Bean 的名称
*/
@Nullable
private String beanName; // ... 省略相关 getter、setter 方法
}
  • defaultHandler:默认处理器,在获得不到处理器时,可使用该属性

  • interceptors:配置的拦截器数组

  • adaptedInterceptors:初始化后的拦截器 HandlerInterceptor 数组,也就是interceptors 转换成的 HandlerInterceptor 拦截器对象

initApplicationContext

initApplicationContext()方法,用于初始化拦截器们,方法如下:

在父类 WebApplicationObjectSupport 的父类 ApplicationObjectSupport 中可以看到,因为实现了 ApplicationContextAware 接口,则在初始化该 Bean 的时候会调用 setApplicationContext(@Nullable ApplicationContext context) 方法,在这个方法中会调用 initApplicationContext() 这个方法

@Override
protected void initApplicationContext() throws BeansException {
// <1> 空实现,交给子类实现,用于注册自定义的拦截器到 interceptors 中,目前暂无子类实现
extendInterceptors(this.interceptors);
// <2> 扫描已注册的 MappedInterceptor 的 Bean 们,添加到 mappedInterceptors 中
detectMappedInterceptors(this.adaptedInterceptors);
// <3> 将 interceptors 初始化成 HandlerInterceptor 类型,添加到 mappedInterceptors 中
initInterceptors();
}
  1. 调用 extendInterceptors(List<Object> interceptors) 方法,空方法,目前暂无子类实现,暂时忽略

  2. 调用 detectMappedInterceptors(List<HandlerInterceptor> mappedInterceptors) 方法,从 Spring 的上下文中,扫描已注册的 MappedInterceptor 的拦截器们,添加到 adaptedInterceptors 中,方法如下:

    protected void detectMappedInterceptors(List<HandlerInterceptor> mappedInterceptors) {
    // 扫描已注册的 MappedInterceptor 的 Bean 们,添加到 mappedInterceptors 中
    // MappedInterceptor 会根据请求路径做匹配,是否进行拦截
    mappedInterceptors.addAll(BeanFactoryUtils
    .beansOfTypeIncludingAncestors(obtainApplicationContext(), MappedInterceptor.class, true, false)
    .values());
    }
  3. 调用 initInterceptors() 方法,将 interceptors 初始化成 HandlerInterceptor 类型,添加到 adaptedInterceptors 中,方法如下:

    protected void initInterceptors() {
    if (!this.interceptors.isEmpty()) {
    for (int i = 0; i < this.interceptors.size(); i++) {
    Object interceptor = this.interceptors.get(i);
    if (interceptor == null) {
    throw new IllegalArgumentException("Entry number " + i + " in interceptors array is null");
    }
    // 将 interceptors 初始化成 HandlerInterceptor 类型,添加到 mappedInterceptors 中
    // 注意,HandlerInterceptor 无需进行路径匹配,直接拦截全部
    this.adaptedInterceptors.add(adaptInterceptor(interceptor));
    }
    }
    }
    protected HandlerInterceptor adaptInterceptor(Object interceptor) {
    if (interceptor instanceof HandlerInterceptor) {
    return (HandlerInterceptor) interceptor;
    }
    else if (interceptor instanceof WebRequestInterceptor) {
    return new WebRequestHandlerInterceptorAdapter((WebRequestInterceptor) interceptor);
    }
    else {
    throw new IllegalArgumentException("Interceptor type not supported: " + interceptor.getClass().getName());
    }
    }

关于拦截器在后文进行分析

getHandler

getHandler(HttpServletRequest request) 方法,获得请求对应的 HandlerExecutionChain 处理器执行链,包含处理器(handler)和拦截器们(interceptors),方法如下:

@Override
@Nullable
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
// <1> 获得处理器(HandlerMethod 或者 HandlerExecutionChain),该方法是抽象方法,由子类实现
Object handler = getHandlerInternal(request);
// <2> 获得不到,则使用默认处理器
if (handler == null) {
handler = getDefaultHandler();
}
// <3> 还是获得不到,则返回 null
if (handler == null) {
return null;
}
// Bean name or resolved handler?
// <4> 如果找到的处理器是 String 类型,则从 Spring 容器中找到对应的 Bean 作为处理器
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
} // <5> 创建 HandlerExecutionChain 对象(包含处理器和拦截器)
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request); if (logger.isTraceEnabled()) {
logger.trace("Mapped to " + handler);
}
else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {
logger.debug("Mapped to " + executionChain.getHandler());
} if (CorsUtils.isCorsRequest(request)) {
CorsConfiguration globalConfig = this.corsConfigurationSource.getCorsConfiguration(request);
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
} return executionChain;
}
  1. 调用getHandlerInternal(HttpServletRequest request) 抽象方法,获得 handler 处理器

  2. 如果 handler 处理器没有找到,则调用getDefaultHandler() 方法,使用默认处理器,也就是 defaultHandler 属性

  3. 如果 handler 处理器没有找到,且没有默认的处理器,则直接返回 null

  4. 如果找到的处理器是 String 类型,可能是 Bean 的名称,则从 Spring 容器中找到对应的 Bean 作为处理器

  5. 调用 getHandlerExecutionChain(Object handler, HttpServletRequest request) 方法,获得 HandlerExecutionChain 对象,方法如下:

    protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
    // <1> 创建 HandlerExecutionChain 对象
    HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ? (HandlerExecutionChain) handler
    : new HandlerExecutionChain(handler)); // <2> 获得请求路径
    String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
    // <3> 遍历 adaptedInterceptors 数组,获得请求匹配的拦截器
    for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
    // 需要匹配,若路径匹配,则添加到 chain 中
    if (interceptor instanceof MappedInterceptor) {
    MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
    if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) { // 匹配
    chain.addInterceptor(mappedInterceptor.getInterceptor());
    }
    }
    // 无需匹配,直接添加到 chain 中
    else {
    chain.addInterceptor(interceptor);
    }
    }
    return chain
    }
    1. 创建一个 HandlerExecutionChain 对象,如果 handler 处理器就是该类型对象,则直接使用
    2. 获得请求路径
    3. 遍历 adaptedInterceptors 拦截器数组,根据请求路径获得当前请求匹配的拦截器们,添加到 HandlerExecutionChain 对象中
  6. 返回上面创建的 HandlerExecutionChain 对象

MatchableHandlerMapping

org.springframework.web.servlet.handler.MatchableHandlerMapping,定义了“判断请求和指定 pattern 路径是否匹配”的方法。代码如下:

public interface MatchableHandlerMapping extends HandlerMapping {

	/**
* 判断请求和指定 pattern 路径是否匹配
*/
@Nullable
RequestMatchResult match(HttpServletRequest request, String pattern);
}

RequestMatchResult

org.springframework.web.servlet.handler.RequestMatchResult 类,判断请求和指定 pattern 路径是否匹配时,返回的匹配结果,代码如下:

public class RequestMatchResult {
/**
* 匹配到的路径
*/
private final String matchingPattern; /**
* 被匹配的路径
*/
private final String lookupPath; /**
* 路径匹配器
*/
private final PathMatcher pathMatcher; public RequestMatchResult(String matchingPattern, String lookupPath, PathMatcher pathMatcher) {
Assert.hasText(matchingPattern, "'matchingPattern' is required");
Assert.hasText(lookupPath, "'lookupPath' is required");
Assert.notNull(pathMatcher, "'pathMatcher' is required");
this.matchingPattern = matchingPattern;
this.lookupPath = lookupPath;
this.pathMatcher = pathMatcher;
} public Map<String, String> extractUriTemplateVariables() {
return this.pathMatcher.extractUriTemplateVariables(this.matchingPattern, this.lookupPath);
}
}

目前实现 MatchableHandlerMapping 接口的类,有 RequestMappingHandlerMapping 类和 AbstractUrlHandlerMapping 抽象类,在后续都会进行分析

总结

本文对 Spring MVC 处理请求的过程中使用到的 HandlerMapping 组件进行了分析,会为请求找到合适的 HandlerExecutionChain 处理器执行链,包含处理器(handler)和拦截器们(interceptors)

HandlerMapping 组件的实现类分为两种:

  • 基于 URL 进行匹配。例如 《基于 XML 配置的 Spring MVC 简单的 HelloWorld 实例应用》 ,当然,目前这种方式已经基本不用了,被 @RequestMapping 等注解的方式所取代。不过,Spring MVC 内置的一些路径匹配,还是使用这种方式
  • 基于 Method 进行匹配。例如,我们所熟知的 @RequestMapping 等注解的方式

AbstractHandlerMapping 抽象类,作为一个基类,实现了“为请求找到合适的 HandlerExecutionChain 处理器执行链”对应的的骨架逻辑,而暴露 getHandlerInternal(HttpServletRequest request) 抽象方法,交由子类实现。

本文对 HandlerMapping 组件做了一个简单的介绍,更多的细节交由其子类去实现,由于涉及到的内容比较多,BeanNameUrlHandlerMapping 和 RequestMappingHandlerMapping 两种实现类则在后续的文档中依次进行分析

参考文章:芋道源码《精尽 Spring MVC 源码分析》

精尽Spring MVC源码分析 - HandlerMapping 组件(一)之 AbstractHandlerMapping的更多相关文章

  1. 精尽Spring MVC源码分析 - HandlerMapping 组件(二)之 HandlerInterceptor 拦截器

    该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...

  2. 精尽Spring MVC源码分析 - HandlerMapping 组件(三)之 AbstractHandlerMethodMapping

    该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...

  3. 精尽Spring MVC源码分析 - HandlerMapping 组件(四)之 AbstractUrlHandlerMapping

    该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...

  4. 精尽Spring MVC源码分析 - HandlerAdapter 组件(一)之 HandlerAdapter

    该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...

  5. 精尽Spring MVC源码分析 - HandlerAdapter 组件(二)之 ServletInvocableHandlerMethod

    该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...

  6. 精尽Spring MVC源码分析 - HandlerAdapter 组件(三)之 HandlerMethodArgumentResolver

    该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...

  7. 精尽Spring MVC源码分析 - HandlerAdapter 组件(四)之 HandlerMethodReturnValueHandler

    该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...

  8. 精尽Spring MVC源码分析 - HandlerExceptionResolver 组件

    该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...

  9. 精尽Spring MVC源码分析 - MultipartResolver 组件

    该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...

随机推荐

  1. SpringBoot WebSocket 消息交互

    1. Websocket原理 Websocket协议本质上是一个基于TCP的独立协议,能够在浏览器和服务器之间建立双向连接,以基于消息的机制,赋予浏览器和服务器间实时通信能力. WebSocket资源 ...

  2. No matching distribution found for Tensorflow

    No matching distribution found for Tensorflow 原因:python 3.7.3 版本过高 解决:安装3.6  64位

  3. PHP一些不一样的思路

    大多数来自p牛 SQL注入(left join) 源代码 <?php $link = mysqli_connect('localhost', 'root', 'root'); mysqli_se ...

  4. go编译成exe后,打开出现闪退问题

    今天博主编译了一个go小脚本,编译完成后用自己电脑试了一下没有问题 然而,当换了一台电脑后,一样是win7系统64位,出现了闪退 于是乎博主疯狂百度 看到网上说   入口文件 的顶部改成  packa ...

  5. 如何将IDM中的进程设置进行备份

    有时候我们想用浏览器自带的下载管理器进行下载,但是一点下载却被IDM(Internet Download Manager)自动嗅探捕获并下载,还有人因为重装系统使得之前更改IDM的设置都失效,只得重新 ...

  6. MAC系统网页链接如何下载

    Folx 5 是一款简单易用.功能强大的Mac OS系统的下载管理器.Folx界面简洁,下载管理方便,支持网页链接下载.BT下载和YouTube下载,而且还可以设置计划任务.搜索BT种子以及添加Tra ...

  7. vulnhub: DC 9

    信息收集: root@kali:/opt/test# nmap -A -v 192.168.76.137 Starting Nmap 7.80 ( https://nmap.org ) at 2020 ...

  8. 【操作系统】先来先服务和短作业优先算法(C语言实现)

    [操作系统] 先来先服务算法和短作业优先算法实现 介绍: 1.先来先服务 (FCFS: first come first service) 如果早就绪的进程排在就绪队列的前面,迟就绪的进程排在就绪队列 ...

  9. Django中ModelForm详解

    1.ModelForm组件介绍:这个组件的功能就是把model和form组合起来 2.ModelForm的使用 1.首先需要导入ModelForm from django.forms import M ...

  10. 技巧:如何区分dll程序集的编译目标平台(同样适用于查看程序集的其它依赖)

    我们在进行net core迁移过程中,有时候需要区分一个dll是针对netstandard平台还是net framework. 本文提供一个技巧来快速区分:通过工具dnSpy打开目标dll,按照如下截 ...