0 Resumen
Este artículo explica brevemente el enlace de mapeo del procesador de SpringMVC desde el nivel del código fuente, es decir, el proceso detallado de encontrar el controlador
1 proceso de solicitud de SpringMVC
El controlador busca los pasos 1 a 2 correspondientes a la imagen de arriba
Diagrama de flujo de operación detallado de SpringMVC
2 Proceso de inicialización de SpringMVC
2.1 Primero entienda dos categorías
1.RequestMappingInfo
Anotación de mapas de solicitudes de encapsulación
Contiene información relevante sobre encabezados de solicitud HTTP
Una instancia corresponde a una anotación de mapeo de solicitudes
2.etlermetodio
Método de manejo de solicitudes para encapsular controlador
Incluye el objeto Bean al que pertenece el método, el objeto del método correspondiente al método, los parámetros del método, etc.
Relación de herencia de SolicmingMappingHandLermapping
Durante la inicialización de SpringMVC
Primero ejecute el AfterPropertIesset of SolicsMappingHandLermapping
Luego ingrese la AfterPropertiesset AfterPropertiosset AfterPropertiosset AfterPropertioss.
Este método entrará en los metros de inithandlermets de esta clase.
Responsable de escanear los frijoles de ApplicationContext, luego encontrar y registrar métodos de procesador de frijoles
// escanee los frijoles en los métodos de manejador aplicados, detectar y registrar el manejador.protected inithandlermethods () {... // obtenga todos los frijoles en la aplicación AplicationContext [] beanNames = (this.detEtThandLermethodsinancEntorconTexts? getApplicationContext (). GetBeanNamesFortype (objeto.class)); // Transferir la matriz de BeanName para (String BeanName: BeanNames) {// isHandler juzgará si la definición de bean contiene anotación del controlador o anotación de mapeo de solicitudes basadas en el bean. If (isHandler (getApplicationContext (). GetType (BeanName))) {DetEdThandLermethods (BeanName); }} handlermethodsinitialized (gethandlermethods ());} Requestmappinghandlermapping#isHandler
El método anterior es determinar si la definición actual de frijoles contiene anotación del controlador o anotación de mapas de solicitudes
Si solo Solicmapping entra en vigencia? ¡No!
Porque en este caso, la clase no se registrará como un frijol de primavera al inicializar el resorte, y la clase no se atravesará al atravesar los nombres de los frijoles, por lo que está bien reemplazar el controlador con Compotent aquí, pero esto no se hace en general
Después de confirmar que el frijol es un controlador, el método de controlador específico se encontrará de Bean (es decir, el método de procesamiento de solicitudes definido en la clase del controlador). El código de búsqueda es el siguiente
/** * Busque métodos de controlador en un controlador * @param Handler El nombre del frijol de un controlador o una instancia de controlador */protegido void detectthandLermethods (controlador de objetos final) {// ¿Obtiene el objeto de clase del controlador actual BeanClass <?> Handlertype = (Handler Instance de String)? getApplicationContext (). GetType ((String) Handler): Handler.getClass (); // Evite las llamadas repetidas a GetMappingFormethod para reconstruir el mapa final de la instancia de la instancia de SolyMappingInfo <método, t> mappes = new IdentityHashMap <método, t> (); // Igual que el anterior, también es el objeto de clase de la clase final del controlador Bean <?> usertype = classUtils.getuserClass (Handlertype); // Obtenga todos los métodos de controlador de la frijol actual // Definir si tiene requestmapping de acuerdo con el método // Si existe, cree un conjunto de instancias de SolicSmappingInfo <Methodic> métodos = handlermethodselector.selectMethods (userType, nuevo método MethodFilter () {@Override public Publican Boolean (método método) {t Mappping = getMapping; NULL) {MAPPES.PUT (método, mapeo); // Viaje y registre todo el método del controlador del bean actual para (método método: métodos) {// Método del controlador de registro e ingrese el siguiente método Registrohandlermethod (Handler, Method, Mappes.get (Method)); } Hay dos lugares en el código anterior que llama GetMappingFormethod
Crear requestMappingInfo utilizando el método y el tipo de anotación de mapeo de solicitudes de nivel
@Override requestmappinginfo getMappingFormethod (método método, clase <?> Handlertype) {requestMappingInfo info = null; // Obtener el método @RequestMapping requestmapping métodannotation = annotationUtils.findannotation (método, requestMapping.class); if (MethodAntation! = NULL) {requestCondition <?> MethodCondition = getCustomMethodCondition (método); info = creeaterEctMappingInfo (MethodAnnotation, MethodCondition); // Obtenga la anotación @reqUtestMapping del bean a la que el método pertenece a requestmapping typeannotation = annotationUtils.findannotation (handlertype, requitsmapping.class); if (typeanNotation! = null) {requestCondition <?> typecondition = getCustomTyPeCondition (HandLertype); // fusionar dos @RequestMapping Annotations Info = CreateRequestMappingInfo (typeanNotation, typecondition) .combine (información); }} Información de retorno; } El propósito de este método es crear un objeto requestMappingInfo basado en el método del método del controlador. Primero, determine si el Mehtod contiene la anotación de SolicMpping. Si es así, cree un objeto RequestMappingInfo directamente en función del contenido de la anotación. Después de la creación, determine si el frijol al que el método actual también contiene la anotación de mapas de solicitudes. Si se incluye esta anotación, se creará un objeto SolicmingMappingInfo en función de la anotación de la clase. Luego, se devuelve el objeto requestMappingInfo en el método de fusión, y finalmente se devuelve el objeto fusionado. Ahora, mirando hacia atrás en el método DetThandLermethods, hay dos llamadas al método GetMappingFormethod. Personalmente, creo que esto puede optimizarse. Al juzgar si el método es un manejador en primer lugar, el objeto SolicingMappingInfo creado se puede guardar y usar directamente más tarde, lo que significa que falta el proceso de crear un objeto SolicmingMappingInfo. Luego ingrese inmediatamente el método de registrohandlermehtod, de la siguiente manera
Protegido void registreHandlermethod (manejador de objetos, método del método, mapeo t) {// Crear handlermethod handlermethod newHandlermethod = createHandLermethod (Handler, Method); Handlermethod OldHandlermethod = handlermethods.get (mapeo); // Verifique si hay ambigüedad en la configuración if (OldHandlermethod! = NULL &&! OldHandlermethod.equals (newHandlermethod)) {throLe New IllegalStateException ("Mapeo ambigüedad OldHandlermethod.getBean () + "'Método de bean/n" + OldHandlermethod + "Mapeado."); } this.handlermethods.put (mapeo, newhandlermethod); if (logger.isinfoEnabled ()) {logger.info ("mapped /" " + mapping +" /"en" + newhandlermethod); } // Obtenga el valor de la anotación @RequestMapping, y luego agregue el valor-> RequestMappingInfo Mapping Registro al conjunto URLMAP SET <String> Patterns = GetMappingPathPattern (asignación); for (patrón de cadena: patrones) {if (! getPathMatcher (). isPattern (patrón)) {this.urlmap.add (patrón, mapeo); }}} Aquí el tipo de T es requestMappingInfo. Este objeto es la información relevante de la anotación de mapas de solicitudes del método bajo el controlador específico encapsulado. Una anotación de mapeo de solicitud corresponde a un objeto SolicmingMappingInfo. Handlermethod es similar al requestmappingInfo, y es una encapsulación de métodos de procesamiento específicos en ControlElr. Primero mire la primera línea del método y cree un objeto Handlermethod basado en Handler y Mehthod. La segunda línea utiliza el mapa Handlermethods para obtener el metro de mano correspondiente al mapeo de corriente. Luego determine si existe la misma configuración de mapeo de solicitudes. La siguiente configuración causará el lanzado aquí
Invocation of init method failed; nested exception is java.lang.IllegalStateException: Ambiguous mapping found. Cannot map...
anormal
@Controlador @requestmapping ("/ambiguausTest") clase pública ambiguousTestController {@RequestMapping (valor = "/test1") @ResponseBody public String test1 () {return "Method test1"; } @RequestMapping (valor = "/test1") @ResponseBody public String test2 () {return "Method test2"; }} Compruebe si la configuración de solicitud de solicitud es ambigüedad durante la fase SpringMVC Startup (Inicialización), que es una de las cosas para verificar la ambigüedad (un lugar para verificar la ambigüedad en el tiempo de ejecución se mencionará más adelante). Luego, después de confirmar que la configuración es normal, los objetos de SolicSmappingInfo y Handlermethod se agregarán a Handlermethods (LinkedHashMap), y luego se agregarán el valor anotado de SolicSmapping y los objetos ReuqestMappingInfo.
Resumen simple del método de RegistroHandlermethod
Hay tres responsabilidades principales de este método
1. Compruebe si la configuración de anotación de mapeo de solicitudes es ambigüedad.
2. Cree un mapa de requestmappinginfo para handlermethod. Este mapa es el miembro variable variable de los retratos de abstractthandlermethodmapping. Linkedhashmap.
3. Construya el mapa de URL de la variable miembro de AbstractthandLermethodmapping y multivaluMap. Esta estructura de datos puede entenderse como MAP>. La tecla de tipo de cadena almacena el valor de la anotación de mapeo de solicitudes en el método de procesamiento. Es el URI específico
Primero está el siguiente controlador
@Controlador @requestmapping ("/urlmap") public class UrlMapController {@RequestMapping (valor = "/test1", método = requestmethod.get) @ResponseBody public String test1 () {return "Method test1"; } @RequestMapping (valor = "/test1") @ResponseBody public String test2 () {return "Method test2"; } @RequestMapping (valor = "/test3") @ResponseBody public String test3 () {return "Method test3"; }} Después de completar la inicialización, la estructura del mapa de URL correspondiente a abstractthandlermethodmapping es la siguiente
Lo anterior es el proceso principal de inicialización de SpringMVC
Proceso de búsqueda
Para comprender el proceso de búsqueda, con una pregunta, los siguientes controladores están disponibles
@Controlador @requestmapping ("/lookuptest") clase pública LookUptestController {@RequestMapping (valor = "/test1", método = requestmethod.get) @ResponseBody public String test1 () {return "Method test1"; } @RequestMapping (valor = "/test1", encabezados = "referente = https: //www.baidu.com") @ResponseBody public String test2 () {return "Method test2"; } @RequestMapping (valor = "/test1", params = "id = 1") @ResponseBody public String test3 () {return "Method test3"; } @RequestMapping (valor = "/*") @ResponseBody public String test4 () {return "Method test4"; }} Hay solicitudes de la siguiente manera
¿Qué método ingresará esta solicitud?
Después de recibir la solicitud, el contenedor web (Tomcat, Jetty) se entrega al despachador para el procesamiento. FrameWorkServlet llama al método de solicitud correspondiente (por ejemplo: obtener llamadas doget) y luego llama al método ProcessRequest. Después de ingresar el método ProcessRequest, después de una serie de procesamiento, ingrese el método de doservicio en la línea: 936. Luego ingrese el método Dodispatch en Line856. Obtenga el controlador de procesadores solicitado actualmente en línea: 896. Luego ingrese el método LookupHandlermethod de abstractthandlermethodmapping. El código es el siguiente
Handlermethod protegido LookupHandLermethod (String LookUppath, HttpServletRequest solicitud) lanza la excepción {list <catch> coincidentes = new ArrayList <Comate> (); // Obtenga directPathMatches basados en la lista de URI <T> directPathMatches = this.urlmap.get (LookUppath); if (directPathMatches! = null) {addMatchingMappings (directPathMatches, coincides, solicitud); } // No hay solicitud de coincidencia directa ,MappingInfo, itera a través de todos los requestmappingInfo if (coincidir.isEmpty ()) {// no hay más remedio que pasar por todas las asignaciones addMatchingMappings (this.handlermethods.KeySet (), Matches, solicitud); } // Obtenga el Handlermethod correspondiente al mejor Match SolicSMappingInfo if (! Matches.IsEmpty ()) {Comparator <Scatch> comparator = new MatchComparator (GetMappingComparator (request)); Colección.sort (coincidencias, comparador); if (logger.istraceEnabled ()) {logger.trace ("encontrado" + match.size () + "mapeo (s) de coincidencia para [" + lookuppath + "]:" + coincidentes); } // Verifique la ambigüedad de la configuración nuevamente Mate bestMatch = Matches.get (0); if (coincidir.size ()> 1) {Match SecondBestMatch = Matches.get (1); if (comparator.compare (bestMatch, SecondBestMatch) == 0) {método m1 = bestmatch.handlermethod.getMethod (); Método m2 = SecondBestMatch.Handlermethod.getMethod (); Agregue nuevo IlegalStateException ("Métodos de controlador ambiguo mapeados para la ruta http '" + request.getRequestUrl () + "': {" + m1 + "," + m2 + "}"); }} handlematch (bestmatch.mapping, lookuppath, solicitud); return bestmatch.handlermethod; } else {return handlenomatch (handlermethods.keySet (), lookUppath, solicitud); }} Ingrese el método de BearupHandlermethod, donde lookuppath = "/lookuptest/test1", de acuerdo con lookuppath, es decir, el URI solicitado. Encuentre directamente el URLMAP y obtenga la lista de SolicSMappingInfo que coincida directamente. Aquí coincidiremos con 3 solicitudes de MappingInfos. como sigue
Luego ingrese el método AddMatchingMappings
Private void addMatchingMappings (Mapeaciones de colección <t>, listado <Catch>, solicitud de httpservletRequest) {for (t asignación: asignaciones) {t coincy = getMatchingMapping (mapeo, solicitud); if (match! = null) {Matches.Add (nuevo Match (Match, Handlermethods.get (mapeo))); }}} La responsabilidad de este método es atravesar si la URI actual solicitada y el SolicSmappingInfo en las asignaciones puede coincidir, y de ser así, crear el mismo objeto SolicSmappingInfo. Luego obtenga el metro manual correspondiente a requestMappingInfo. Luego cree un objeto de coincidencia y agréguelo a la lista de partidos. Después de ejecutar el método AddMatchingMappings, vuelva a BearupHandlermethod. En este momento, los partidos todavía tienen 3 objetos de solicitud de MappingInfo que pueden coincidir. El siguiente proceso es ordenar la lista de Matchers y luego obtener el primer elemento de la lista como la mejor coincidencia. Devuelve el metro de handlermethod del partido. Aquí ingresamos el método Compareto de requestMappingInfo y echamos un vistazo a la lógica de clasificación específica. El código es el siguiente
Public int Compareto (requestMappingInfo Other, httpservletRequest solicitud) {int resultado = PatternScondition.compareto (OTRO.GETPTERTNSCONDITION (), request); if (resultado! = 0) {Resultado de retorno; } resultado = paramScondition.compareto (otro.getParamScondition (), request); if (resultado! = 0) {Resultado de retorno; } resultado = HeadersCondition.Compareto (OTRO.GETHEADERSCONDITION (), solicitud); if (resultado! = 0) {Resultado de retorno; } resultado = ConsumerCondition.Compareto (OTRO.GetConsumeScondition (), solicitud); if (resultado! = 0) {Resultado de retorno; } resultado = produceScondition.compareto (otro.getProduceCondition (), request); if (resultado! = 0) {Resultado de retorno; } result = MethodsCondition.Compareto (OTRO.GETMETHODSCONDITION (), solicitud); if (resultado! = 0) {Resultado de retorno; } resultado = CustomConditionHolder.Compareto (OTRO.CUSTOMCONDITIONHENTER, Solicitar); if (resultado! = 0) {Resultado de retorno; } return 0;} Como puede ver en el código, el orden de las coincidencias es valor> params> encabezados> consumes> produce> métodos> costumbre. Al ver esto, la pregunta anterior se puede responder fácilmente. En el caso del mismo valor, los parámetros pueden coincidir primero. Para que esa solicitud ingrese el método test3 (). Regrese a LookupHandlermethod y encuentre handlermethod. SpringMVC verificará la ambigüedad de la configuración nuevamente aquí. El principio del cheque aquí es comparar las dos solicitudes de solicitudes con el más alto grado de coincidencia. Puede haber preguntas aquí que al inicializar SpringMVC, hay una verificación de configuración ambigua, ¿por qué se revisa nuevamente aquí? Si ahora hay dos métodos en el controlador, la siguiente configuración se puede verificar a través de la ambigüedad de inicialización.
@RequestMapping (value = "/test5", método = {requestMethod.get, requestmethod.post})@respuestas ebloyPublic string test5 () {return "método test5";}@requestmapping (value = "/test5", método = {requestMethod.get, requestmethod.delete})@ResponseBodyPublic. Ahora ejecute la solicitud http: // localhost: 8080/springmvc-demo/lookuptest/test5, y se lanzará en el método de BearupHandlermethod
java.lang.IllegalStateException: Ambiguous handler methods mapped for HTTP path 'http://localhost:8080/SpringMVC-Demo/LookupTest/test5' excepción. La excepción se lanza aquí porque el método Compareto de requestMethodsRequestCondition es el número de método de comparación. El código es el siguiente
Public int Compareto (requestMethodsRequestCondition Other, httpservletRequest solicitud) {return other.methods.size () - this.methods.size ();}¿Cuándo coincide con los comodines? Cuando el requestmappingInfo que coincide directamente con el valor no se puede obtener a través de URLMAP, se utilizará la coincidencia de comodines para ingresar el método AddMatchingMappings.
Resumir
Lo anterior es todo el contenido de este artículo. Espero que el contenido de este artículo tenga cierto valor de referencia para el estudio o el trabajo de todos. Si tiene alguna pregunta, puede dejar un mensaje para comunicarse. Gracias por su apoyo a Wulin.com.