Introduzione
Questo è uno sguardo approfondito alle potenti funzionalità e al funzionamento interno di Spring Web MVC, che fa parte del framework Spring.
Il codice sorgente di questo articolo è disponibile su GitHub.
Configurazione del progetto
In questo articolo, useremo l’ultimo e più grande Framework Spring 5., Ci stiamo concentrando qui sul classico stack web di Spring, che è stato disponibile fin dalle prime versioni del framework ed è ancora il modo principale di creare applicazioni web con Spring.
Per cominciare, per impostare il tuo progetto di test, userai Spring Boot e alcune delle sue dipendenze di avviamento; dovrai anche definire il genitore:
Nota che, per usare Spring 5, devi anche usare Spring Boot 2.x. Al momento della scrittura, questa è una versione milestone, disponibile nel repository Spring Milestone., Aggiungiamo questo repository al tuo progetto Maven:
Puoi controllare la versione corrente di Spring Boot su Maven Central.
Progetto di esempio
Per capire come funziona Spring Web MVC, implementerai una semplice applicazione con una pagina di accesso. Per mostrare la pagina di accesso, creare una classe @ Controller-annotated InternalController con una mappatura GET per la radice del contesto.
Il metodo hello() è senza parametri. Restituisce una stringa che viene interpretata da Spring MVC come un nome di vista (nel nostro caso, il login.,html template):
import org.springframework.web.bind.annotation.GetMapping;@GetMapping("/")public String hello() { return "login";}
Per elaborare un login utente, creare un altro metodo che gestisce le richieste POST con i dati di accesso. Quindi reindirizza l’utente alla pagina successo o fallimento, a seconda del risultato.,
si noti che il login() metodo riceve un oggetto di dominio come argomento e restituisce un ModelAndView oggetto:
ModelAndView è titolare di due oggetti distinti:
- Modello a chiave-valore mappa di dati utilizzati per il rendering della pagina
- Visualizzazione – un modello di pagina, che viene riempito con i dati del modello
Questi sono entrato per la comodità, in modo che il metodo di controller in grado di riportare entrambi contemporaneamente.
Per rendere la tua pagina HTML, usa Thymeleaf come motore di template di visualizzazione, che ha un’integrazione solida e pronta all’uso con Spring.,
Servlet come fondamento di un’applicazione Web Java
Quindi, cosa succede effettivamente quando si digita http://localhost:8080/ nel browser, premere Invio e la richiesta colpisce il server Web? Come si ottiene da questa richiesta di vedere un modulo web nel browser?
Dato che il progetto è una semplice applicazione di avvio a molla, sarai in grado di eseguirlo tramite Spring5Application.
Spring Boot utilizza Apache Tomcat per impostazione predefinita., Quindi, eseguendo l’applicazione, è probabile che si vedano le seguenti informazioni nel registro:
Poiché Tomcat è un contenitore Servlet, naturalmente ogni richiesta HTTP inviata a un server Web Tomcat viene elaborata da un servlet Java. Quindi il punto di ingresso dell’applicazione Web Spring è, non a caso, un servlet.
Un servlet è, in poche parole, un componente fondamentale di qualsiasi applicazione web Java; è di basso livello e non impone troppo in termini di modelli di programmazione specifici, come MVC.
Un servlet HTTP può solo ricevere una richiesta HTTP, elaborarla in qualche modo e inviare una risposta.,
E, a partire dall’API Servlet 3.0, è ora possibile andare oltre la configurazione XML e iniziare a sfruttare la configurazione Java (con restrizioni minori).,
DispatcherServlet come il Cuore di Spring MVC
Ciò che vogliamo davvero fare come gli sviluppatori di una applicazione web è distogliere la seguente noioso e standard di attività e di concentrarsi sugli utili la logica di business:
- mapping di una richiesta HTTP ad un certo metodo di elaborazione
- analisi dei dati di richiesta HTTP e le intestazioni in oggetti di trasferimento dati (DTOs) o oggetti di dominio
- model-view-controller interazione
- generazione di risposte di DTOs, oggetti di dominio, etc.
Il DispatcherServlet di primavera fornisce esattamente questo., È il cuore del framework Spring Web MVC; questo componente principale riceve tutte le richieste alla tua applicazione.
Come vedrai, DispatcherServlet è molto estensibile.,placcatura motori, XML, XSLT o qualsiasi altra tecnologia di visualizzazione (implementazioni di ViewResolver interfaccia)
l’Elaborazione di una Richiesta HTTP
Prima di tutto, analizziamo il trattamento dei semplici richieste HTTP a un metodo, in un controllore di livello e tornare al browser/client.,
Il DispatcherServlet ha una lunga gerarchia di ereditarietà; vale la pena comprendere questi singoli aspetti uno per uno, dall’alto verso il basso. Le modalità di elaborazione delle richieste ci interesseranno di più.
Comprendere la richiesta HTTP, sia localmente durante lo sviluppo standard, sia in remoto, è una parte fondamentale della comprensione dell’architettura MVC.
GenericServlet
GenericServlet è una parte della specifica Servlet non direttamente focalizzata su HTTP. Definisce il metodo service () che riceve le richieste in arrivo e produce risposte.,
Nota come gli argomenti del metodo ServletRequest e ServletResponse non sono legati al protocollo HTTP:
public abstract void service(ServletRequest req, ServletResponse res) throws ServletException, IOException;
Questo è il metodo che alla fine viene chiamato su qualsiasi richiesta al server, inclusa una semplice richiesta GET.
HttpServlet
La classe HttpServlet è, come suggerisce il nome, l’implementazione Servlet focalizzata su HTTP, definita anche dalla specifica.,
In termini più pratici, HttpServlet è una classe astratta con un’implementazione del metodo service() che divide le richieste in base al tipo di metodo HTTP e assomiglia più o meno a questo:
HttpServletBean
Successivamente, HttpServletBean è la prima classe a conoscenza della molla nella gerarchia. Inietta le proprietà del bean utilizzando i valori servlet init-param ricevuti dal web.xml o da WebApplicationInitializer.
In caso di richieste alla tua applicazione, i metodi doGet(), doPost(), ecc.,
FrameworkServlet
FrameworkServlet integra la funzionalità Servlet con un contesto di applicazione Web, implementando l’interfaccia ApplicationContextAware. Ma è anche in grado di creare un contesto di applicazione web da solo.
Come hai già visto, la superclasse HttpServletBean inietta init-params come proprietà bean. Quindi, se viene fornito un nome di classe di contesto nel contextClass init-param del servlet, verrà creata un’istanza di questa classe come contesto dell’applicazione. In caso contrario, verrà utilizzata una classe XmlWebApplicationContext predefinita.,
Poiché la configurazione XML è fuori moda al giorno d’oggi, Spring Boot configura DispatcherServlet con AnnotationConfigWebApplicationContext per impostazione predefinita. Ma si potrebbe cambiare facilmente.
Ad esempio, se è necessario configurare l’applicazione Spring Web MVC con un contesto applicativo basato su Groovy, è possibile utilizzare la seguente configurazione di DispatcherServlet nel Web.file xml:
dispatcherServlet org.springframework.web.servlet.DispatcherServlet contextClass org.springframework.web.context.support.GroovyWebApplicationContext
La stessa configurazione può essere eseguita in un modo più moderno basato su Java utilizzando la classe WebApplicationInitializer.,
DispatcherServlet: unificare l’elaborazione della richiesta
HttpServlet.l’implementazione di service (), che instrada le richieste in base al tipo di verbo HTTP, ha perfettamente senso nel contesto dei servlet di basso livello. Tuttavia, a livello di astrazione Spring MVC, il tipo di metodo è solo uno dei parametri che possono essere utilizzati per mappare la richiesta al suo gestore.,
E così, la principale funzione del FrameworkServlet classe è quello di unire la logica di gestione in una singola metodo processRequest (), che a sua volta chiama il doService() metodo:
@Overrideprotected final void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response);}@Overrideprotected final void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response);}// …
DispatcherServlet: Arricchire la Richiesta
Infine, la DispatcherServlet implementa il doService() metodo. Qui, aggiunge alla richiesta alcuni oggetti utili che possono tornare utili nella pipeline di elaborazione: contesto dell’applicazione Web, resolver locale, resolver tema, fonte tema ecc.:
Inoltre, il metodo doService () prepara le mappe flash di input e output., Flash map è fondamentalmente un modello per passare i parametri da una richiesta all’altra richiesta che segue immediatamente. Questo può essere molto utile durante i reindirizzamenti (come mostrare all’utente un messaggio informativo one-shot dopo il reindirizzamento):
Quindi, il metodo doService () chiama il metodo doDispatch () che è responsabile dell’invio delle richieste.
DispatcherServlet: Invio della richiesta
Lo scopo principale del metodo dispatch() è trovare un gestore appropriato per la richiesta e alimentarlo con i parametri di richiesta / risposta., Il gestore è fondamentalmente qualsiasi tipo di oggetto e non è limitato a un’interfaccia specifica. Ciò significa anche che Spring ha bisogno di trovare un adattatore per questo gestore che sappia come “parlare” con il gestore.
Per trovare il gestore che corrisponde alla richiesta, Spring passa attraverso le implementazioni registrate dell’interfaccia HandlerMapping. Ci sono molte implementazioni diverse che potrebbero soddisfare le tue esigenze.
SimpleUrlHandlerMapping consente di mappare una richiesta dal suo URL a un determinato bean di elaborazione. Ad esempio, può essere configurato iniettando la sua proprietà mappings con un java.util.,Istanza di proprietà simile a questa:
/welcome.html=ticketController/show.html=ticketController
Probabilmente la classe più utilizzata per il mapping del gestore è RequestMappingHandlerMapping, che associa una richiesta a un metodo annotato @RequestMapping di una classe @Controller. Questa è esattamente la mappatura che collega il dispatcher con i metodi hello() e login() del controller.
Nota che i tuoi metodi Spring-aware sono annotati con @GetMapping e @PostMapping in modo corrispondente. Queste annotazioni, a loro volta, sono contrassegnate con la meta-annotazione @RequestMapping.,
Il metodo dispatch() si occupa anche di alcuni altri HTTP compiti specifici:
- corto circuito di elaborazione della richiesta GET nel caso in cui la risorsa non è stata modificata
- applicare il multipart resolver per richieste corrispondenti
- corto circuito di elaborazione della richiesta se il gestore ha scelto di gestire in modo asincrono
la Gestione della Richiesta
Ora che la Primavera determinato gestore per la richiesta e l’adattatore per il gestore, è finalmente ora di gestire la richiesta. Ecco la firma del HandlerAdapter.metodo handle ()., È importante notare che il gestore ha una scelta su come gestire la richiesta:
- scrivere i dati sull’oggetto response da solo e restituire null
restituire un oggetto ModelAndView da rendere dal DispatcherServlet
@NullableModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
Ci sono diversi tipi di gestori forniti. Ecco come SimpleControllerHandlerAdapter elabora un’istanza del controller MVC Spring (non confonderla con un POJO annotato da @Controller).,
Si noti come il gestore del controller restituisce l’oggetto ModelAndView e non rende la vista da solo:
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { return ((Controller) handler).handleRequest(request, response);}
Il secondo è SimpleServletHandlerAdapter, che adatta un Servlet regolare come gestore di richieste.
Un Servlet non sa nulla di ModelAndView e gestisce semplicemente la richiesta da solo, rendendo il risultato nell’oggetto response., Quindi questo adattatore restituisce semplicemente null invece di ModelAndView:
Nel tuo caso, un controller è un POJO con diverse annotazioni @RequestMapping, quindi qualsiasi gestore è fondamentalmente un metodo di questa classe racchiuso in un’istanza HandlerMethod. Per adattarsi a questo tipo di gestore, Spring utilizza la classe RequestMappingHandlerAdapter.,
Argomenti di elaborazione e valori di ritorno dei metodi del gestore
Si noti che i metodi del controller di solito non prendono argomenti HttpServletRequest e HttpServletResponse, ma ricevono e restituiscono molti tipi diversi di dati, come oggetti di dominio, parametri di percorso ecc.
Inoltre, si noti che non è necessario restituire un’istanza ModelAndView da un metodo controller. È possibile restituire un nome di vista o una ResponseEntity o un POJO che verrà convertito in una risposta JSON, ecc.,
Il RequestMappingHandlerAdapter assicura che gli argomenti del metodo siano risolti da HttpServletRequest. Inoltre, crea l’oggetto ModelAndView dal valore restituito del metodo.
C’è un pezzo importante di codice nel RequestMappingHandlerAdapter che assicura che tutta questa magia di conversione abbia luogo:
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);if (this.argumentResolvers != null) { invocableMethod.setHandlerMethodArgumentResolvers( this.argumentResolvers);}if (this.returnValueHandlers != null) { invocableMethod.setHandlerMethodReturnValueHandlers( this.returnValueHandlers);}
L’oggetto argumentResolvers è un composto di diverse istanze HandlerMethodArgumentResolver.
Esistono oltre 30 diverse implementazioni di risolutori di argomenti., Consentono di estrarre qualsiasi tipo di informazione dalla richiesta e fornirla come argomenti del metodo. Ciò include le variabili del percorso URL, i parametri del corpo della richiesta, le intestazioni delle richieste, i cookie, i dati di sessione ecc.
L’oggetto returnValueHandlers è un composto di oggetti HandlerMethodReturnValueHandler. Ci sono anche molti gestori di valori diversi che possono elaborare il risultato del metodo per creare l’oggetto ModelAndView previsto dall’adattatore.
Ad esempio, quando si restituisce una stringa dal metodo hello (), ViewNameMethodReturnValueHandler elabora il valore., Ma quando si restituisce un ModelAndView pronto dal metodo login (), Spring utilizza ModelAndViewMethodReturnValueHandler.
Rendering della vista
Ormai, Spring ha elaborato la richiesta HTTP e ha ricevuto un oggetto ModelAndView, quindi deve rendere la pagina HTML che l’utente vedrà nel browser. Lo fa in base al modello e alla vista selezionata incapsulata nell’oggetto ModelAndView.
Si noti inoltre che è possibile eseguire il rendering di un oggetto JSON, o XML, o qualsiasi altro formato di dati che può essere trasferito tramite protocollo HTTP., Ne parleremo di più nella prossima sezione focalizzata sul RESTO qui.
Torniamo al DispatcherServlet. Il metodo render () imposta prima la localizzazione della risposta utilizzando l’istanza LocaleResolver fornita. Supponiamo che il tuo browser moderno imposta correttamente l’intestazione Accept e che AcceptHeaderLocaleResolver sia utilizzato per impostazione predefinita.
Durante il rendering, l’oggetto ModelAndView potrebbe già contenere un riferimento a una vista selezionata, o solo un nome di vista, o nulla se il controller si affidava a una vista predefinita.,
Poiché entrambi i metodi hello() e login() specificano la vista desiderata come nome stringa, deve essere cercata con questo nome. Quindi, questo è dove entra in gioco l’elenco viewResolvers:
Questo è un elenco di istanze ViewResolver, inclusa la nostra ThymeleafViewResolver fornita dalla libreria di integrazione thymeleaf-spring5. Questo risolutore sa dove cercare le viste e fornisce le istanze di visualizzazione corrispondenti.,
Dopo aver chiamato il metodo render() della vista, Spring completa finalmente l’elaborazione della richiesta inviando la pagina HTML al browser dell’utente:
Supporto REST
Oltre al tipico scenario MVC, possiamo anche utilizzare il framework per creare servizi web REST.
In poche parole, puoi accettare una Risorsa come input, specificare un POJO come argomento del metodo e annotarlo con @RequestBody., Puoi anche annotare il metodo stesso con @ ResponseBody per specificare che il suo risultato deve essere trasformato direttamente in una risposta HTTP:
Questo è possibile anche grazie all’estensibilità di Spring MVC.
Per marshall i DTO interni ad una rappresentazione REST, il framework fa uso dell’infrastruttura HttpMessageConverter. Ad esempio, una delle implementazioni è MappingJackson2HttpMessageConverter, che è in grado di convertire gli oggetti modello da e verso JSON utilizzando la libreria Jackson.,
E per semplificare ulteriormente la creazione di un’API REST, Spring introduce l’annotazione @RestController. Questo è utile per assumere la semantica @ ResponseBody per impostazione predefinita ed evitare di impostarla esplicitamente su ciascun controller REST:
Conclusione
In questo articolo, hai esaminato l’elaborazione di una richiesta nel framework Spring MVC in dettaglio. Hai visto come diverse estensioni del framework lavorano insieme per fornire tutta la magia e risparmiarti la necessità di gestire le parti difficili del protocollo HTTP.,
Migliora continuamente le tue applicazioni Java utilizzando Stackify Retrace, lo strumento di gestione delle prestazioni delle applicazioni per l’intero ciclo di vita. Provalo.