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

网站优化的代码外贸推广公司排名

网站优化的代码,外贸推广公司排名,沐浴露营销软文,下载安装微信写在前面 哥#xff0c;来帮我看看#xff0c;这个请求怎么404了#xff0c;明明接口路径是对的啊#xff01;一个下午#xff0c;组里的小哥突然让我帮忙看这个问题#xff0c;我不禁一惊#xff0c;啥#xff0c;这年头了还有人搞不定404#xff0c;如有还有#…写在前面 哥来帮我看看这个请求怎么404了明明接口路径是对的啊一个下午组里的小哥突然让我帮忙看这个问题我不禁一惊啥这年头了还有人搞不定404如有还有那一定是没看完这篇文章 一、为何要写这篇文章 作为一名crud工程师咱们的工作真的就只剩增删改查了吗在笔者所遇到各类从事软件开发的人群中工作1-2年甚至3-5年的在遇到404这类的http异常code时都显得束手无策经验稍微丰富的点“老”手可能凭经验能看出问题出在哪里但是又有多少人知道为什么会出现404 code往深了说又有多少人知道一个http请求是如何找到controller中的方法并执行呢更进一步在你了解到spring mvc 的处理机制前如果让你来设计这套流程你会怎么做 二、举个例子 下面是一个最简单的http接口例子 接口路径为 /api/common/getNumber RequestMapping(/api/common) Controller public class CommonController {RequestMapping(/getNumber)ResponseBodypublic Object getNumberMethod(RequestParam(range) Integer range) {return ThreadLocalRandom.current().nextInt(range);} } 过滤器 public class LogFilter implements Filter {Overridepublic void init(FilterConfig filterConfig) {}Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {HttpServletRequest request (HttpServletRequest) servletRequest;log.info(经过logFilter {}, request);filterChain.doFilter(servletRequest, servletResponse);}Overridepublic void destroy() {} } 拦截器 public class LogInterceptor extends HandlerInterceptorAdapter {Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {log.info(经过拦截器 {}, request);return true;} } 执行结果 2023-10-03 19:22:12.373 INFO 54072 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet dispatcherServlet 2023-10-03 19:22:12.373 INFO 54072 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet dispatcherServlet 2023-10-03 19:22:15.646 INFO 54072 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 3273 ms 2023-10-03 19:22:19.759 INFO 54072 --- [nio-8080-exec-1] com.example.demo.filter.LogFilter : 经过logFilter org.apache.catalina.connector.RequestFacade67d0b80a 2023-10-03 19:22:26.177 INFO 54072 --- [nio-8080-exec-1] c.e.demo.intercpetor.LogInterceptor : 经过拦截器 org.apache.catalina.connector.RequestFacade67d0b80a 三、执行过程 1、运行环境 jdk 1.8 spring-boot-starter-parent 2.1.9.RELEASE spring-webmvc 5.1.0 2、源码解析 在进行源码解析时我们先过一眼整个请求处理的过程UML图 0ApplicationFilterChain # internalDoFilter(ServletRequest request, ServletResponse response) 该方法是tomcat包中的方法用来执行filter在filter执行完成后再执行servlet.service方法而servlet.service方法也是业务的入口方法。servlet对象即为DispatchServlet其service对应的也就是其父类HttpServlet的service方法。 private void internalDoFilter(ServletRequest request,ServletResponse response)throws IOException, ServletException {// n 表示filter数的总和pos表示当前位置// Call the next filter if there is oneif (pos n) { // 如果filter没有执行完成则走下面的逻辑继续执行// 获取pos位置对应的filterConfig,同时将pos1ApplicationFilterConfig filterConfig filters[pos];try {Filter filter filterConfig.getFilter();if (request.isAsyncSupported() false.equalsIgnoreCase(filterConfig.getFilterDef().getAsyncSupported())) {request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, Boolean.FALSE);}if( Globals.IS_SECURITY_ENABLED ) {final ServletRequest req request;final ServletResponse res response;Principal principal ((HttpServletRequest) req).getUserPrincipal();Object[] args new Object[]{req, res, this};SecurityUtil.doAsPrivilege (doFilter, filter, classType, args, principal);} else {// 执行具体的filter逻辑如本文例子中的LogFilterfilter.doFilter(request, response, this);}} catch (IOException | ServletException | RuntimeException e) {throw e;} catch (Throwable e) {e ExceptionUtils.unwrapInvocationTargetException(e);ExceptionUtils.handleThrowable(e);throw new ServletException(sm.getString(filterChain.filter), e);}return;}// We fell off the end of the chain -- call the servlet instance// 如果执行完最后一个filtertry {if (ApplicationDispatcher.WRAP_SAME_OBJECT) {lastServicedRequest.set(request);lastServicedResponse.set(response);}if (request.isAsyncSupported() !servletSupportsAsync) {request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR,Boolean.FALSE);}// Use potentially wrapped request from this pointif ((request instanceof HttpServletRequest) (response instanceof HttpServletResponse) Globals.IS_SECURITY_ENABLED ) {final ServletRequest req request;final ServletResponse res response;Principal principal ((HttpServletRequest) req).getUserPrincipal();Object[] args new Object[]{req, res};SecurityUtil.doAsPrivilege(service,servlet,classTypeUsedInService,args,principal);} else {// 执行HttpServlet的service方法servlet.service(request, response);}} catch (IOException | ServletException | RuntimeException e) {throw e;} catch (Throwable e) {e ExceptionUtils.unwrapInvocationTargetException(e);ExceptionUtils.handleThrowable(e);throw new ServletException(sm.getString(filterChain.servlet), e);} finally {if (ApplicationDispatcher.WRAP_SAME_OBJECT) {lastServicedRequest.set(null);lastServicedResponse.set(null);}}} 1FrameworkServlet # service(HttpServletRequest request, HttpServletResponse response) 由于HttpServlet的service方法只是做了入参的转换即将ServletRequest转成HttpServletRequestServletResponse转成HttpServletResponse参数转换完后随即调用了子类 FrameworkServlet 的service(HttpServletRequest request, HttpServletResponse response) 方法而FrameworkServlet 的service方法功能很简单就是为了适配httpMethod 中的 PATCH模式非PATCH模式直接走父类HttpServlet的service(HttpServletRequest request, HttpServletResponse response)方法【DispatcherServlet的继承关系依赖图如下】。 /*** Override the parent class implementation in order to intercept PATCH requests.* 主要是为了拦截 httpMethod 中的 PATCH** patch是2010后成为的正式http方法详见RFC5789* 它是对put的补充在没有patch之前我们都是用put进行更新操作* 这时候我们的接口中通常会有一个逻辑规则如如果对象的的一个字符属性为NULL* 那么就是不更新该属性字段值如果对象的字符属性是“”那么就更新该属性字段的值* 通过这种方式来避免全部覆盖的操作。现在有了patch就解决了这种判断在put接口中不管属性是不是null* 都进行更新在patch接口中就对非null的进行更新**/Overrideprotected void service(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {HttpMethod httpMethod HttpMethod.resolve(request.getMethod());// 如果是 HttpMethod.PATCH 或者 找不到httpMethodif (httpMethod HttpMethod.PATCH || httpMethod null) {processRequest(request, response);}else {// 其他情况则调用父类也就是HttpServlet的service方法// 在这里由于我们的请求是get类型的所以会走到此分支super.service(request, response);}} 2HttpServlet # service(HttpServletRequest req, HttpServletResponse resp) 进入HttpServlet的service(HttpServletRequest req, HttpServletResponse resp)方法后该方法主要做了method类型的区分调用即get post put等对应的doGetdoPostdoPut 由子类实现。 /*** Receives standard HTTP requests from the public* codeservice/code method and dispatches* them to the codedo/codeiMethod/i methods defined in* this class. This method is an HTTP-specific version of the* {link javax.servlet.Servlet#service} method. Theres no* need to override this method.** param req the {link HttpServletRequest} object that* contains the request the client made of* the servlet** param resp the {link HttpServletResponse} object that* contains the response the servlet returns* to the client** exception IOException if an input or output error occurs* while the servlet is handling the* HTTP request** exception ServletException if the HTTP request* cannot be handled** see javax.servlet.Servlet#service*/protected void service(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {String method req.getMethod();if (method.equals(METHOD_GET)) {// 默认返回 -1long lastModified getLastModified(req);if (lastModified -1) {// servlet doesnt support if-modified-since, no reason// to go through further expensive logicdoGet(req, resp);} else {long ifModifiedSince;try {ifModifiedSince req.getDateHeader(HEADER_IFMODSINCE);} catch (IllegalArgumentException iae) {// Invalid date header - proceed as if none was setifModifiedSince -1;}if (ifModifiedSince (lastModified / 1000 * 1000)) {// If the servlet mod time is later, call doGet()// Round down to the nearest second for a proper compare// A ifModifiedSince of -1 will always be lessmaybeSetLastModified(resp, lastModified);doGet(req, resp);} else {resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);}}} else if (method.equals(METHOD_HEAD)) {long lastModified getLastModified(req);maybeSetLastModified(resp, lastModified);doHead(req, resp);} else if (method.equals(METHOD_POST)) {doPost(req, resp);} else if (method.equals(METHOD_PUT)) {doPut(req, resp);} else if (method.equals(METHOD_DELETE)) {doDelete(req, resp);} else if (method.equals(METHOD_OPTIONS)) {doOptions(req,resp);} else if (method.equals(METHOD_TRACE)) {doTrace(req,resp);} else {//// Note that this means NO servlet supports whatever// method was requested, anywhere on this server.//String errMsg lStrings.getString(http.method_not_implemented);Object[] errArgs new Object[1];errArgs[0] method;errMsg MessageFormat.format(errMsg, errArgs);resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);}} 3DispatcherServlet # doDispatch(HttpServletRequest request, HttpServletResponse response) HttpServlet的service(HttpServletRequest req, HttpServletResponse resp)方法经过一连串的包装调用后就会进入最重要的DispatcherServlet 的 doDispatch(HttpServletRequest request, HttpServletResponse response)方法doDispatch顾名思义就是将请求进行分发包括获取HandlerExecutionChain执行拦截器获取执行器适配器handler调用视图渲染等工作。 /*** Process the actual dispatching to the handler.* pThe handler will be obtained by applying the servlets HandlerMappings in order.* The HandlerAdapter will be obtained by querying the servlets installed HandlerAdapters* to find the first that supports the handler class.* pAll HTTP methods are handled by this method. Its up to HandlerAdapters or handlers* themselves to decide which methods are acceptable.* param request current HTTP request* param response current HTTP response* throws Exception in case of any kind of processing failure*/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.// 该方法得到一个 HandlerExecutionChain 处理器执行链实际上它包含了一个真正的处理handler// 和 若干个拦截器mappedHandler getHandler(processedRequest);if (mappedHandler null) {noHandlerFound(processedRequest, response);return;}// Determine handler adapter for the current request.// 获取执行器适配器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 (new ServletWebRequest(request, response).checkNotModified(lastModified) isGet) {return;}}// 执行拦截器的preHandle方法如果拦截了则直接返回if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}// Actually invoke the handler.// 真正调用handlermv ha.handle(processedRequest, response, mappedHandler.getHandler());if (asyncManager.isConcurrentHandlingStarted()) {return;}applyDefaultViewName(processedRequest, mv);// 执行拦截器的 postHandle 方法mappedHandler.applyPostHandle(processedRequest, response, mv);}catch (Exception ex) {dispatchException ex;}catch (Throwable err) {// As of 4.3, were processing Errors thrown from handler methods as well,// making them available for ExceptionHandler methods and other scenarios.dispatchException new NestedServletException(Handler dispatch failed, err);}// 处理视图的方法将逻辑视图转为物理视图的过程同时执行拦截器的afterCompletion方法processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);}catch (Exception ex) {// 执行拦截器的afterCompletion方法triggerAfterCompletion(processedRequest, response, mappedHandler, ex);}catch (Throwable err) {triggerAfterCompletion(processedRequest, response, mappedHandler,new NestedServletException(Handler processing failed, 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);}}}} a. DispatcherServlet # getHandler(HttpServletRequest request) 该方法得到一个 HandlerExecutionChain 处理器执行链实际上它包含了一个真正的处理handler和 若干个拦截器 /*** Return the HandlerExecutionChain for this request.* pTries all handler mappings in order.* param request current HTTP request* return the HandlerExecutionChain, or {code null} if no handler could be found*/Nullableprotected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {if (this.handlerMappings ! null) {for (HandlerMapping mapping : this.handlerMappings) {HandlerExecutionChain handler mapping.getHandler(request);if (handler ! null) {return handler;}}}return null;} 从上图执行过程中可以看出HandlerExecutionChain的获取主要依赖于HandlerMapping 那么何为HandlerMappingHandlerMapping 称为处理器映射器 从HandlerMapping的继承关系图中可以看出HandlerMapping可以大致分为 AbstractHandlerMethodMapping 和 AbstractUrlHandlerMapping 两大类其中AbstractHandlerMethodMapping 映射器主要处理用 Controller 和 RequestMapping 这样注解来描述视图控制器的逻辑也是我们日常开发中用的最多的场景而AbstractUrlHandlerMapping用的比较少比如lt;mvc:view-controller pathquot;quot; view-namequot;quot;/gt; 标签配置资源不经过视图控制器直接跳转就用到了 SimpleUrlHandlerMapping 这种映射器。 当执行 HandlerExecutionChain handler mapping.getHandler(request); 时会跳到 AbstractHandlerMapping类执行getHandler方法AbstractHandlerMapping 是个抽象类提供了模板方法主要的功能在代码块getHandlerInternal方法在本例中getHandlerInternal的功能主要就是根据request来获取HandlerMethodHandlerMethod对象存储于MappingRegistry的mappingLookupT, HandlerMethod映射表中该映射表在容器启动时探测类上是否有Controller或者RequestMapping注解修饰如有则生成RequestMappingInfo到HandlerMethod的映射关系。 /*** 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*/OverrideNullablepublic final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {// 重要 本例中返回 HandlerMethod 对象该对象里面包含了目标类的目标method信息以及目标类的beanObject 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(本例中对应的是HandlerMethod 对象)以及拦截器信息封装到 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;} b. DispatcherServlet # getHandlerAdapter(Object handler) 该方法是获取处理器适配器那么为什么要有处理器适配器直接执行handler不行吗原因就是处理器 handler 的类型是 Object 类型。Spring 中的handler实现多变比如用户的处理器可以实现 Controller 接口或者 HttpRequestHandler 接口也可以用 RequestMapping 注解将方法作为一个处理器等这就导致 Spring MVC 无法直接执行这个处理器。所以这里需要一个处理器适配器由它去执行处理。获取处理器适配的方法寥寥数语最主要的逻辑就是 adapter.supports(handler)根据语句猜测大概就是根据条件匹配对应的适配器。在我们弄清楚这个逻辑前先来看看第一条语句if (this.handlerAdapters ! null)那么这个this.handlerAdapters 的值从哪里来 /*** Return the HandlerAdapter for this handler object.* param handler the handler object to find an adapter for* throws ServletException if no HandlerAdapter can be found for the handler. This is a fatal error.*/protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {if (this.handlerAdapters ! null) {for (HandlerAdapter adapter : this.handlerAdapters) {if (adapter.supports(handler)) {return adapter;}}}throw new ServletException(No adapter for handler [ handler ]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler);} 根据下图的执行过程可以看出有三个满足条件的handlerAdapter。 那么这三个handlerAdapter是如何确定的呢如下 /*** Initialize the HandlerAdapters used by this class.* pIf no HandlerAdapter beans are defined in the BeanFactory for this namespace,* we default to SimpleControllerHandlerAdapter.*/private void initHandlerAdapters(ApplicationContext context) {this.handlerAdapters null;if (this.detectAllHandlerAdapters) {// Find all HandlerAdapters in the ApplicationContext, including ancestor contexts.MapString, HandlerAdapter matchingBeans BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);if (!matchingBeans.isEmpty()) {this.handlerAdapters new ArrayList(matchingBeans.values());// We keep HandlerAdapters in sorted order.AnnotationAwareOrderComparator.sort(this.handlerAdapters);}}else {try {HandlerAdapter ha context.getBean(HANDLER_ADAPTER_BEAN_NAME, HandlerAdapter.class);this.handlerAdapters Collections.singletonList(ha);}catch (NoSuchBeanDefinitionException ex) {// Ignore, well add a default HandlerAdapter later.}}// Ensure we have at least some HandlerAdapters, by registering// default HandlerAdapters if no other adapters are found.if (this.handlerAdapters null) {this.handlerAdapters getDefaultStrategies(context, HandlerAdapter.class);if (logger.isTraceEnabled()) {logger.trace(No HandlerAdapters declared for servlet getServletName() : using default strategies from DispatcherServlet.properties);}}} 上述代码的大体含义如下 如果“开启”探测功能则扫描已注册的 HandlerAdapter 的 Bean 们添加到 handlerAdapters 中默认 开启 这里会进行排序可以通过实现 Order 接口设置排序值 如果“关闭”探测功能则获得 Bean 名称为 handlerAdapter 对应的 Bean 将其添加至 handlerAdapters 如果未获得到则获得默认配置的 HandlerAdapter 类调用 getDefaultStrategies(ApplicationContext context, ClassT strategyInterface) 方法就是从 DispatcherServlet.properties 文件中读取 HandlerAdapter 的默认实现类如下 那么回到getHandlerAdapter方法中的adapter.supports(handler)语句依次通过boolean supports(Object handler)方法判断使用哪个adapter。HandlerAdapter即采用适配器模式, 用于统一不同handler的接口调用。在本文例子中最后采用的是RequestMappingHandlerAdapter其对应的supports方法是 /*** This implementation expects the handler to be an {link HandlerMethod}.* param handler the handler instance to check* return whether or not this adapter can adapt the given handler*/Overridepublic final boolean supports(Object handler) {return (handler instanceof HandlerMethod supportsInternal((HandlerMethod) handler));} /*** Always return {code true} since any method argument and return value* type will be processed in some way. A method argument not recognized* by any HandlerMethodArgumentResolver is interpreted as a request parameter* if it is a simple type, or as a model attribute otherwise. A return value* not recognized by any HandlerMethodReturnValueHandler will be interpreted* as a model attribute.*/Overrideprotected boolean supportsInternal(HandlerMethod handlerMethod) {return true;} c. AbstractHandlerMethodAdapter # handle(HttpServletRequest request, HttpServletResponse response, Object handler) 该方法就是最终要执行业务方法也就是Controller类中的某个方法的入口。 /*** This implementation expects the handler to be an {link HandlerMethod}.*/OverrideNullablepublic final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {return handleInternal(request, response, (HandlerMethod) handler);} handleInternal 方法依赖于子类的实现 Overrideprotected ModelAndView handleInternal(HttpServletRequest request,HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {ModelAndView mav;checkRequest(request);// Execute invokeHandlerMethod in synchronized block if required.if (this.synchronizeOnSession) {HttpSession session request.getSession(false);if (session ! null) {Object mutex WebUtils.getSessionMutex(session);synchronized (mutex) {mav invokeHandlerMethod(request, response, handlerMethod);}}else {// No HttpSession available - no mutex necessarymav invokeHandlerMethod(request, response, handlerMethod);}}else {// No synchronization on session demanded at all...mav invokeHandlerMethod(request, response, handlerMethod);}if (!response.containsHeader(HEADER_CACHE_CONTROL)) {if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);}else {prepareResponse(response);}}return mav;} 在以上方法中我们只需要关注invokeHandlerMethod(request, response, handlerMethod) 即可接着看 /*** Invoke the {link RequestMapping} handler method preparing a {link ModelAndView}* if view resolution is required.* since 4.2* see #createInvocableHandlerMethod(HandlerMethod)*/Nullableprotected 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);.... 省去若干代码// 只需关注这行即可invocableMethod.invokeAndHandle(webRequest, mavContainer);if (asyncManager.isConcurrentHandlingStarted()) {return null;}return getModelAndView(mavContainer, modelFactory, webRequest);}finally {webRequest.requestCompleted();}} /*** Invoke the method and handle the return value through one of the* configured {link HandlerMethodReturnValueHandler HandlerMethodReturnValueHandlers}.* param webRequest the current request* param mavContainer the ModelAndViewContainer for this request* param providedArgs given arguments matched by type (not resolved)*/public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {// 实际调用Object returnValue invokeForRequest(webRequest, mavContainer, providedArgs);// 处理结果状态值setResponseStatus(webRequest);if (returnValue null) {if (isRequestNotModified(webRequest) || getResponseStatus() ! null || mavContainer.isRequestHandled()) {disableContentCachingIfNecessary(webRequest);mavContainer.setRequestHandled(true);return;}}else if (StringUtils.hasText(getResponseStatusReason())) {mavContainer.setRequestHandled(true);return;}mavContainer.setRequestHandled(false);Assert.state(this.returnValueHandlers ! null, No return value handlers);try {// 处理返回值this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);}catch (Exception ex) {if (logger.isTraceEnabled()) {logger.trace(formatErrorForReturnValue(returnValue), ex);}throw ex;}} /*** Invoke the method after resolving its argument values in the context of the given request.* pArgument values are commonly resolved through* {link HandlerMethodArgumentResolver HandlerMethodArgumentResolvers}.* The {code providedArgs} parameter however may supply argument values to be used directly,* i.e. without argument resolution. Examples of provided argument values include a* {link WebDataBinder}, a {link SessionStatus}, or a thrown exception instance.* Provided argument values are checked before argument resolvers.* pDelegates to {link #getMethodArgumentValues} and calls {link #doInvoke} with the* resolved arguments.* param request the current request* param mavContainer the ModelAndViewContainer for this request* param providedArgs given arguments matched by type, not resolved* return the raw value returned by the invoked method* throws Exception raised if no suitable argument resolver can be found,* or if the method raised an exception* see #getMethodArgumentValues* see #doInvoke*/Nullablepublic Object invokeForRequest(NativeWebRequest request, Nullable ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {// 解析参数值Object[] args getMethodArgumentValues(request, mavContainer, providedArgs);if (logger.isTraceEnabled()) {logger.trace(Arguments: Arrays.toString(args));}// 执行调用 return doInvoke(args);} /*** Invoke the handler method with the given argument values.*/Nullableprotected Object doInvoke(Object... args) throws Exception {// 改变方法的可见性这就是为什么即使controller中的方法是private的也能正常访问ReflectionUtils.makeAccessible(getBridgedMethod());try {// 这就是精髓所在熟悉的配方熟悉的味道这不就是反射调用吗return getBridgedMethod().invoke(getBean(), args);}catch (IllegalArgumentException ex) {assertTargetBean(getBridgedMethod(), getBean(), args);String text (ex.getMessage() ! null ? ex.getMessage() : Illegal argument);throw new IllegalStateException(formatInvokeError(text, args), ex);}catch (InvocationTargetException ex) {// Unwrap for HandlerExceptionResolvers ...Throwable targetException ex.getTargetException();if (targetException instanceof RuntimeException) {throw (RuntimeException) targetException;}else if (targetException instanceof Error) {throw (Error) targetException;}else if (targetException instanceof Exception) {throw (Exception) targetException;}else {throw new IllegalStateException(formatInvokeError(Invocation failure, args), targetException);}}} d. DispatchServlet # processDispatchResult 对于某些接口需要渲染ModelAndView的需要在下面这个方法里处理例如有个接口采用的是thymeleaf模板引擎来渲染接口数据。如下例子 RequestMapping(/testHtml)public String testHtml(MapString, Object map) {map.put(msg,h1Hello,SpringBoot/h1);map.put(users, Arrays.asList(zhangsan,lisi));return testHtml;} !DOCTYPE html html langen xmlns:thhttp://www.thymeleaf.org headmeta charsetUTF-8title测试/title/head bodyh1测试页面/h1 div th:text${msg}/div div th:utext${msg}/div h4 th:eachuser :${users}th:text${user} /h4 /body /html 效果如下 针对上面这个例子执行完testHtml方法后拿到返回的ModelAndView对象后执行下面processDispatchResult中的render方法渲染页面信息 /*** 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,Nullable HandlerExecutionChain mappedHandler, Nullable ModelAndView mv,Nullable Exception exception) throws Exception {boolean errorView false;... 省略若干代码 // Did the handler return a view to render?if (mv ! null !mv.wasCleared()) {// 渲染 ModelAndViewrender(mv, request, response);if (errorView) {WebUtils.clearErrorRequestAttributes(request);}}else {if (logger.isTraceEnabled()) {logger.trace(No view rendering, null ModelAndView returned.);}}if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {// Concurrent handling started during a forwardreturn;}if (mappedHandler ! null) {// 执行拦截器的afterCompletion方法mappedHandler.triggerAfterCompletion(request, response, null);}} 从下图中可以看到 mv ha.handle(processedRequest, response, mappedHandler.getHandler()); 返回的是mv 非空说明有对应的ModelAndView需要渲染。 3、总结 从以上的执行过程来看一个完整的http get 请求大概会经过执行 filter、从HandlerMapping中获取HandlerExecutionChainHandlerExecutionChain里面包含了一个真正的处理handler(HandlerMethodHandlerMethod包含了要执行方法的method信息以及类实例对象) 和若干个拦截器interceptors然后根据handler获取对应的HandlerAdapter去执行在执行过程中通过反射机制调用对应Controller的方法拿到结果拿到结果后进行返回值的回写以及页面的渲染(如果有必要)在执行过程的前后会分别执行接口的拦截器preHandle以及postHandle方法。 那么这整个过程的示意图如下 四、用到的技术点 1、设计模式 1) 模版模式 HandlerMapping​​​​​​​ 比如 HandlerMapping的实现抽象类AbstractHandlerMapping中有个getHanlder 方法其中getHandlerInternal定义了模版方法具体由子类实现 /*** 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*/OverrideNullablepublic final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {// 该方法则是模版方法具体由子类实现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);}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;} /*** Look up a handler for the given request, returning {code null} if no* specific one is found. This method is called by {link #getHandler};* a {code null} return value will lead to the default handler, if one is set.* pOn CORS pre-flight requests this method should return a match not for* the pre-flight request but for the expected actual request based on the URL* path, the HTTP methods from the Access-Control-Request-Method header, and* the headers from the Access-Control-Request-Headers header thus allowing* the CORS configuration to be obtained via {link #getCorsConfiguration(Object, HttpServletRequest)},* pNote: This method may also return a pre-built {link HandlerExecutionChain},* combining a handler object with dynamically determined interceptors.* Statically specified interceptors will get merged into such an existing chain.* param request current HTTP request* return the corresponding handler instance, or {code null} if none found* throws Exception if there is an internal error*/Nullableprotected abstract Object getHandlerInternal(HttpServletRequest request) throws Exception; 2) 责任链模式 Filter http请求中会执行filter, filter采用的是责任链模式整个过程沿着链条上的各个有序的filter执行 /*** ApplicationFilterChain*/public void doFilter(ServletRequest request, ServletResponse response)throws IOException, ServletException {... 省略若干行internalDoFilter(request,response);}private void internalDoFilter(ServletRequest request,ServletResponse response)throws IOException, ServletException {// Call the next filter if there is oneif (pos n) {ApplicationFilterConfig filterConfig filters[pos];...省略若干行// 调用filter的doFilter方法同时将 this对象传过去方便将责任链传递下去filter.doFilter(request, response, this);return;}} Slf4j public class LogFilter implements Filter {Overridepublic void init(FilterConfig filterConfig) {}Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {HttpServletRequest request (HttpServletRequest) servletRequest;log.info(经过logFilter {}, request);// 接收上一个filter传过来的filterChain同时调用filterChain的doFilter方法filterChain.doFilter(servletRequest, servletResponse);}Overridepublic void destroy() {} } 2、反射 在执行handler时handler处理过程中会把流量转发到各个controller中的方法执行为了统一调用逻辑这里采用了反射的方式处理 /*** Invoke the handler method with the given argument values.*/Nullableprotected Object doInvoke(Object... args) throws Exception {// 改变方法的可见性这就是为什么即使controller中的方法是private的也能正常访问ReflectionUtils.makeAccessible(getBridgedMethod());try {// 这就是精髓所在熟悉的配方熟悉的味道这不就是反射调用吗return getBridgedMethod().invoke(getBean(), args);}catch (IllegalArgumentException ex) {assertTargetBean(getBridgedMethod(), getBean(), args);String text (ex.getMessage() ! null ? ex.getMessage() : quot;Illegal argumentquot;);throw new IllegalStateException(formatInvokeError(text, args), ex);}catch (InvocationTargetException ex) {// Unwrap for HandlerExceptionResolvers ...Throwable targetException ex.getTargetException();if (targetException instanceof RuntimeException) {throw (RuntimeException) targetException;}else if (targetException instanceof Error) {throw (Error) targetException;}else if (targetException instanceof Exception) {throw (Exception) targetException;}else {throw new IllegalStateException(formatInvokeError(quot;Invocation failurequot;, args), targetException);}}} 五、回顾 那么回过头来你能回答文章最前面提出的问题了吗 1、http请求出现404等状态码时知道从哪里开始排查了吗 2、一个http请求是如何找到controller中的方法并执行呢 3、在你了解到spring mvc 的处理机制前如果让你来设计这套流程你会怎么做 针对前面三个问题在你了解spring mvc 的处理机制后你觉得这个流程设计到怎么样呢
http://wiki.neutronadmin.com/news/90140/

相关文章:

  • 建站公司属于什么类型求职网站开发
  • 做网站找景安吉林省水土保持生态建设网站
  • 辽源做网站外部调用wordpress函数
  • 郴州吧百度贴吧廊坊seo优化
  • 电子商务网站建设与管理心得精仿虎嗅网wordpress
  • 国内搜索网站许昌建网站的公司在哪条路
  • 中国河北建设银行官网招聘网站苏州建筑业网
  • 冒充it男给某网站做修复深圳企业网站建设制作公司
  • 区域城市分站网站怎么做wordpress上传图片时发生了错误
  • 免费刷粉网站推广免费万州网站建设果园路
  • wordpress 图片外链插件山东seo优化
  • 网站怎么架设怎样申请网站空间
  • 记事本做网站怎么加背景图广西住建厅行政审批系统
  • 北京网站建设itcask大望路网站制作
  • 建网站要多少钱一年wordpress子目录站点
  • 上海网站建设专家昆明市建设厅官方网站
  • 网站运营成功案例建网络商城网站吗
  • 优秀企业网站设计欣赏深圳市建设工程资料网站
  • 建设网站银行wordpress太多重定向
  • 电商网站建设需求分析引言移动互联网开发试卷
  • 营销型网站的类型做网站多少钱google
  • 怎么选择网站建设公司在线电子印章制作
  • 深圳微信推广平台网站优化建议书
  • 公司想做一个网站中国十大网络安全公司排名
  • 房地产网站设计公司wordpress公众号验证码
  • 免费有效的推广网站重庆响应式网站平台
  • 湖南系统建站怎么用网站自己怎么做
  • 高端建站什么意思互动对战平台
  • 怎样做汽车网站建设济南建设银行网站
  • 大气网站模板免费下载瑞安做网站