2019独角兽企业重金招聘Python工程师标准>>>

MVC 处理 HTTP 分发请求

HandlerMapping 配置与设计

在初始化完成时,在上下文环境中已定义的所有 HandlerMapping 都已经被加载了,这些加载的 handlerMapping 被放在一个 List 中并被排序,存储着 HTTP 请求对应的映射数据。这个 List 中的每一个元素都对应着一个具体 handlerMapping 的配置,一般每一个 handlerMapping 可以持有一系列从 URL 请求到 Controller 的映射,而 Spring MVC 提供了一系列的 HandlerMapping 实现。如下:

通过这些在 HandlerMapping 中定义的映射关系,即这些 URL 请求和控制器的对应关系,使 Spring MVC 应用可以根据 HTTP 请求确定一个对应的 Controller。具体来说,这些映射关系是通过接口类 HandlerMapping 来封装的,在 HandlerMapping 接口中定义了一个 getHandler 方法,通过这个方法,可以获得与 HTTP 请求对应的 HandlerExecutionChain,而这个 HandlerExecutionChain,封装了具体的 Controller 对象:

public interface HandlerMapping {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";// 调用 getHandler 实际上返回的是一个 HandlerExecutionChain ,这是典型的 Command 的模式的使用,这个 HandlerExecutionChain // 不但持有 handler 本身,还包括了处理这个 HTTP 请求相关的拦截器HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}

HandlerExecutionChain 看起来比较简洁,它持有一个 Interceptor 链和一个 handler 对象,这个 handler 对象实际上就是 HTTP 请求对应的 Controller,在持有这个 handler 对象的同时,还在 HandlerExecutionChain 中设置了一个拦截器链,通过这个拦截器链中的拦截器,可以为 handler 对象提供功能的增强。要完成这些工作,需要对拦截器链和 handler 都进行配置,这些配置都是在 HandlerExecutionChain 的初始化函数中完成的。为了维护这个拦截器链和 handler,handlerExecutionChain 还提供了一系列与拦截器链维护相关的一些操作,比如可以为拦截器链增加拦截器的 addInterceptor 方法等。

public class HandlerExecutionChain {private static final Log logger = LogFactory.getLog(HandlerExecutionChain.class);private final Object handler;private HandlerInterceptor[] interceptors;private List<HandlerInterceptor> interceptorList;private int interceptorIndex = -1;public HandlerExecutionChain(Object handler) {this(handler, (HandlerInterceptor[]) null);}public HandlerExecutionChain(Object handler, HandlerInterceptor... interceptors) {if (handler instanceof HandlerExecutionChain) {HandlerExecutionChain originalChain = (HandlerExecutionChain) handler;this.handler = originalChain.getHandler();this.interceptorList = new ArrayList<HandlerInterceptor>();CollectionUtils.mergeArrayIntoCollection(originalChain.getInterceptors(), this.interceptorList);CollectionUtils.mergeArrayIntoCollection(interceptors, this.interceptorList);}else {this.handler = handler;this.interceptors = interceptors;}}public Object getHandler() {return this.handler;}public void addInterceptor(HandlerInterceptor interceptor) {initInterceptorList().add(interceptor);}public void addInterceptors(HandlerInterceptor... interceptors) {if (!ObjectUtils.isEmpty(interceptors)) {initInterceptorList().addAll(Arrays.asList(interceptors));}}private List<HandlerInterceptor> initInterceptorList() {if (this.interceptorList == null) {this.interceptorList = new ArrayList<HandlerInterceptor>();if (this.interceptors != null) {// An interceptor array specified through the constructorthis.interceptorList.addAll(Arrays.asList(this.interceptors));}}this.interceptors = null;return this.interceptorList;}public HandlerInterceptor[] getInterceptors() {if (this.interceptors == null && this.interceptorList != null) {this.interceptors = this.interceptorList.toArray(new HandlerInterceptor[this.interceptorList.size()]);}return this.interceptors;}boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {HandlerInterceptor[] interceptors = getInterceptors();if (!ObjectUtils.isEmpty(interceptors)) {for (int i = 0; i < interceptors.length; i++) {HandlerInterceptor interceptor = interceptors[i];if (!interceptor.preHandle(request, response, this.handler)) {triggerAfterCompletion(request, response, null);return false;}this.interceptorIndex = i;}}return true;}void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv) throws Exception {HandlerInterceptor[] interceptors = getInterceptors();if (!ObjectUtils.isEmpty(interceptors)) {for (int i = interceptors.length - 1; i >= 0; i--) {HandlerInterceptor interceptor = interceptors[i];interceptor.postHandle(request, response, this.handler, mv);}}}void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex)throws Exception {HandlerInterceptor[] interceptors = getInterceptors();if (!ObjectUtils.isEmpty(interceptors)) {for (int i = this.interceptorIndex; i >= 0; i--) {HandlerInterceptor interceptor = interceptors[i];try {interceptor.afterCompletion(request, response, this.handler, ex);}catch (Throwable ex2) {logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);}}}}void applyAfterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response) {HandlerInterceptor[] interceptors = getInterceptors();if (!ObjectUtils.isEmpty(interceptors)) {for (int i = interceptors.length - 1; i >= 0; i--) {if (interceptors[i] instanceof AsyncHandlerInterceptor) {try {AsyncHandlerInterceptor asyncInterceptor = (AsyncHandlerInterceptor) interceptors[i];asyncInterceptor.afterConcurrentHandlingStarted(request, response, this.handler);}catch (Throwable ex) {logger.error("Interceptor [" + interceptors[i] + "] failed in afterConcurrentHandlingStarted", ex);}}}}}@Overridepublic String toString() {if (this.handler == null) {return "HandlerExecutionChain with no handler";}StringBuilder sb = new StringBuilder();sb.append("HandlerExecutionChain with handler [").append(this.handler).append("]");if (!CollectionUtils.isEmpty(this.interceptorList)) {sb.append(" and ").append(this.interceptorList.size()).append(" interceptor");if (this.interceptorList.size() > 1) {sb.append("s");}}return sb.toString();}
}

下面我们以 SimpleUrlHandlerMapping 说明在实际中是如何应用的,SimpleUrlHandlerMapping 定义了一个 Map 变量(自己定义一个 Map 主要有两个作用,第一是方便配置,第二是可以在注册前做一些预处理,如确保所以 url 都以”/“开头),将所有的 url 和 Handler 的对应关系放在里面,最后注册到父类的 Map 中。

SimpleUrlHandlerMapping.java

public void initApplicationContext() throws BeansException {super.initApplicationContext();registerHandlers(this.urlMap);
}protected void registerHandlers(Map<String, Object> urlMap) throws BeansException {if (urlMap.isEmpty()) {logger.warn("Neither 'urlMap' nor 'mappings' set on SimpleUrlHandlerMapping");}else {for (Map.Entry<String, Object> entry : urlMap.entrySet()) {String url = entry.getKey();Object handler = entry.getValue();// Prepend with slash if not already present.if (!url.startsWith("/")) {url = "/" + url;}// Remove whitespace from handler bean name.if (handler instanceof String) {handler = ((String) handler).trim();}registerHandler(url, handler);}}
}

AbstractUrlHandlerMapping 对 handler 的注册。

AbstractUrlHandlerMapping.java

protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {Assert.notNull(urlPath, "URL path must not be null");Assert.notNull(handler, "Handler object must not be null");Object resolvedHandler = handler;// Eagerly resolve handler if referencing singleton via name.if (!this.lazyInitHandlers && handler instanceof String) {String handlerName = (String) handler;if (getApplicationContext().isSingleton(handlerName)) {resolvedHandler = getApplicationContext().getBean(handlerName);}}Object mappedHandler = this.handlerMap.get(urlPath);if (mappedHandler != null) {if (mappedHandler != resolvedHandler) {throw new IllegalStateException("Cannot map " + getHandlerDescription(handler) + " to URL path [" + urlPath +"]: There is already " + getHandlerDescription(mappedHandler) + " mapped.");}}else {if (urlPath.equals("/")) {if (logger.isInfoEnabled()) {logger.info("Root mapping to " + getHandlerDescription(handler));}setRootHandler(resolvedHandler);}else if (urlPath.equals("/*")) {if (logger.isInfoEnabled()) {logger.info("Default mapping to " + getHandlerDescription(handler));}setDefaultHandler(resolvedHandler);}else {this.handlerMap.put(urlPath, resolvedHandler);if (logger.isInfoEnabled()) {logger.info("Mapped URL path [" + urlPath + "] onto " + getHandlerDescription(handler));}}}
}

HandlerMapping 完成请求和映射处理

在各种准备工作完成后,就是使用 HandlerMapping 来完成请求的映射处理了,而具体执行过程是在 AbstactHandlerMapping 中的 getHandler 来完成的。

AbstactHandlerMapping.java

@Override
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {Object handler = getHandlerInternal(request);// 使用默认的 Handler,也就是"/"对应的 handlerif (handler == null) {handler = getDefaultHandler();}// 这里通过名称取出对应的 Handler Beanif (handler == null) {return null;}// Bean name or resolved handler?if (handler instanceof String) {String handlerName = (String) handler;handler = getApplicationContext().getBean(handlerName);}// 这里把 Handler 封装到 HandlerExecutionChain 中并加上拦截器HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);if (CorsUtils.isCorsRequest(request)) {CorsConfiguration globalConfig = this.corsConfigSource.getCorsConfiguration(request);CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);executionChain = getCorsHandlerExecutionChain(request, executionChain, config);}return executionChain;
}protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);for (HandlerInterceptor interceptor : this.adaptedInterceptors) {if (interceptor instanceof MappedInterceptor) {MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {chain.addInterceptor(mappedInterceptor.getInterceptor());}}else {chain.addInterceptor(interceptor);}}return chain;
}

在取得 handler 的具体过程在 getHandlerInternal 方法中实现。这个方法接受 HTTP 请求作为参数,它的实现在 AbstractUrlHandlerMapping 中,这个实现过程包括从 HTTP 请求中得到 URL ,并根据 URL 到 urlMapping 中获取 handler。

AbstractUrlHandlerMapping .java

protected Object getHandlerInternal(HttpServletRequest request) throws Exception {// 从request 中得到请求的 URL 路径String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);// 将得到的 URL 路径与 Handler 进行匹配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 = getApplicationContext().getBean(handlerName);}validateHandler(rawHandler, request);handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);}}if (handler != null && logger.isDebugEnabled()) {logger.debug("Mapping [" + lookupPath + "] to " + handler);}else if (handler == null && logger.isTraceEnabled()) {logger.trace("No handler mapping found for [" + lookupPath + "]");}return handler;
}// lookupHandler 根据 URL 路径启动在 HandlerMap 中对 handler 的检索,并最终返回 handler 对象
protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {// Direct match?Object handler = this.handlerMap.get(urlPath);if (handler != null) {// Bean name or resolved handler?if (handler instanceof String) {String handlerName = (String) handler;handler = getApplicationContext().getBean(handlerName);}validateHandler(handler, request);return buildPathExposingHandler(handler, urlPath, urlPath, null);}// Pattern match?List<String> matchingPatterns = new ArrayList<String>();for (String registeredPattern : this.handlerMap.keySet()) {if (getPathMatcher().match(registeredPattern, urlPath)) {matchingPatterns.add(registeredPattern);}else if (useTrailingSlashMatch()) {if (!registeredPattern.endsWith("/") && getPathMatcher().match(registeredPattern + "/", urlPath)) {matchingPatterns.add(registeredPattern +"/");}}}String bestPatternMatch = null;Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath);if (!matchingPatterns.isEmpty()) {Collections.sort(matchingPatterns, patternComparator);if (logger.isDebugEnabled()) {logger.debug("Matching patterns for request [" + urlPath + "] are " + matchingPatterns);}bestPatternMatch = matchingPatterns.get(0);}if (bestPatternMatch != null) {handler = this.handlerMap.get(bestPatternMatch);if (handler == null) {Assert.isTrue(bestPatternMatch.endsWith("/"));handler = this.handlerMap.get(bestPatternMatch.substring(0, bestPatternMatch.length() - 1));}// Bean name or resolved handler?if (handler instanceof String) {String handlerName = (String) handler;handler = getApplicationContext().getBean(handlerName);}validateHandler(handler, request);String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestPatternMatch, urlPath);// There might be multiple 'best patterns', let's make sure we have the correct URI template variables// for all of themMap<String, String> uriTemplateVariables = new LinkedHashMap<String, String>();for (String matchingPattern : matchingPatterns) {if (patternComparator.compare(bestPatternMatch, matchingPattern) == 0) {Map<String, String> vars = getPathMatcher().extractUriTemplateVariables(matchingPattern, urlPath);Map<String, String> decodedVars = getUrlPathHelper().decodePathVariables(request, vars);uriTemplateVariables.putAll(decodedVars);}}if (logger.isDebugEnabled()) {logger.debug("URI Template variables for request [" + urlPath + "] are " + uriTemplateVariables);}return buildPathExposingHandler(handler, bestPatternMatch, pathWithinMapping, uriTemplateVariables);}// No handler found...return null;
}

至此,HTTP 请求的解析和匹配 Handler 过程就全部完成了,剩下的就是 HTTP 请求的分发处理了。

Spring MVC 对 HTTP 请求的分发处理

让我们重回到 DispatcherServlet,毫无疑问,它是 Spring MVC 中非常重要的一个类,他不仅仅建立了自己的持有 Ioc 容器,还肩负请求分布处理的任务。而 HTTP 的请求发布处理是在 doService 方法中完成的。

DispatcherServlet.java

protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {if (logger.isDebugEnabled()) {String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed +" processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");}// Keep a snapshot of the request attributes in case of an include,// to be able to restore the original attributes after the include.Map<String, Object> attributesSnapshot = null;if (WebUtils.isIncludeRequest(request)) {attributesSnapshot = new HashMap<String, Object>();Enumeration<?> attrNames = request.getAttributeNames();while (attrNames.hasMoreElements()) {String attrName = (String) attrNames.nextElement();if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) {attributesSnapshot.put(attrName, request.getAttribute(attrName));}}}// 对 HTTP 请求参数进行处理request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);if (inputFlashMap != null) {request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));}request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);try {// 这里是 doDispatch 的分发请求的人口doDispatch(request, response);}finally {if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {// Restore the original attribute snapshot, in case of an include.if (attributesSnapshot != null) {restoreAttributesAfterInclude(request, attributesSnapshot);}}}
}

对于请求的实际处理是由 doDispatch() 来完成的,这个方法很长,但过程简单明了。这个 doDispatch 方法是 DispatcherServlet 完成 Dispatcher 的主要方法,包括准备 ModelAndView,调用 getHandler 来响应 HTTP 请求,然后通过执行 Handler 的处理来得到返回的 ModelAndView 结果,最后把这个 ModelAndView 对象交给相应的视图对象去呈现。这里是 MVC 模式的核心地区:

DispatcherServlet.java

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {HttpServletRequest processedRequest = request;HandlerExecutionChain mappedHandler = null;boolean multipartRequestParsed = false;WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);// 这里为视图准备好一个 ModelAndView,这个 ModelAndView 持有 handler 处理请求的结果try {ModelAndView mv = null;Exception dispatchException = null;try {processedRequest = checkMultipart(request);multipartRequestParsed = (processedRequest != request);// 根据请求得到对应的 handler,handler 的注册以及 getHandler 的实现mappedHandler = getHandler(processedRequest);if (mappedHandler == null || mappedHandler.getHandler() == null) {noHandlerFound(processedRequest, response);return;}// 调用 Handler 的地方HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());// Process last-modified header, if supported by the handler.String method = request.getMethod();boolean isGet = "GET".equals(method);// 判断是否需要进行视图的翻译和转换if (isGet || "HEAD".equals(method)) {long lastModified = ha.getLastModified(request, mappedHandler.getHandler());if (logger.isDebugEnabled()) {logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);}if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {return;}}if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}// 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;}processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);}catch (Exception ex) {triggerAfterCompletion(processedRequest, response, mappedHandler, ex);}catch (Error err) {triggerAfterCompletionWithError(processedRequest, response, mappedHandler, 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);}}}
}

这里是与 MVC 框架最紧密相关的代码,得到 HTTP 请求对应的 HandlerExecutionChain ,执行 handler 并把模型数据展现到视图中去。

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {for (HandlerMapping hm : this.handlerMappings) {if (logger.isTraceEnabled()) {logger.trace("Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");}HandlerExecutionChain handler = hm.getHandler(request);if (handler != null) {return handler;}}return null;
}

其实,这个方法执行结束,MVC 框架中的 Controller 和 定义的一个拦截器链已经包含在其中了。然后,需要执行的就是返回适配的结果了。

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {// 对持有的所有 adapter 进行匹配for (HandlerAdapter ha : this.handlerAdapters) {if (logger.isTraceEnabled()) {logger.trace("Testing handler adapter [" + ha + "]");}if (ha.supports(handler)) {return ha;}}throw new ServletException("No adapter for handler [" + handler +"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}

这里的 handler 是不是 Controller 接口的实现,可以通过 HandlerAdapter 来了解这个适配的过程。而具体的适配过程,我们以 SimpleControllerHandlerAdapter 的实现来说明。

public class SimpleControllerHandlerAdapter implements HandlerAdapter {// 判断将要调用的 handler 是不是 Controller.@Overridepublic boolean supports(Object handler) {return (handler instanceof Controller);}@Overridepublic ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {return ((Controller) handler).handleRequest(request, response);}@Overridepublic long getLastModified(HttpServletRequest request, Object handler) {if (handler instanceof LastModified) {return ((LastModified) handler).getLastModified(request);}return -1L;}}

        得到 handler 对象,然后调用 handler 对象中的 HTTP 进行动作的响应。而具体的业务逻辑则被封装在 handler 中,由这些逻辑对 HTTP 请求进行相应的处理,从而生成所需要的数据,并把这些数据封装到 ModelAndView 对象中。最后,通过 handler 的 handlerRequest 方法触发完成,然后交给 MVC 的 View 部分处理。至此,Spring MVC 的主要逻辑就完成了。前面已经,分析了,MVC 的 C 和 M 部分。下面就是 View 部分的分析了。

——水门(2016年4月写于杭州)

转载于:https://my.oschina.net/kaywu123/blog/651841

Spring 源码分析(四) ——MVC(六)M 与 C 的实现相关推荐

  1. Spring 源码分析(四) ——MVC(二)概述

    随时随地技术实战干货,获取项目源码.学习资料,请关注源代码社区公众号(ydmsq666) from:Spring 源码分析(四) --MVC(二)概述 - 水门-kay的个人页面 - OSCHINA ...

  2. Spring 源码分析(四) ——MVC(一)Web 基础

    2019独角兽企业重金招聘Python工程师标准>>> 软件的分类 软件(Software)是一系列按照特定顺序组织的计算机数据和指示,是计算机中的非有形部分.而计算机中的有形部分称 ...

  3. 【转】ABP源码分析四十六:ABP ZERO中的Ldap模块

    通过AD作为用户认证的数据源.整个管理用户认证逻辑就在LdapAuthenticationSource类中实现. LdapSettingProvider:定义LDAP的setting和提供Defaut ...

  4. Spring源码分析(三)

    Spring源码分析 第三章 手写Ioc和Aop 文章目录 Spring源码分析 前言 一.模拟业务场景 (一) 功能介绍 (二) 关键功能代码 (三) 问题分析 二.使用ioc和aop重构 (一) ...

  5. Spring源码分析——汇总全集

    文章目录 一.背景 二.源码分析目录 三.源码番外篇(补充) 更新时间 更新内容 备注 2022-04-01 Spring源码分析目录和计划 2022-04-10 Spring源码分析一:容器篇-re ...

  6. spring源码分析第四天------springmvc核心原理及源码分析

    spring源码分析第四天------springmvc核心原理及源码分析 1.基础知识普及 2. SpringMVC请求流程 3.SpringMVC代码流程 4.springMVC源码分析 4.1 ...

  7. Spring源码分析之Bean的创建过程详解

    前文传送门: Spring源码分析之预启动流程 Spring源码分析之BeanFactory体系结构 Spring源码分析之BeanFactoryPostProcessor调用过程详解 本文内容: 在 ...

  8. Spring 源码分析 (一)——迈向 Spring 之路

    一切都是从 Bean 开始的 在 1996 年,Java 还只是一个新兴的.初出茅庐的编程语言.人们之所以关注她仅仅是因为,可以使用 Java 的 Applet 来开发 Web 应用.但这些开发者很快 ...

  9. Spring 源码分析衍生篇十 :Last-Modified 缓存机制

    文章目录 一.前言 二.Last-Modify 三.实现方案 1. 实现 org.springframework.web.servlet.mvc.LastModified接口 1.1. 简单演示 1. ...

最新文章

  1. java 矩形重叠问题_两个矩形重叠的问题
  2. 如何通过 Impex 在 SAP Commerce Cloud 创建一个新的 Component
  3. python tkinter教程 博客园_python tkinter教程-事件绑定
  4. node.js 和 HTML5-Canvas 结合实现截图上传交互
  5. 运用begin和end截取值
  6. 浅谈UWB室内定位(三)
  7. 高端存储器研发再获突破 集成电路国产化进程加快
  8. bde连接mysql设置,delphi通过BDE方式连接数据库以及程序Demo
  9. dism++封装系统使用教程_Dism++系统精简利器 10.1.4.7
  10. 浏览器如何禁用或启用Cookie
  11. ET6.0服务器框架学习笔记(一、启动配置)
  12. 记录下自己拙计的算法之旅 LeetCode Rotate Array
  13. 大数据之Hive:Hive中next_day函数
  14. U盘图标不显示(转)
  15. 解决 VS 无法打开包括文件: “XXX.h”: No such file or directory问题
  16. Altium Designer初学教程(一)
  17. Mac小技巧:强制退出程序的六种方法
  18. 售让链克业务后的迅雷 此身终于分明
  19. CSR8670蓝牙模块,立体声高品质音频方案
  20. UG NX 8.5有限元分析入门与实例精讲(PPT、视频、模型)

热门文章

  1. Java高并发BlockingQueue重要的实现类二
  2. js日期操作,某天的N天后,一个月后的日期
  3. Linux -chattr -隐藏权限(附加权限)
  4. Go设计模式之Factory
  5. 源码解析——消息机制
  6. nginx 反向代理 转发请求时,有时好有时没反应,产生原因及解决
  7. linux下使用source /etc/profile保存配置后,新的环境变量只能在一个终端里面有效...
  8. 关于新版chrome设置编码格式(55以上)
  9. java常用序列化与反序列化方法
  10. 学习vue 20天,我写了点东西