所有的请求都将会经过DispatcherServlet这个类,而查看这个类的类结构,可以看到对应的继承体系图:
既然是定位到了对应的HttpServlet方法,那么看一下其中的doPost/Get方法对应的实现。
org.springframework.web.servlet.HttpServletBean
doGet方法:
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String msg = lStrings.getString("http.method_get_not_supported"); this.sendMethodNotAllowed(req, resp, msg); }
doPost方法:
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String msg = lStrings.getString("http.method_post_not_supported"); this.sendMethodNotAllowed(req, resp, msg); }
这两个方法看上去没有可获取得到价值的地方。那么看一下子类对其的实现:
org.springframework.web.servlet.FrameworkServlet
doGet/Post实现
protected final void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.processRequest(request, response); } protected final void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.processRequest(request, response); }
从这里可以看到无论是doGet/Post方法,都对类中的方法进行了重写。所以看一下重写的方法:
protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { long startTime = System.currentTimeMillis(); Throwable failureCause = null; LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext(); LocaleContext localeContext = this.buildLocaleContext(request); RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes(); ServletRequestAttributes requestAttributes = this.buildRequestAttributes(request, response, previousAttributes); WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new FrameworkServlet.RequestBindingInterceptor()); this.initContextHolders(request, localeContext, requestAttributes); try { // 核心方法 this.doService(request, response); } catch (IOException | ServletException var16) { failureCause = var16; throw var16; } catch (Throwable var17) { failureCause = var17; throw new NestedServletException("Request processing failed", var17); } finally { this.resetContextHolders(request, previousLocaleContext, previousAttributes); if (requestAttributes != null) { requestAttributes.requestCompleted(); } this.logResult(request, response, (Throwable)failureCause, asyncManager); this.publishRequestHandledEvent(request, response, startTime, (Throwable)failureCause); } }
那么接着看对应的doService方法:
protected abstract void doService(HttpServletRequest var1, HttpServletResponse var2) throws Exception;
结果发现这里是一个抽象方法,那么交给子类来进行实现。通过最开始的继承体系图可以看到子类是DispatcherServlet类,那么看一下对应的子类的实现:
@Override protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception { logRequest(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<>(); Enumeration<?> attrNames = request.getAttributeNames(); while (attrNames.hasMoreElements()) { String attrName = (String) attrNames.nextElement(); if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) { attributesSnapshot.put(attrName, request.getAttribute(attrName)); } } } // Make framework objects available to handlers and view objects. 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()); if (this.flashMapManager != null) { 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(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方法:
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. 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; } } 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; } catch (Throwable err) { // As of 4.3, we're processing Errors thrown from handler methods as well, // making them available for @ExceptionHandler methods and other scenarios. dispatchException = new NestedServletException("Handler dispatch failed", err); } processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); } catch (Exception ex) { triggerAfterCompletion(processedRequest, response, mappedHandler, ex); } catch (Throwable err) { triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", err)); } finally { if (asyncManager.isConcurrentHandlingStarted()) { // Instead of postHandle and afterCompletion if (mappedHandler != null) { mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response); } } else { // Clean up any resources used by a multipart request. if (multipartRequestParsed) { cleanupMultipart(processedRequest); } } } }
所以下面只需要分析几个重点的代码片段即可,下面将一一列举:
// Determine handler for the current request. mappedHandler = getHandler(processedRequest); // Determine handler adapter for the current request. HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } // Actually invoke the handler. mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); mappedHandler.applyPostHandle(processedRequest, response, mv); processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
以上几个方法就是重要的方法,只要搞懂了这些,那么定制化SpringMVC就指日可待。
首先看看第一个方法:
// Determine handler for the current request. mappedHandler = getHandler(processedRequest);
对于本次请求,哪个handler可以来为本次请求进行响应。所谓的handler就是我们在controller中写的方法,springMVC将其称之为handler。
那么翻译过来就是controller层中的哪个方法可以会当前的请求来进行响应。那么又是如何找到的呢?我们接着看:
@Nullable protected 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; }
看到这个方法重点主要变量handlerMappings,那么这个是什么?
/** List of HandlerMappings used by this servlet. */ @Nullable private List<HandlerMapping> handlerMappings;
对于HandlerMapping来说,可以看一下结构体系图:
尽管这里我们都不认识,但是我们比较熟悉一个类:RequestMappingHandlerMapping
请求映射处理器映射器。我们经常使用的注解@RequestMapping大概和这个有关。
但是止步于此,打上断点。看一下handlerMappings的值:
通过断点可以看到,我们使用最多的就是第一个,那么第一个注解中有什么信息:
从这里我们可以看到几个信息。springmvc在启动加载的时候,就会扫描所有的controller上加了@RequestMapping注解的路径,然后将其保存到urlLookup中,然后将对应的handler保存起来。对应的可以在mappingLookup中可以看到对应的信息。
那么接着看刚刚的代码,看看如何来查找对应的handler的:
/** * 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 { // 找到handler 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包装到handler的执行链中来 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 (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) { CorsConfiguration config = (this.corsConfigurationSource != null ? this.corsConfigurationSource.getCorsConfiguration(request) : null); CorsConfiguration handlerConfig = getCorsConfiguration(handler, request); config = (config != null ? config.combine(handlerConfig) : handlerConfig); executionChain = getCorsHandlerExecutionChain(request, executionChain, config); } return executionChain; }
那么看一下如何找到对应的handler:
@Override protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception { request.removeAttribute(PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE); try { // 访问父类的handler return super.getHandlerInternal(request); } finally { ProducesRequestCondition.clearMediaTypesAttribute(request); } }
@Override protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception { // 这里可以从UrlPathHelper中找到。UrlPathHelper保存了request中的信息保存到域中 // 在获取的时候只需要来拿即可 String lookupPath = getUrlPathHelper().getLookupPathForRequest(request); request.setAttribute(LOOKUP_PATH, lookupPath); this.mappingRegistry.acquireReadLock(); try { // 从这里来进行获取!可以进去看一下,因为刚刚已经可以通过路径知道,哪个url的可以由哪个handler来获取 // 直接获取即可。然后封装成HandlerMethod HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request); return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null); } finally { this.mappingRegistry.releaseReadLock(); } }
返回之后,下面来到了这个方法。
// Determine handler adapter for the current request. HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
因为找到了对应的handler之后,紧接着需要去确定参数的值,确定好了参数的值之后,需要通过反射来调用方法获取得到返回值。
因为这里的步骤太过于复杂,所以springMVC中将其封装成了一个工具类,就是这个handler适配器。
那么可以看一下如何获取得到适配器的。
/** * 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"); }
看一下handlerAdaper中的接口设计:
public interface HandlerAdapter { // 当前的adpater是否支持这种类型的handler boolean supports(Object handler); // 如果支持,那么就用这种adpater来进行处理 ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception; }
那么断点看一下所有的adpater:
看到第一个,就已经十分明显了。第一个就是我们需要的。那么看一下第一个里面保存了什么信息
这个在后期会为我们来做很多工作,那么接着看如何来判断是否支持的:
来到第一个adapter
@Override public final boolean supports(Object handler) { return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler)); }
很明显当前的hander是HandlerMethod,因为获取的时候已经被封装了,那么接着判断
@Override protected boolean supportsInternal(HandlerMethod handlerMethod) { return true; }
就直接返回了true。所以当前的org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter支持解析标注了@RequestMapping注解的
那么接着走:
// 如果这里返回的是true,那么表示的是mappedHandler.applyPreHandle为false,也就是说明被拦截器拦截住了 // 被拦截器拦截住了,方法就不应该继续向下进行执行,直接进行return,结束了整个方法,方法就不会继续向下执行了 if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; }
接着就来到了
org.springframework.web.servlet.HandlerExecutionChain#applyPreHandle
这个方法标识的是拦截器的前置处理方法
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]; // 从这里可以看到如果当前的拦截器返回为fasle,代表的是被拦截,被拦截就立即执行后置处理方法 // 所以后置处理方法中可以来做一些处理。处理完成之后,然后返回false if (!interceptor.preHandle(request, response, this.handler)) { triggerAfterCompletion(request, response, null); return false; } this.interceptorIndex = i; } } return true; }
那么不妨在这里看一下HandlerInterceptor接口:
public interface HandlerInterceptor { // 前置处理!如果这里的判断为true,才会去执行目标方法。 // 因为从上面可以看到,handler方法还没有执行。 default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { return true; } // handler方法执行之后。整个讲到了之后慢慢来进行分析 default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception { } // 最终执行 default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception { } }
如果拦截器的前置方法返回为true,那么代表是的当前方法可以执行handler。如果返回为fasle,那么标识的是直接被拒绝。
那么看一下为true的情况,让方法继续向下执行。
// Actually invoke the handler. mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
这个方法的注释已经说明了,真正执行handler,然后得到返回值。重要!因为前面已经确定是了哪个handler可以来处理这个方式,下面就会进入到对应的方法中来:
org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter#handle
看一下对应的代码:
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { return handleInternal(request, response, (HandlerMethod) handler); }
接着调用下一个:
@Override protected 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 necessary mav = invokeHandlerMethod(request, response, handlerMethod); } } else { // 调用handler的方法来处理请求!那么重点就在这里了 mav = invokeHandlerMethod(request, response, handlerMethod); } if (!response.containsHeader(HEADER_CACHE_CONTROL)) { if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) { applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers); } else { prepareResponse(response); } } return mav; }
进去查看:
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); // 注意查看这里的参数解析器。因为在handler方法中会存在着注解和指定的数据类型 // 参数解析器就是根据注解和数据类型来进行判断是否支持以及支持了之后如何来进行解析 if (this.argumentResolvers != null) { invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers); } // 返回值处理器 // 对于方法返回值来说,也就是我们方法的返回值类型。springmvc决定来如何进行处理 // 因为可能是文件、字符串、json、xml等等各种格式。使用返回值处理器来决定对我们程序中产生的返回值来进行处理 if (this.returnValueHandlers != null) { invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers); } invocableMethod.setDataBinderFactory(binderFactory); invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer); ModelAndViewContainer mavContainer = new ModelAndViewContainer(); 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(); } }
那么handler即将要被调用了,看看如何处理的:
org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod#invokeAndHandle
/** * 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 { // 对当前的handler进行调用。这里获取的方法的返回值结果 // 所以这里是重点! Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs); setResponseStatus(webRequest); if (returnValue == null) { if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) { disableContentCachingIfNecessary(webRequest); mavContainer.setRequestHandled(true); return; } } // ResponseStatus注解的reason解释 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; } }
@Nullable public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { // 确定handler中的参数 Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs); if (logger.isTraceEnabled()) { logger.trace("Arguments: " + Arrays.toString(args)); } // 看到没!反射开始执行 return doInvoke(args); }
那么今天的重点来了,如何确定方法的参数值的:
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { // 在这里来获取hander中的每个参数的详细信息。有无注解,对应的参数数据类型是什么,这里都有 MethodParameter[] parameters = getMethodParameters(); if (ObjectUtils.isEmpty(parameters)) { // 如果没有参数,就直接返回了 return EMPTY_ARGS; } // 有多少个参数,就创建出来数组长度为多少个参数的length数组 Object[] args = new Object[parameters.length]; for (int i = 0; i < parameters.length; i++) { MethodParameter parameter = parameters[i]; // 找到参数的名字 parameter.initParameterNameDiscovery(this.parameterNameDiscoverer); // 根据参数名称找到对应的参数值 args[i] = findProvidedArgument(parameter, providedArgs); if (args[i] != null) { continue; } // 拿到值之后需要来对其进行解析。因为从客户端来的,大多都是string、文件等等。需要将对应的数据来进行转换 // 比如说"123"转换成123(Interger)或者是""类型的;亦或是json转换成对象等等操作 // 拿到所有的参数解析器来进行解析,首先进行判断,判断完成之后来进行解析。看看下面抛出的异常! if (!this.resolvers.supportsParameter(parameter)) { throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver")); } try { // 拿到参数解析器来进行解析。解析之后,将解析完成后的值赋值返回来,然后重新返回。 args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory); } catch (Exception ex) { // Leave stack trace for later, exception may actually be resolved and handled... if (logger.isDebugEnabled()) { String exMsg = ex.getMessage(); if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) { logger.debug(formatArgumentError(parameter, exMsg)); } } throw ex; } } return args; }
那么至此,大概原理是分析结束了。下一章节举例来说明每种类型的处理方式。