1、HandlerMapping实现原理及源码解析

在前面讲解Spring MVC工作流程的时候我们说过,前端控制器收到请求后会调⽤处理器映射器(HandlerMapping),处理器映射器根据请求Url找到具体的处理器(Handler),生成处理器对象及处理器拦截器(如果有)一并返回给前端控制器。HandlerMapping就是处理器映射器,其作用就是根据当前请求的找到对应的 Handler,并将 Handler(执行程序)与一堆 HandlerInterceptor(拦截器)封装到HandlerExecutionChain 对象中。

HandlerMapping 具体的表现形式可以是类,也可以是⽅法。⽐如,标注了@RequestMapping的每个⽅法都可以看成是⼀个Handler。Handler负责具体实际的请求处理,在请求到达后,HandlerMapping 的作用便是找到请求相应的处理器Handler 和 Interceptor。

在 HandlerMapping 接口的内部只有一个方法:HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception; 这个方法由 AbstractHandlerMapping 实现,实现方法如下:

  /**
* Look up a handler for the given request, falling back to the default
* handler if no specific one is found.
* @param request current HTTP request
* @return the corresponding handler instance, or the default handler
* @see #getHandlerInternal
*/
@Override
@Nullable
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
// 根据请求获取执行程序,具体的获取方式由子类决定
// getHandlerInternal() 是抽象方法
Object handler = getHandlerInternal(request);
if (handler == null) {
handler = getDefaultHandler();
}
if (handler == null) {
return null;
}
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
} // 将 Handler 与一堆拦截器包装到 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;
}

可以看到在这个方法中调用了 getHandlerInternal() 方法获取到了 Handler 对象,而 Handler 对象具体内容是由它的子类去定义的。

HandlerMapping 有很多个实现类,如下图所示:

它主要有两个分支,分别继承自 AbstractHandlerMethodMapping(得到 HandlerMethod)和 AbstractUrlHandlerMapping(得到 HttpRequestHandler、Controller 或 Servlet),它们又统一继承于 AbstractHandlerMapping,如下图所示:

下面就来看一下 AbstractHandlerMapping 的两个分支子类(AbstractUrlHandlerMapping和AbstractHandlerMethodMapping)。

1.1、AbstractUrlHandlerMapping

AbstractUrlHandlerMapping 这个分支获取的 Handler 的类型实际就是一个 Controller 类,所以一个 Controller 只能对应一个请求(或者像 Struts2 那样定位到方法,使同一个业务的方法放在同一个类里).

上文中getHandler的实现方法中调用的getHandlerInternal方法(根据请求获取执行程序)就是在AbstractUrlHandlerMapping中,源码如下所示:

/**
* Look up a handler for the URL path of the given request.
* @param request current HTTP request
* @return the handler instance, or {@code null} if none found
*/
@Override
@Nullable
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
// 根据当前请求获取 查找路径
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
// 根据路径获取 Handler(即Controller),先尝试直接匹配,再尝试模式匹配
Object handler = lookupHandler(lookupPath, request);
if (handler == null) {
// We need to care for the default handler directly, since we need to
// expose the PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE for it as well.
Object rawHandler = null;
if ("/".equals(lookupPath)) {
rawHandler = getRootHandler();
}
if (rawHandler == null) {
rawHandler = getDefaultHandler();
}
if (rawHandler != null) {
// Bean name or resolved handler?
if (rawHandler instanceof String) {
String handlerName = (String) rawHandler;
rawHandler = obtainApplicationContext().getBean(handlerName);
}
validateHandler(rawHandler, request);
handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
}
}
return handler;
}

1.2、AbstractHandlerMethodMapping

AbstractHandlerMethodMapping 这个分支获取的 Handler 的类型是 HandlerMethod,即这个 Handler 是一个方法,它保存了方法的信息(如Method),这样一个 Controller 就可以处理多个请求了,源码如下所示:

/**
* Look up a handler method for the given request.
*/
@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
// 根据当前请求获取 查找路径
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
// 内部类对象获取锁
this.mappingRegistry.acquireReadLock();
try {
// 获取当前请求最佳匹配的处理方法(即Controller类的方法)
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
finally {
// 内部类对象释放锁
this.mappingRegistry.releaseReadLock();
}
}

上述代码中 lookupHandlerMethod() 方法的主要工作是在 Map<T, HandlerMethod> handlerMethods 中找到 HandlerMethod,这里的 T 是 HandlerMappingInfo,它封装了 @RequestMapping 注解中的信息。lookupHandlerMethod的源码如下图所示:

/**
* Look up the best-matching handler method for the current request.
* If multiple matches are found, the best match is selected.
* @param lookupPath mapping lookup path within the current servlet mapping
* @param request the current request
* @return the best-matching handler method, or {@code null} if no match
* @see #handleMatch(Object, String, HttpServletRequest)
* @see #handleNoMatch(Set, String, HttpServletRequest)
*/
@Nullable
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List<Match> matches = new ArrayList<>();
List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
if (directPathMatches != null) {
addMatchingMappings(directPathMatches, matches, request);
}
if (matches.isEmpty()) {
// No choice but to go through all mappings...
addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
} if (!matches.isEmpty()) {
Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
matches.sort(comparator);
Match bestMatch = matches.get(0);
if (matches.size() > 1) {
if (logger.isTraceEnabled()) {
logger.trace(matches.size() + " matching mappings: " + matches);
}
if (CorsUtils.isPreFlightRequest(request)) {
return PREFLIGHT_AMBIGUOUS_MATCH;
}
Match secondBestMatch = matches.get(1);
if (comparator.compare(bestMatch, secondBestMatch) == 0) {
Method m1 = bestMatch.handlerMethod.getMethod();
Method m2 = secondBestMatch.handlerMethod.getMethod();
String uri = request.getRequestURI();
throw new IllegalStateException(
"Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
}
}
request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
handleMatch(bestMatch.mapping, lookupPath, request);
return bestMatch.handlerMethod;
}
else {
return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
}
}

那么 HandlerMethod 是怎么创建的(即怎么把 Controller 的方法变成了它)?继续看一下源码找到 initHandlerMethods() 方法,这个方法是在这个类创建后调用的,如下所示是它的源码(包含其调用的子方法的源码):

    /**
* Scan beans in the ApplicationContext, detect and register handler methods.
* @see #getCandidateBeanNames()
* @see #processCandidateBean
* @see #handlerMethodsInitialized
*/
protected void initHandlerMethods() {
for (String beanName : getCandidateBeanNames()) {
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
processCandidateBean(beanName);
}
}
handlerMethodsInitialized(getHandlerMethods());
} /**
* Determine the names of candidate beans in the application context.
* @since 5.1
* @see #setDetectHandlerMethodsInAncestorContexts
* @see BeanFactoryUtils#beanNamesForTypeIncludingAncestors
*/
protected String[] getCandidateBeanNames() {
// 从容器中获取所有 Bean 的名称,detectHandlerMethodsInAncestorContexts 默认false,不从父容器中查找
// 即默认只查找 SpringMVC 的 IOC 容器,不查找它的父容器 Spring 的 IOC 容器
return (this.detectHandlerMethodsInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) :
obtainApplicationContext().getBeanNamesForType(Object.class));
} /**
* Determine the type of the specified candidate bean and call
* {@link #detectHandlerMethods} if identified as a handler type.
* <p>This implementation avoids bean creation through checking
* {@link org.springframework.beans.factory.BeanFactory#getType}
* and calling {@link #detectHandlerMethods} with the bean name.
* @param beanName the name of the candidate bean
* @since 5.1
* @see #isHandler
* @see #detectHandlerMethods
*/
protected void processCandidateBean(String beanName) {
Class<?> beanType = null;
try {
beanType = obtainApplicationContext().getType(beanName);
}
catch (Throwable ex) {
// An unresolvable bean type, probably from a lazy bean - let's ignore it.
if (logger.isTraceEnabled()) {
logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
}
}
// 这里的 isHandler()方法由子类实现,判断是否拥有 @Controller 注解或 @RequestMapping 注解
if (beanType != null && isHandler(beanType)) {
// 利用反射得到 Bean 中的 Method 并包装成 HandlerMethod,然后放入 Map 中
detectHandlerMethods(beanName);
}
} /**
* Look for handler methods in the specified handler bean.
* @param handler either a bean name or an actual handler instance
* @see #getMappingForMethod
*/
protected void detectHandlerMethods(Object handler) {
// 获取这个 Bean 的 Class 对象
Class<?> handlerType = (handler instanceof String ?
obtainApplicationContext().getType((String) handler) : handler.getClass()); if (handlerType != null) {
// 获取被代理前的原始类型
Class<?> userType = ClassUtils.getUserClass(handlerType); // 获取 Method
// 这里的 T 就是 RequestMappingInfo,它封装了 @RequestMapping 信息
Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
(MethodIntrospector.MetadataLookup<T>) method -> {
try {
// 根据 Method 和它的 @RequestMapping 注解,创建 RequestMappingInfo 对象。
return getMappingForMethod(method, userType);
}
catch (Throwable ex) {
throw new IllegalStateException("Invalid mapping on handler class [" +
userType.getName() + "]: " + method, ex);
}
});
if (logger.isTraceEnabled()) {
logger.trace(formatMappings(userType, methods));
}
methods.forEach((method, mapping) -> {
Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
// 注册 Method 和它的映射,RequestMappingInfo 储存着映射信息
registerHandlerMethod(handler, invocableMethod, mapping);
});
}
}

看完上述代码后,可以知道是在 detectHandlerMethods() 方法中将 Bean 的方法转换为 HandlerMethod 对象。

最后在 registerHandlerMethod() 方法中,将 RequestMappingInfo 作为 key,把 Method 包装成 HandlerMethod 作为 value 添加到了 Map<T, HandlerMethod> handlerMethods 中。registerHandlerMethod的源码如下所示:

    /**
* Register a handler method and its unique mapping. Invoked at startup for
* each detected handler method.
* @param handler the bean name of the handler or the handler instance
* @param method the method to register
* @param mapping the mapping conditions associated with the handler method
* @throws IllegalStateException if another method was already registered
* under the same mapping
*/
protected void registerHandlerMethod(Object handler, Method method, T mapping) {
this.mappingRegistry.register(mapping, handler, method);
}   public void register(T mapping, Object handler, Method method) {
this.readWriteLock.writeLock().lock();
try {
        HandlerMethod handlerMethod = createHandlerMethod(handler, method);
assertUniqueMethodMapping(handlerMethod, mapping);
this.mappingLookup.put(mapping, handlerMethod); List<String> directUrls = getDirectUrls(mapping);
for (String url : directUrls) {
this.urlLookup.add(url, mapping);
} String name = null;
if (getNamingStrategy() != null) {
name = getNamingStrategy().getName(handlerMethod, mapping);
addMappingName(name, handlerMethod);
} CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
if (corsConfig != null) {
this.corsLookup.put(handlerMethod, corsConfig);
} this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
} finally {
        this.readWriteLock.writeLock().unlock();
}
  }

1.3、AbstractHandlerMapping 实现类及使用

AbstractHandlerMethodMapping<T> 只有一个实现类:RequestMappingHandlerMapping(下文中会介绍该类)。

2、HandlerAdapter实现原理及源码解析

HandlerAdapter的作用是根据 Handler 来找到支持它的 HandlerAdapter,通过 HandlerAdapter 执行这个 Handler 得到 ModelAndView 对象。HandlerAdapter 接口中的方法如下:

它有以下实现类:

2.1、RequestMappingHandlerAdapter

从上面的文章中可以知道,利用 RequestMappingHandlerMapping 获取的 Handler 是 HandlerMethod 类型,它代表 Controller 里要执行的方法,而 RequestMappingHandlerAdapter 可以执行 HandlerMethod 对象。

RequestMappingHandlerAdapter 的 handle() 方法是在它的父类 AbstractHandlerMethodAdapter 类中实现的,源码如下所示:

    /**
* This implementation expects the handler to be an {@link HandlerMethod}.
*/
@Override
@Nullable
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return handleInternal(request, response, (HandlerMethod) handler);
}

handleInternal() 方法是由 RequestMappingHandlerAdapter 自己来实现的,源码如下所示:

    /**
* Use the given handler method to handle the request.
* @param request current HTTP request
* @param response current HTTP response
* @param handlerMethod handler method to use. This object must have previously been passed to the
* {@link #supportsInternal(HandlerMethod)} this interface, which must have returned {@code true}.
* @return a ModelAndView object with the name of the view and the required model data,
* or {@code null} if the request has been handled directly
* @throws Exception in case of errors
*/
@Override
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { ModelAndView mav;
checkRequest(request); // Execute invokeHandlerMethod in synchronized block if required.(是否需要在 synchronize 块中执行)
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
// 执行 HandlerMethod
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// No HttpSession available -> no mutex necessary
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// No synchronization on session demanded at all...
mav = invokeHandlerMethod(request, response, handlerMethod);
} if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
// 是否通过 @SessionAttributes 注释声明了 session 属性
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
}
else {
prepareResponse(response);
}
} // 执行 HandlerMethod(invokeHandlerMethod(request, response, handlerMethod);),得到 ModelAndView
return mav;
}

继续来看一下如何得到 ModelAndView,invokeHandlerMethod() 方法如下:

    /**
* Invoke the {@link RequestMapping} handler method preparing a {@link ModelAndView}
* if view resolution is required.
* @since 4.2
* @see #createInvocableHandlerMethod(HandlerMethod)
*/
@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
// 数据绑定
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory); // 绑定参数,执行方法
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
if (this.argumentResolvers != null) {
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
if (this.returnValueHandlers != null) {
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
invocableMethod.setDataBinderFactory(binderFactory);
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer); // 创建模型和视图容器
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
// 设置FlasgMap中的值
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
// 初始化模型
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect); AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
asyncWebRequest.setTimeout(this.asyncRequestTimeout); WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.setTaskExecutor(this.taskExecutor);
asyncManager.setAsyncWebRequest(asyncWebRequest);
asyncManager.registerCallableInterceptors(this.callableInterceptors);
asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors); if (asyncManager.hasConcurrentResult()) {
Object result = asyncManager.getConcurrentResult();
mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
asyncManager.clearConcurrentResult();
LogFormatUtils.traceDebug(logger, traceOn -> {
String formatted = LogFormatUtils.formatValue(result, !traceOn);
return "Resume with async result [" + formatted + "]";
});
invocableMethod = invocableMethod.wrapConcurrentResult(result);
} invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
} return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
webRequest.requestCompleted();
}
}

2.2、HttpRequestHandlerAdapter

HttpRequestHandlerAdapter 可以执行 HttpRequestHandler 类型的 Handler,源码如下:

    @Override
@Nullable
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
((HttpRequestHandler) handler).handleRequest(request, response);
return null;
}

2.3、SimpleControllerHandlerAdapter

SimpleControllerHandlerAdapter 可以执行 Controller 类型的 Handler,源码如下:

    @Override
@Nullable
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return ((Controller) handler).handleRequest(request, response);
}

2.4、SimpleServletHandlerAdapter

SimpleServletHandlerAdapter 可以执行 Servlet 类型的 Handler,源码如下:

    @Override
@Nullable
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
((Servlet) handler).service(request, response);
return null;
}

Spring MVC工作原理及源码解析(三) HandlerMapping和HandlerAdapter实现原理及源码解析的更多相关文章

  1. Spring MVC工作原理及源码解析(一) MVC原理介绍、与IOC容器整合原理

    MVC原理介绍 Spring MVC原理图 上图是Spring MVC工作原理图(图片来自网上搜索),根据上图,我们可以得知Spring MVC的工作流程如下: 1.用户(客户端,即浏览器)发送请求至 ...

  2. Spring MVC工作原理(好用版)

    Spring MVC工作原理 参考: SpringMVC工作原理 - 平凡希 - 博客园https://www.cnblogs.com/xiaoxi/p/6164383.html SpringMVC的 ...

  3. Spring MVC工作原理及源码解析(二)DispatcherServlet实现原理及源码解析

    1.DispatcherServlet 处理流程 从上一篇文章中Spring MVC原理图中我们可以看出:DispatcherServlet 在 Spring MVC框架 中处于核心位置,它负责协调和 ...

  4. Spring MVC工作原理 及注解说明

    SpringMVC框架介绍 1) spring MVC属于SpringFrameWork的后续产品,已经融合在Spring Web Flow里面. Spring 框架提供了构建 Web 应用程序的全功 ...

  5. Spring MVC 工作原理和流程、注解

    Spring MVC 是实现MVC设计模式的企业级开发框架,是Spring框架的一个子模块,无需整合,开发起来更加便捷. MVC设计模式 MVC是一种设计模式,它将应用程序分为 Controller. ...

  6. 浅析Spring MVC工作机制

    1.如何使用Spring MVC? 在web.xml中配置一个DispatcherServlet DispatchServlet初始化的时候会去寻找一个在应用程序的WEB-INF目录下的配置文件,命名 ...

  7. spring mvc 工作流程

    Spring Web MVC 处理Http请求的大致过程:一旦Http请求到来,DispatcherSevlet将负责将请求分发.DispatcherServlet可以认为是Spring提供的前端控制 ...

  8. Spring MVC工作流程

    本文回答Spring MVC如何处理一个请求的. 1.请求是由中央调度器DispatcherServlet接收的. 2.中央调度器将请求交给处理器映射器HandlerMapping,处理器映射器解析请 ...

  9. Spring MVC工作流程图

    图一   图二    Spring工作流程描述       1. 用户向服务器发送请求,请求被Spring 前端控制Servelt DispatcherServlet捕获:       2. Disp ...

随机推荐

  1. 【Azure 服务总线】详解Azure Service Bus SDK中接收消息时设置的maxConcurrentCalls,prefetchCount参数

    (Azure Service Bus服务总线的两大类消息处理方式: 队列Queue和主题Topic) 问题描述 使用Service Bus作为企业消息代理,当有大量的数据堆积再Queue或Topic中 ...

  2. PTA 求链式表的表长

    6-1 求链式表的表长 (10 分)   本题要求实现一个函数,求链式表的表长. 函数接口定义: int Length( List L ); 其中List结构定义如下: typedef struct ...

  3. 「免费开源」基于Vue和Quasar的前端SPA项目crudapi后台管理系统实战之自定义组件(四)

    基于Vue和Quasar的前端SPA项目实战之序列号(四) 回顾 通过上一篇文章 基于Vue和Quasar的前端SPA项目实战之布局菜单(三)的介绍,我们已经完成了布局菜单,本文主要介绍序列号功能的实 ...

  4. 「HTML+CSS」--自定义加载动画【005】

    前言 Hello!小伙伴! 首先非常感谢您阅读海轰的文章,倘若文中有错误的地方,欢迎您指出- 哈哈 自我介绍一下 昵称:海轰 标签:程序猿一只|C++选手|学生 简介:因C语言结识编程,随后转入计算机 ...

  5. ELK查询命令详解总结

    目录 ELK查询命令详解 倒排索引 倒排索引原理 分词器介绍及内置分词器 使用ElasticSearch API 实现CRUD 批量获取文档 使用Bulk API 实现批量操作 版本控制 什么是Map ...

  6. 面试题:Linux 中一个文件的 MAC 代表什么意思

    查看文件状态 stat ls 命令能够查看文件的类型.时间.属主.属组,大小以及最近的修改时间等信息,但是还有一些文件的扩展属性,是使用 ls 命令无法查看到的 stat 命令则用于显示文件的详细属性 ...

  7. 【Visual Studio调教小记录】C++指针靠前靠后??

    本文地址:https://www.cnblogs.com/oberon-zjt0806/p/14631149.html 甜咸之争 经常写C++的基本上避不开使用指针,而且C++中指针类型的写法大体上有 ...

  8. 记一次metasploitable2内网渗透之512,513,514端口攻击

    512,513,514端口都是R服务: TCP端口512,513和514为著名的rlogin提供服务.在系统中被错误配置从而允许远程访问者从任何地方访问(标准的,rhosts + +). 默认端口:5 ...

  9. Windows系统搭建ELK日志收集服务器

    一.ELK是什么?ELK是由Elasticsearch.Logstash.Kibana这3个软件的首字母缩写. Elasticsearch是一个分布式搜索分析引擎,稳定.可水平扩展.易于管理是它的主要 ...

  10. 痞子衡嵌入式:同一厂商不同系列Flash型号下Dummy Cycle设置方法可能有差异 (以IS25LP064为例)

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是同一厂商不同系列Flash型号下Dummy Cycle设置方法的差异. 上一篇文章 <在i.MXRT启动头FDCB里调整Flash ...