What is template mode
The template method pattern defines an algorithm skeleton in a method and defers certain steps to subclasses. The template method pattern allows subclasses to redefine certain steps in the algorithm without changing the overall structure of the algorithm.
The role of template mode
Reuse and expansion
Standard implementation
public abstract class AbstractClass { public final void templateMethod () { .... handles some initialization operations or common logic // ... method1 (); // ... method2 (); // ... } // define Good template method 1 protected abstract void method1 (); // Defined template method 2 protected abstract void method2 (); } // Implementation 1 public class ConcreteClass1 extends AbstractClass { //The parent class initializes its own processing logic @Override protected void method1 () { // ... } @Override protected void method2 () { // ... } } // Implement 2 public class ConcreteClass2 extends AbstractClass { // The parent class initializes its own processing logic @Override protected void method1 () { // ... } @Override protected void method2 () { // ... } } AbstractClass demo = ConcreteClass1(); demo.templateMethod();
Use in source code
DispatcherServlet
org.springframework.web.servlet.FrameworkServlet do some initialization operations
protected final void processRequest (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { long startTime = System.currentTimeMillis (); Throwable failureCause = null ; / ** * <31> Get the current thread internationalization context from inside 2 ThreadLocal Get * org.springframework.web.filter.RequestContextFilter # initContextHolders * If this filter is configured here every request has been initialized spring boot Verification * / LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext (); // Build the current thread when this request is international Contextualization LocaleContext localeContext = this .buildLocaleContext (request); / ** * <32> Get the RequestAttribute of the current thread request which will be obtained from 2 ThreadLocal * org.springframework.web.filter.RequestContextFilter # initContextHolders has initialized spring boot verification * / RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes (); // <4> If the thread cache has not been built, it means that ServletRequestAttributes have not been initialized before requestAttributes = this .buildRequestAttributes (request, response, previousAttributes); / ** * will be obtained from requsetAttribute To initialize one and then set it in key = WebAsyncUtils.WEB_ASYNC_MANAGER_ATTRIBUTE * means that we can follow after reqeust.getAttribute (WebAsyncUtils.WEB_ASYNC_MANAGER_ATTRIBUTE) Get * Used to manage asynchronous requests * / WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager (request); asyncManager.registerCallableInterceptor (FrameworkServlet. Class .getName (), new FrameworkServlet.RequestBindingInterceptor ()); / ** * <5> This will be stored according to the current object idiom variable To which thread cache threadContextInheritable defaults to false * We can use the previous BeanWapper to modify * Indicates that we can get it later based on LocaleContextHolder and RequestContextHolder * * / this .initContextHolders (request, localeContext, requestAttributes); try { // <6> Abstract methods are implemented by subclasses such as: DispatcherServlet implements this.doService(request, response); } catch (ServletException var17) { failureCause = var17; throw var17; } catch (IOException var18) { failureCause = var18; throw var18; } catch (Throwable var19) { failureCause = var19; throw new NestedServletException("Request processing failed", var19); } finally { / ** * <7> The default previousLocaleContext previousAttributes is null * This is mainly to clear the previousLocaleContext previousAttributes cached by the thread * / this .resetContextHolders (request, previousLocaleContext, previousAttributes); if (requestAttributes! = null ) { requestAttributes. requestCompleted (); } if ( this .logger.isDebugEnabled ()) { if (failureCause! = null ) { this .logger.debug ("Could not complete request" , (Throwable) failureCause); } else if(asyncManager.isConcurrentHandlingStarted ()) { this .logger.debug ("Leaving response open for concurrent processing" ); } else { this .logger.debug ("Successfully completed request" ); } } // Spring event mechanism publishes ServletRequestHandlerEvent message , Whether this request is successfully executed or not will publish this message.publishRequestHandledEvent (request, response, startTime, (Throwable) failureCause); } }
// Abstract method protected abstract void doService (HttpServletRequest var1, HttpServletResponse var2) throws Exception;
org.springframework.web.servlet.DispatcherServlet#doService
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception { if (this.logger.isDebugEnabled()) { String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : ""; this.logger.debug("DispatcherServlet with name '" + this.getServletName() + "'" + resumed + " processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]"); } //如果是include请求,保存request attribute快照数据,并在finally中进行还原 Map<String, Object> attributesSnapshot = null; / ** * request.getAttribute ("javax.servlet.include.request_uri")! = null * <jsp: incluede page = "xxx.jsp" /> * JSP nested above the tag compiler is also a servlet Use this attribute to determine whether it is an include tag * / if (WebUtils.isIncludeRequest (request)) { attributesSnapshot = new HashMap (); Enumeration attrNames = request.getAttributeNames (); label108: while ( true ) { String attrName; do { if (! attrNames.hasMoreElements ()) { break label108; } attrName = (String)attrNames.nextElement(); } while(!this.cleanupAfterInclude && !attrName.startsWith("org.springframework.web.servlet")); attributesSnapshot.put(attrName, request.getAttribute(attrName)); } } //保存ApplicationContext 到request request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.getWebApplicationContext()); //保存localeResolver 到request request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver); // Save themeResolver to request request.setAttribute (THEME_RESOLVER_ATTRIBUTE, this .themeResolver); // Save gThemeSource to request request.setAttribute (THEME_SOURCE_ATTRIBUTE, this .getThemeSource ()); // It seems to solve the redirect 302 band The parameter length problem is first saved to the server and then the 302 redirect only needs to bring a corresponding key FlashMap inputFlashMap = this .flashMapManager.retrieveAndUpdate (request, response); if (inputFlashMap! = Null ) { request.setAttribute (INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap (inputFlashMap)); } //Save an empty FlashMap request.setAttribute (OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap ()); // Save flashMapManager request.setAttribute (FLASH_MAP_MANAGER_ATTRIBUTE, this .flashMapManager); try { // <7> Execute processing request this .doDispatch (request, response) ; } finally { // Restore snapshot data if (! WebAsyncUtils.getAsyncManager (request) .isConcurrentHandlingStarted () && attributesSnapshot! = null ) { this .restoreAttributesAfterInclude (request, attributesSnapshot); } } }
Excerpt from: "SpringMVC source code reading-a request main processing flow DispatcherServlet (four)"