¿Qué es el modo de plantilla?
El patrón de método de plantilla define un esqueleto de algoritmo en un método y difiere ciertos pasos a subclases. El patrón de método de plantilla permite que las subclases redefinan ciertos pasos en el algoritmo sin cambiar la estructura general del algoritmo.
El rol del modo plantilla
Reutilización y expansión
Implementación estándar
public abstract class AbstractClass { public final void templateMethod () { .... maneja algunas operaciones de inicialización o lógica común // ... method1 (); // ... method2 (); // ... } // define Método de plantilla correcto 1 método de vacío abstracto protegido1 (); // Método de plantilla definido 2 método de vacío abstracto protegido2 (); } // Implementación 1 clase pública ConcreteClass1 extiende AbstractClass { // enfoque inicializado en su lógica de procesamiento de los padres @ Override protegida sin efecto la metodo1 () { // ... } @ Override protegida vacío metodo2 () { // ... } } // aplicar 2 pública clase ConcreteClass2 la extiende el AbstractClass { // La clase padre inicializa su propia lógica de procesamiento @Override protected void method1 () { // ... } @Override protected void method2 () { // ... } } AbstractClass demo = ConcreteClass1 (); demo.templateMethod ();
Usar en código fuente
DispatcherServlet
org.springframework.web.servlet.FrameworkServlet realiza algunas operaciones de inicialización
proceso de solicitud final vacío protegido (solicitud HttpServletRequest, respuesta HttpServletResponse) arroja ServletException, IOException { long startTime = System.currentTimeMillis (); Throwable failureCause = null ; / ** * <31> Obtiene el contexto de internacionalización de subproceso actual desde dentro de 2 ThreadLocal Get * org.springframework.web.filter.RequestContextFilter # initContextHolders * Si este filtro está configurado aquí, todas las solicitudes se han inicializado Spring boot Verification * / LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext (); // Construye el hilo actual cuando esta solicitud es internacional Contextualización LocaleContext localeContext = this .buildLocaleContext (request); / ** * <32> Obtenga el atributo RequestAttribute de la solicitud de subproceso actual que se obtendrá de 2 ThreadLocal * org.springframework.web.filter.RequestContextFilter # initContextHolders ha inicializado la verificación de arranque de resorte * / RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes (); // <4> Si no se ha creado el caché de subprocesos, significa que ServletRequestAttributes no se han inicializado antes de requestAttributes = this .buildRequestAttributes (request, response, previousAttributes); / ** * se obtendrá de requsetAttribute Para inicializar uno y luego configurarlo en key = WebAsyncUtils.WEB_ASYNC_MANAGER_ATTRIBUTE * significa que podemos seguir después reqeust.getAttribute (WebAsyncUtils.WEB_ASYNC_MANAGER_ATTRIBUTE) Get * Se usa para administrar solicitudes asincrónicas * / WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager (solicitud); asyncManager.registerCallableInterceptor (FrameworkServlet. Class .getName (), nuevo FrameworkServlet.RequestBindingInterceptor ()); / ** * <5> Esto se almacenará de acuerdo con la variable de idioma del objeto actual A qué subproceso de caché threadContextInheritable se establece en falso * Podemos usar el BeanWapper anterior para modificar * Indica que podemos obtenerlo más tarde en función de LocaleContextHolder y RequestContextHolder * * / this .initContextHolders (request, localeContext, requestAttributes); try{ // <6> 抽象 方法 由 子 类 实现 如: DispatcherServlet 实现 this .doService (solicitud, respuesta); } catch (ServletException var17) { failureCause = var17; tirar var17; } catch (IOException var18) { failureCause = var18; tirar var18; } catch (Throwable var19) { failureCause = var19; lanzar una nueva NestedServletException ("Error al procesar la solicitud" , var19); } finalmente { / ** * <7> El valor predeterminado anteriorLocaleContext previousAttributes es nulo * Esto es principalmente para borrar los anteriorLocaleContext previousAttributes almacenados en caché por el hilo * / this .resetContextHolders (request, previousLocaleContext, previousAttributes); if (requestAttributes! = null ) { requestAttributes. requestCompleted (); } if ( this .logger.isDebugEnabled ()) { if (failureCause! = null ) { this .logger.debug ("No se pudo completar la solicitud" , (Throwable) failureCause); } más si(asyncManager.isConcurrentHandlingStarted ()) { this .logger.debug ("Dejar respuesta abierta para procesamiento concurrente" ); } else { this .logger.debug ("Solicitud completada con éxito" ); } } // El mecanismo de evento Spring publica el mensaje ServletRequestHandlerEvent , Si esta solicitud se ejecuta correctamente o no, publicará este mensaje.publishRequestHandledEvent (request, response, startTime, (Throwable) failureCause); } }
// Método abstracto protegido abstract void throwsdoService (HttpServletRequest var1, HttpServletResponse var2) Excepción;
org.springframework.web.servlet.DispatcherServlet # doService
protegida vacío doService (HttpServletRequest solicitud, HttpServletResponse respuesta) lanza la excepción { si ( este .logger.isDebugEnabled ()) { Cadena reanudó = WebAsyncUtils.getAsyncManager (petición) .hasConcurrentResult ()? "resumed": "" ; this .logger.debug ("DispatcherServlet con el nombre '" + this .getServletName () + "'" + resumed + "processing" + request.getMethod () + "request for [" + getRequestUri (request) + "]" ) ; } // 如果 是 incluir 请求 , 保存 atributo de solicitud 快照 数据 , 并 在 finalmente 中 进行 还原 Mapa <Cadena, Objeto>; / ** * request.getAttribute ("javax.servlet.include.request_uri")! = null * <jsp: incluede page = "xxx.jsp" /> * JSP anidado sobre el compilador de etiquetas también es un servlet Use este atributo para determinar si es una etiqueta de inclusión * / if (WebUtils.isIncludeRequest (request)) { atributosSnapshot = new HashMap (); Enumeration attrNames = request.getAttributeNames (); label108: while ( true ) { String attrName; do { if (! attrNames.hasMoreElements ()) { breaketiqueta108; } attrName = (String) attrNames.nextElement (); } while (! this .cleanupAfterInclude &&! attrName.startsWith ("org.springframework.web.servlet" )); atributosSnapshot.put (attrName, request.getAttribute (attrName)); } } // 保存 ApplicationContext 到 request request.setAttribute (WEB_APPLICATION_CONTEXT_ATTRIBUTE, this .getWebApplicationContext ()); // 保存 localeResolver 到 request request.setAttribute (LOCALE_RESOLVER_ATTRIBUTE, esto.localeResolver); // guardar la solicitud de themeResolver request.setAttribute (THEME_RESOLVER_ATTRIBUTE, la presente .themeResolver); // guardar la solicitud de gThemeSource request.setAttribute (THEME_SOURCE_ATTRIBUTE, la presente .getThemeSource ()); // se parece a un cinturón 302 para resolver la redirección El problema de la longitud del parámetro se guarda primero en el servidor y luego la redirección 302 solo necesita traer la clave correspondiente FlashMap inputFlashMap = this .flashMapManager.retrieveAndUpdate (request, response); if (inputFlashMap! = Null ) { request.setAttribute (INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodif. (inputFlashMap)); } //Guarde un flashMap request.setAttribute vacío (OUTPUT_FLASH_MAP_ATTRIBUTE, nuevo FlashMap ()); // Guarde flashMapManager request.setAttribute (FLASH_MAP_MANAGER_ATTRIBUTE, este .flashMapManager); intente { // <7> Ejecutar solicitud de procesamiento esta .doDispatch (solicitud de respuesta, esta respuesta de respuesta, ; } finalmente { // Restaurar datos de la instantánea if (! WebAsyncUtils.getAsyncManager (request) .isConcurrentHandlingStarted () && atributosSnapshot! = null ) { this .restoreAttributesAfterInclude (request, atributosSnapshot); } } }
Extracto de: "Lectura del código fuente SpringMVC : flujo de procesamiento principal de solicitud DispatcherServlet (cuatro)"