Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

Estendere Spring MVC

Link copiato negli appunti

Negli articoli precedenti abbiamo parlato più o meno approfonditamente di una serie di componenti che permettono di gestire al meglio le nostre applicazioni Web. Abbiamo introdotto concetti come i Controller, le Viste, gli Interceptor e gli Exception Handler. Questi strumenti sono spesso sufficenti per scrivere applicazioni secondo i più alti standard qualitativi.

Spring MVC però non finisce qua: grazie alla Dipendency Injection offerta da Spring Core possiamo estendere alcuni di questi componenti aggiungendo funzionalità e permettendoci di avere un codice ancor meglio organizzato. Le componenti che estenderemo in questo ultimo articolo sono i Controller e le Viste che senza dubbio rappresentano gli aspetti più importanti di tutto lo stack.

Gli HandlerMethodArgumentResolver

Tornando con la mente all'articolo sui Controller, abbiamo detto che essi presentano una signature dinamica che può essere personalizzata in base alle nostre esigenze. Questo significa che esiste un set di oggetti che Spring può mettere a disposizione semplicemente "leggendo" la signature che abbiamo definito nel nostro codice. Ecco alcuni esempi per rinfrescare la memoria:

Se abbiamo bisogno di leggere un particolare header HTTP possiamo scrivere:

@RequestMapping(...)
public String metodoDiUnController(@RequestHeader("Accept") Stringaccept) { 
	// [...]
}

Se abbiamo bisogno della sessione HTTP e di un parametro inviato dal client:

@RequestMapping(...)
public String metodoDiUnController(@RequestParam(value="paramName") String paramName, HttpSession session) {
	// [...]
}

Gli HandlerMethodArgumentResolver sono il motore dietro a tutto questo, essi permettono di recuperare e rendere disponibili eventuali oggetti inseriti nella signature del nostro controller. Per esempio, se avessimo bisogno in molti metodi un particolare oggetto, potremmo scrivere il nostro WebArgumentResolver e avere in automatico questo oggetto disponibile ovunque, semplicemente modificando la signature dei metodi che lo richiedono.

Supponiamo di avere un applicazione che gestisce articoli di varia natura dove esistono diverse pagine che ricevono come parametro GET un article_id che identifica appunto un articolo salvato su database; in ogni metodo che riceve questo parametro avremmo bisogno di leggere il parametro (grazie alla signature dinamica) e di scrivere il codice per recuperare l'oggetto dal database. Perchè non creare un WebArgumentResolver ad hoc che automatizzi questa attività? Grazie ad esso basterà inserire l'oggetto nelle signature dei metodi per avere il nostro articolo automaticamente.

HandlerMethodArgumentResolver è un'interfaccia che deve essere implementata per ogni oggetto che vogliamo che Spring MVC "risolva" al posto nostro. I metodi da implementare sono:

public Object resolveArgument(MethodParameter methodParameters, ModelAndViewContainer modelAndViewContainer, NativeWebRequest WebRequest, WebDataBinderFactory binderFactory) throws Exception
public boolean supportsParameter(MethodParameter methodParameters)

Il primo metodo implementa la logica vera e propria mentre il secondo permette di identificare quale Resolver è adatto per questa tipologia di oggetto in quanto vengono invocati in cascata uno dopo l'altro. Una volta creata una semplice classe Article, possiamo passare all'implementazione del nostro ArticleArgumentResolver:

public Object resolveArgument(MethodParameter methodParameters, ModelAndViewContainer modelAndViewContainer, NativeWebRequest WebRequest, WebDataBinderFactory binderFactory) throws Exception {
	Article article = new Article();
	article.setId(Long.parseLong(WebRequest.getParameter("article_id")));
	article.setTitle("Fake Title");
	article.setText("Fake Text");
	article.setDate(new Date());
	return article;
}
public boolean supportsParameter(MethodParameter methodParameters) {
	return Article.class == methodParameters.getParameterType();
}

Il metodo resolveArgument crea un finto oggetto Article (in un caso reale sarà necessario recuperare l'articolo corrente, magari tramite accesso al database) mentre il supportsParameter notifica Spring che la classe ArticleArgumentResolver sa gestire oggetti di classe Article.

Una volta definita la classe non serve altro che comunicare a Spring MVC della sua esistenza tramite configurazione XML:

<mvc:annotation-driven>
	<mvc:argument-resolvers>
		<bean class="com.albertobottarini.springmvc.utils.ArticleArgumentResolver"/>
	</mvc:argument-resolvers>
</mvc:annotation-driven>

Da questo momento possiamo quindi utilizzare l'argomento Article all'interno dei nostri Controller, per esempio in questo modo:

@RequestMapping(value="/get-article", method=RequestMethod.GET) public String getArticle(Article article, Model model) {
	model.addAttribute("article", article); return "article";
}

Questo argomento merita un'ultima nota: da Spring MVC 3.0 a Spring MVC 3.1 ci sono stati molti cambiamenti. La guida, sin dalla prima lezione, si è concentrata su Spring MVC 3.1 ma è necessario sottolineare questa novità perchè sono rare le modifiche che non sono retrocompatibili. Nella versione passata l'interfaccia HandlerMethodArgumentResolver non esisteva ma veniva utilizzata WebArgumentResolver che aveva una struttura leggermente diversa. Anche la definizione della nostra classe custom all'interno della configurazione XML era leggermente diversa. Se utilizzate questa versione, fate prima qualche ricerca sulla documentazione ufficiale.

Gli HandlerMethodReturnValueHandler

La seconda interfaccia che analizzeremo in questo articolo dedicato alle estensioni di Spring MVC è appunto HandlerMethodReturnValueHandler che permette di personalizzare il valore di ritorno dei Controller. Ritornando sempre all'articolo dedicato ai Controller, avevamo introdotto una serie di possibili valori di ritorno che i nostri metodi potevano avere, per esempio void per gestire direttamente la response, una stringa per invocare il ViewResolver o un oggetto annotato con @ResponseBody per convertirlo in XML o JSON.

Grazie agli HandlerMethodReturnValueHandler è possibile personalizzare questo tipo di oggetto. Il funzionamento è molto simile al resolver visto nel primo capitolo: implementeremo un'interfaccia, sceglieremo quale oggetto è in grado di "gestire" e ci occuperemo di definire il comportamento da adottare. L'interfaccia richiede l'implementazione di due metodi:

public void handleReturnValue(Object object, MethodParameter methodParameter, ModelAndViewContainer mavContainer, NativeWebRequest WebRequest) throws Exception {
	mavContainer.addAttribute("article", object);
	mavContainer.setView("article");
}
public boolean supportsReturnType(MethodParameter methodParameter) {
	return Article.class == methodParameter.getParameterType();
}

In questo caso la classe sarà in grado di gestire oggetti di tipo Article, aggiungendo al ModelAndView l'oggetto omonimo passato (per renderlo disponibile nella jsp) e definendo "article" come nome della vista da visualizzare.

Questo significa che ogni volta che nel nostro Controller ritorneremo un oggetto di tipo Article, automaticamente Spring MVC caricherà la vista article senza doverla ridefinire ogni volta. Analizziamo ora il metodo del Controller:

@RequestMapping(value="/get-article-auto", method=RequestMethod.GET)
public Article getArticleAuto(Article article, Model model) {
	return article;
}

Grazie ad entrambe le estensioni, quest'ultimo metodo sarà quindi in grado di recuperare il parametro article_id dalla request, di montare un oggetto Article e di visualizzarlo nella vista omonima, tutto in maniera automatizzata e senza scrivere righe di codice aggiuntive. Come per l'estensione precedente, anche questa per poter essere utilizzata, richiede una piccola modifica al file XML di configurazione di Spring:

<mvc:annotation-driven>
	<mvc:argument-resolvers>
		<bean class="com.albertobottarini.springmvc.utils.ArticleReturnValueHandler"/>
	</mvc:argument-resolvers>
</mvc:annotation-driven>

Considerazioni finali

Con questa lezione si conclude la guida dedicata a Spring MVC, un potente framework che, sfruttando le feature di Spring Core, permette di realizzare applicazioni Web sfruttando il paradigma Model View Controller alla perfezione. Abbiamo approfondito tutte le componenti che possono essere strutturate per realizzare un'applicazione che sia allo stesso tempo funzionale, ben organizzata e, perché no, anche divertente.

Ogni componente è preposto ad un determinato scopo e grazie all'Inversion of Control è ben isolato dal resto dell'applicazione, mentre Spring Core e Spring MVC nascondono benissimo al programmatore tutt gli aspetti di integrazione tra le varie funzionalità.

Ti consigliamo anche