0 Resumo
Este artigo explica brevemente o link de mapeamento do processador do SpringMVC do nível do código -fonte, ou seja, o processo detalhado de encontrar o controlador
1 Processo de solicitação Springmvc
Controlador procura as etapas 1 a 2 correspondentes à imagem acima
Fluxograma de operação detalhada da Springmvc
2 Processo de inicialização do Springmvc
2.1 Primeiro compreenda duas categorias
1.RequestMappingInfo
Anotação do encapsulamento Solicitação
Contém informações relevantes sobre os cabeçalhos de solicitação HTTP
Uma instância corresponde a uma anotação de solicitação
2.Handlermethod
Método de manuseio de solicitações para encapsular controlador
Inclui o objeto de feijão ao qual o método pertence, o objeto de método correspondente ao método, os parâmetros do método etc.
RELAÇÃO DE HERIDADEÇÃO DE PEITMINGMAPINGHANDLERMAPPAP
Durante a inicialização do SpringMVC
Primeiro execute o pós -propertiesset de requestmappinghaplaping
Em seguida, digite o abstingo após optoppertiesset
Este método entrará nos initários e nos votos desta classe
Responsável pela digitalização de feijões do ApplicationContext e, em seguida, encontrando e registrando métodos de processador a partir de feijões
// verifica os grãos no aplicativoContext, detectar e registrar o manipulador. getApplicationContext (). getBeanNamesfortype (object.class)); // Transfira a matriz Beanname para (String Beanname: BeanNames) {// ishandler julgará se a definição do feijão contém anotação do controlador ou anotação de requestmaping com base no feijão. If (iSHandler (getApplicationContext (). GetType (beanname))) {detecthandlermethods (beanname); }} handlermethodsinitialized (gethandlermethods ());} RequestMappingHandlerMapping#ISHandler
O método acima é determinar se a definição atual do feijão contém anotação do controlador ou anotação de solicitação
Se apenas o requestMapping entrar em vigor? Não!
Porque, neste caso, a classe não será registrada como um feijão de mola ao inicializar a primavera, e a classe não será percorrida ao atravessar os nomes de feijão, por isso não há problema em substituir o controlador por composição aqui, mas isso não é feito em geral
Depois de confirmar que o feijão é um manipulador, o método do manipulador específico será encontrado no feijão (ou seja, o método de processamento de solicitação definido na classe do controlador). O código de pesquisa é o seguinte
/** * Procure métodos manipuladores em um manipulador * @param manipulador o nome do feijão de um manipulador ou uma instância do manipulador */Protected void DetectHandlermethods (Final Object Handler) {// Obtenha o objeto de classe do controlador atual BeanClasl <? getApplicationContext (). GetType ((String) Handler): Handler.getClass (); // Evite chamadas repetidas para getMappformEthodod para reconstruir o mapa final do requestMappingInfo. // O mesmo que acima, também é o objeto de classe da classe final do Bean <?> UserType = classutils.getUserClass (HandlerType); // Obter todos os métodos manipuladores do feijão atual // Defina se ele tem solicitação de acordo com o método // se houver, crie um requestMappingInfo Set instância <odhod> methods = handlermethodSelector.SelectMethods (UserType, new MethodFilter () @Override BOOLEAN (Método) {MAGH) (mapeamento! = nulo) {mappings.put (método, mapeamento); // viaje e registre todo o método manipulador do feijão atual para (método do método: métodos) {// Registre o método Handler e insira o seguinte método RegisterHandLermethod (Handler, Method, mappings.get (Method)); } Existem dois lugares no código acima que chamam getMappFormethod
Crie requestmappingInfo usando o método e o tipo de anotações de requestmapping anotação
@Override Protected RequestMappingInfo getMappingFormethod (método do método, classe <?> Handlertype) {requestMappinfo info = null; // obtenha o @requestMapping requestMapping Metodannotation = anoTationutils.findannotation (método, requestmapping.class); if (Methodannotation! = NULL) {requestCondition <?> Methodcondition = getCustommethodCondition (Method); info = createrequestmappingInfo (Methodannotation, MethodCondition); // Obtenha a anotação @ReQUtestMapping do feijão ao qual o método pertence a requestMapping typeannotation = anoTationUtils.findannotation (HandlerType, requestmapp.class); if (TypeanNotation! = null) {requestCondition <?> tyseCondition = getCustomTyPecondição (HandlerType); // Mesclar duas anotações @ReQuestMapping Info = CreaterEquestMappingInfo (NOTAÇÃO DE TEMPO, TYPECONDIÇÃO) .Combine (Info); }} retornar informações; } O objetivo deste método é criar um objeto RequestMappingInfo com base no método do manipulador. Primeiro, determine se o mehtod contém a anotação RequestMpping. Nesse caso, crie um objeto RequestMappingInfo diretamente com base no conteúdo da anotação. Após a criação, determine se o feijão para o qual o método atual também contém a anotação do RequestMapping. Se esta anotação estiver incluída, um objeto RequestMappinfo será criado com base na anotação da classe. Em seguida, o objeto RequestMappingInfo no método de mesclagem é retornado e o objeto mesclado é finalmente retornado. Agora, olhando para o método DetectHandlermethods, existem duas chamadas para o método GetMappingFormethod. Pessoalmente, acho que isso pode ser otimizado. Ao julgar se o método é um manipulador em primeiro lugar, o objeto RequestMappinfo criado pode ser salvo e usado diretamente posteriormente, o que significa que o processo de criação de um objeto RequestMappingInfo está faltando. Em seguida, insira imediatamente o método RegisterHandlerMehtod, como segue
Void Protected RegisterHandLermethod (manipulador de objetos, método do método, t mapeamento) {// Crie handlermethod handlermethod newHandlermethod = createHandlermethod (manipulador, método); Handlermethod antigoHandlermethod = handlermethods.get (mapeamento); // Verifique se há ambiguidade na configuração se (antigoHandlerMethod! = NULL &&! OldHandlermethod.Equals (newhandlermethod)) {lança new ilegalStateException ("mapeamento ambíguo encontrado. + OldHandlermethod.getBean () + "'Método Bean/N" + OldHandlermethod + "mapeado."); } this.Handlermethods.put (mapeamento, newhandlermethod); if (logger.isinfoEnabled ()) {Logger.info ("mapeado /" " + mapeamento +" /"para" + newhandlermethod); } // Obtenha o valor da anotação @ReQuestMapping e adicione o valor de mapeamento de valor-> requestMappInfo no URLMAP Set <String> padrões = getMappathPattern (mapeamento); para (padrão da string: padrões) {if (! getPathMatcher (). isPattern (padrony)) {this.urlmap.add (padrão, mapeamento); }}} Aqui, o tipo de t é requestmapinginfo. Este objeto é a informação relevante da anotação RequestMapping do método sob o controlador específico encapsulado. Uma anotação de solicitação corresponde a um objeto RequestMappinfo. Handlermethod é semelhante ao requestMappInfo e é um encapsulamento de métodos de processamento específicos no ControlElr. Primeiro, olhe para a primeira linha do método e crie um objeto Handlermethod baseado no manipulador e mehthod. A segunda linha usa o mapa Handlermethods para obter o handlermethod correspondente ao mapeamento atual. Em seguida, determine se existe a mesma configuração de requestMapping. A configuração a seguir causará os jogados aqui
Invocation of init method failed; nested exception is java.lang.IllegalStateException: Ambiguous mapping found. Cannot map...
anormal
@Controlador @requestmapping ("/ambiguousTest") classe pública AmbiguousTestController {@RequestMapping (value = "/test1") @ResponseBody public String test1 () {return "Method test1"; } @RequestMapping (value = "/test1") @ResponseBody public String test2 () {return "Method test2"; }} Verifique se a configuração do RequestMapping é a ambiguidade durante a fase SpringMVC Startup (Inicialização), que é uma das coisas a verificar a ambiguidade (um local para verificar a ambiguidade no tempo de execução será mencionado posteriormente). Então, após confirmar que a configuração é normal, os objetos RequestMappinfo e Handlermethod serão adicionados aos handlermethods (LinkedHashmap) e, em seguida, o Valor Anotado RequestMaging e os objetos reuqestMappingInfo serão adicionados ao URLMAP.
Resumo simples do método RegisterHandlerMethod
Existem três responsabilidades principais deste método
1. Verifique se a configuração de anotação do RequestMapping é ambiguidade.
2. Construa um mapa de requestMappinfo para Handlermethod. Este mapa é o membro variável Handlermethods de abstrachandlermethodMapping. LinkedHashmap.
3. Construa o URLMAP variável do membro de abstrachandlermethodmapping e multivaluemap. Essa estrutura de dados pode ser entendida como mapa>. A tecla String Type armazena o valor da anotação RequestMapping no método de processamento. É o Uri específico
Primeiro, há o seguinte controlador
@Controlador @requestmapping ("/urlmap") classe pública urlmapController {@RequestMapping (value = "/test1", método = requestMethod.get) @ResponseBody public String test1 () {return "método test1"; } @RequestMapping (value = "/test1") @ResponseBody public String test2 () {return "Method test2"; } @RequestMapping (value = "/test3") @ResponseBody public String test3 () {return "Method test3"; }} Após a conclusão da inicialização, a estrutura do URLMAP correspondente a abstrair
O acima é o principal processo de inicialização do Springmvc
Processo de pesquisa
Para entender o processo de pesquisa, com uma pergunta, os seguintes controladores estão disponíveis
@Controlador @requestmapping ("/lookuptest") classe pública LookUpTestController {@RequestMapping (value = "/test1", método = requestMethod.get) @ResponseBody public string test1 () {return "método test1"; } @RequestMapping (value = "/test1", headers = "referente = https: //www.baidu.com") @ResponseBody public String test2 () {return "Method test2"; } @RequestMapping (value = "/test1", params = "id = 1") @ResponseBody public string test3 () {return "método test3"; } @RequestMapping (value = "/*") @ResponseBody public String test4 () {return "Method test4"; }} Existem solicitações da seguinte maneira
Qual método esse solicitação entrará?
Depois de receber a solicitação, o contêiner da web (Tomcat, Jetty) é entregue ao DispatcheServlet para processamento. O ScrameworkServlet chama o método de solicitação correspondente (por exemplo: Obtenha chamadas DOGET) e, em seguida, chama o método ProcessRequest. Depois de inserir o método ProcessRequest, após uma série de processamento, insira o método doservice na linha: 936. Em seguida, digite o método Dodispatch na linha856. Obtenha o manipulador de processadores atualmente solicitado na linha: 896. Em seguida, digite o método LookUpHandlerMethod de abstração de abstenção de alméias. O código é o seguinte
handlermethod protegido LookUpHandLermethod (String lookuppath, httpServletRequest request) lança Exceção {list <chance> Matches = new ArrayList <Match> (); // Obtenha o DirectPathMatches com base na lista URI <T> DirectPathMatches = this.urlmap.get (lookuppath); if (DirectPathMatches! = null) {addMatchingMappings (DirectPathMatches, correspondências, solicitação); } // Não existe um requestMappingInfo correspondente direto, iterado por todo o requestMappingInfo if (matches.isEmpty ()) {// sem escolha a não ser passar por todos os mapeamentos addmatchingMappings (this.handlermethods.keyset (), correspondências, solicitação); } // Obtenha o handlermethod correspondente ao melhor Match RequestMappingInfo if (! Matches.isEmpty ()) {comparador <chance> comparador = new MatchComParator (getMappComParator (request)); Coleções.sort (fósforos, comparador); if (logger.istraceEnabled ()) {Logger.Trace ("Found" + Matches.size () + "MAPPING (S) correspondente (s) para [" + lookuppath + "]:" + corresponde); } // Verifique a ambiguidade da configuração novamente corresponde a BestMatch = Matches.get (0); if (matches.size ()> 1) {Match SecondBestMatch = Matches.get (1); if (comparator.comPare (BestMatch, segundoBestMatch) == 0) {Método M1 = BestMatch.Handlermethod.getMethod (); Método M2 = SecondBestMatch.Handlermethod.getMethod (); lançar novos ilegalstateException ("Métodos de manipuladores ambíguos mapeados para o caminho http '" + request.getRequesturl () + "': {" + m1 + "," + m2 + "}"); }} handleMatch (bestmatch.mapping, lookuppath, solicitação); return bestmatch.handlermethod; } else {return handlenomatch (handlermethods.keyyset (), lookuppath, solicitação); }} Digite o método LookUpHandlerMethod, onde Lookuppath = "/LookupTest/Test1", de acordo com Lookuppath, ou seja, o URI solicitado. Encontre diretamente o URLMAP e obtenha a lista RequestMappingInfo que corresponde diretamente. Aqui vamos corresponder a 3 requestMappingInfos. do seguinte modo
Em seguida, insira o método AddMatchingMappings
private void addmatchingMappings (coleção <t> mapeamentos, list <fósforo> correspondências, httpServletRequest Request) {para (maping t: mappings) {t corresponde = getMatchingMapping (mapeamento, solicitação); if (corresponde! = null) {matches.add (new Match (correspondência, handlermethods.get (mapeamento))); }}} A responsabilidade desse método é atravessar se o URI atual solicitado e o requestMappingInfo nos mapeamentos podem corresponder e, se assim for, criar o mesmo objeto RequestMappingInfo. Em seguida, obtenha o HandLermethod que corresponde ao requestMappinfo. Em seguida, crie um objeto de correspondência e adicione -o à lista de correspondências. Depois de executar o método AddMatchingMappings, retorne ao LookUpHandLermethod. No momento, as correspondências ainda têm 3 objetos de requestMappingInfo que podem corresponder. O próximo processo é classificar a lista Matchers e, em seguida, obter o primeiro elemento da lista como a melhor correspondência. Retorna o Handlermethodod of Match. Aqui, inserimos o método compareto de requestmappingInfo e damos uma olhada na lógica de classificação específica. O código é o seguinte
public int compareto (requestMappingInfo Outros, httpServletRequest request) {int resultado = padronizcondition.compareto (outros.getPatterNScondition (), request); if (resultado! = 0) {return resultado; } resultado = paramscondition.compareto (outros.getParamSoScondition (), solicitação); if (resultado! = 0) {return resultado; } resultado = headercondition.compareto (outros.getheaderScondition (), solicitação); if (resultado! = 0) {return resultado; } resultado = consumerCondition.compareto (outros.getConsumescondition (), solicitação); if (resultado! = 0) {return resultado; } resultado = Productescondition.compareto (outros.getProduCescondition (), solicitação); if (resultado! = 0) {return resultado; } resultado = methodscondition.compareto (outros.getMethodSoScondition (), solicitação); if (resultado! = 0) {return resultado; } resultado = customConditionHolder.compareto (outros.customconditionholder, solicitação); if (resultado! = 0) {return resultado; } retornar 0;} Como você pode ver no código, a ordem das correspondências é valor> params> cabeçalhos> consumes> produz> métodos> personalizados. Vendo isso, a pergunta anterior pode ser facilmente respondida. No caso do mesmo valor, os parâmetros podem corresponder primeiro. Para que essa solicitação insira o método test3 (). Volte para LookUpHandlermethod e encontre o Handlermethod. A SpringMVC verificará a ambiguidade da configuração novamente aqui. O princípio do cheque aqui é comparar os dois requestmapingInfos com o maior grau de correspondência. Pode haver perguntas aqui que, ao inicializar o SpringMVC, há uma verificação ambígua de configuração, por que ela é verificada novamente aqui? Se houver dois métodos no controlador agora, a seguinte configuração poderá ser verificada através da ambiguidade de inicialização.
@RequestMapping (value = "/test5", método = {requestMethod.get, requestMethod.post})@ResponsbodyPublic String test5 () {return "Método Test5";}@requestMapp (value = "/test5", método = {requestmethod.get, requestmethod.dElete}) Agora execute o http: // localhost: 8080/springmvc-demo/lookupstest/test5 solicitação, e será jogado no método LookUpHandlerMethod
java.lang.IllegalStateException: Ambiguous handler methods mapped for HTTP path 'http://localhost:8080/SpringMVC-Demo/LookupTest/test5' exceção. A exceção é lançada aqui porque o método comparado de requestMethodsRequestCondition é o número do método de comparação. O código é o seguinte
public int Compareto (requestMethodsRequestCondition Outro, httpServletRequest Request) {return Other.methods.size () - this.methods.size ();}Quando o curinga corresponde? Quando o requestMappingInfo que corresponde diretamente ao valor não pode ser obtido através do URLMAP, a correspondência curinga será usada para inserir o método AddMatchingMappings.
Resumir
O acima é o conteúdo inteiro deste artigo. Espero que o conteúdo deste artigo tenha certo valor de referência para o estudo ou trabalho de todos. Se você tiver alguma dúvida, pode deixar uma mensagem para se comunicar. Obrigado pelo seu apoio ao wulin.com.