Utilizzare Spring nelle web application – parte 5


Nella scorsa puntata ci siamo lasciati in “sospensione”, avendo implementato il primo test dell’applicazione ma non… la classe sotto test! Rimedieremo abbondantemente qui, svelando anche qualche novità interessante sul funzionamento di Spring.

Implementazione del controller

Seguendo la nostra strategia top-down implementeremo il controller ipotizzando l’esistenza di servizi funzionanti, che però implementeremo solo in seguito, scrivendo solo il codice strettamente necessario ai nostri bisogni. Ecco dunque l’implementazione di IndexController, da salvare in src/main/java/it/artera/springtut/controller/IndexController.java:

package it.artera.springtut.controller;

import it.artera.springtut.model.Insertion;
import it.artera.springtut.service.InsertionService;

import java.util.*;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("/index.*")
public class IndexController {

    private InsertionService insertionService;

    @Autowired(required = true)
    public void setInsertionService(InsertionService insertionService) {
        this.insertionService = insertionService;
    }

    @RequestMapping
    public Map<String, Object> execute() {
        Map<String, Object> data = new HashMap<String, Object>();
        List<Insertion> insertionsList = insertionService.getMostRecentInsertions();
        data.put("insertionsList", insertionsList);

        return data;
    }
}

Analizziamo ora gli aspetti più interessanti di questa classe.

Ciò che salta subito all’occhio è il massiccio utilizzo di annotazioni: questa modalità è stata introdotta a partire dalla versione 2.5 e ha reso deprecata la complessa gerarchia di classi Controller presente in Spring 2.0. Avere le configurazioni direttamente nel codice rende i file XML molto più snelli e migliora la leggibilità dell’applicazione da parte degli sviluppatori.

La prima annotazione che incontriamo è @Controller: questa serve a Spring per identificare le classi che risponderanno alle chiamate HTTP.

Le annotazioni @RequestMapping invece servono per specificare a quali combinazioni di URL, parametri e metodi HTTP risponderà il controller e i suoi metodi. Questa annotazione è molto potente (e complessa!), per il momento ci basti sapere che:

  • utilizzata a livello di classe serve per indicare una configurazione generica per l’intero controller;
  • utilizzata a livello di metodo consente di specificare con più precisione tutti gli altri parametri.

Infine abbiamo dei riferimenti a un’istanza di InsertionService; questa classe (che ancora non abbiamo realizzato!) si occuperà del recupero delle inserzioni tramite il metodo getMostRecentInsertions(). A questo punto è doveroso far notare alcuni aspetti molto importanti:

  • IndexController ha una variabile membro insertionService, di tipo it.artera.springtut.service.InsertionService;
  • questa variabile viene inizializzata dall’esterno, ovvero a runtime ci sarà “qualcuno” (il container di spring) che inizializzerà correttamente un’istanza di InsertionService, per poi assegnarla al nostro controller tramite il metodo setInsertionService();
  • il metodo setInsertionService() è annotato con @Autowired: ciò farà in modo che a runtime il container di Spring passerà come argomento del metodo un’istanza correttamente configurata di InsertionService.

La logica di funzionamento del controller è molto semplice: viene creata una java.util.Map, vi si aggiunge una lista di oggetti Insertion con il nome “insertionsList” (come specificato dal test, cfr. scorsa puntata), e si restituisce come variabile di ritorno del metodo execute().

Implementazione dei servizi

Realizzeremo ora un’implementazione “finta” (comunemente definita “dummy”) di InsertionService, che ci servirà per portare avanti lo sviluppo della parte web. Questo modo di lavorare ci permetterà di concentrarci sullo sviluppo dello strato web dell’applicazione, nell’attesa che il team di sviluppo dello strato Service si dia una mossa!

Come al solito realizzeremo prima il test, src/test/java/it/artera/springtut/service/InsertionServiceTest.java:

package it.artera.springtut.service;

import static org.junit.Assert.*;

import java.util.List;

import it.artera.springtut.AbstractTest;
import it.artera.springtut.model.Insertion;

import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;

public class InsertionServiceTest extends AbstractTest {
    private InsertionService insertionService;

    @Autowired
    public void setInsertionService(InsertionService insertionService) {
        this.insertionService = insertionService;
    }

    @Test
    public void test_getMostRecentInsertions() {
        List<Insertion> result = insertionService.getMostRecentInsertions();
        assertNotNull(result);
        assertTrue(result.size() > 0);
    }

    @Test
    public void test_getInsertion() {
        Insertion i = insertionService.getInsertion(1L);
        assertNotNull(i);
        assertEquals(1L, i.getId().longValue());
    }
}

Come potete vedere ne abbiamo approfittato per definire il test anche per un metodo getInsertion(), che utilizzeremo nella visualizzazione del dettaglio di un’inserzione.

Ed ecco la realizzazione dummy di InsertionService, da salvare in src/main/java/it/artera/springtut/service/InsertionService.java

package it.artera.springtut.service;

import it.artera.springtut.model.Insertion;

import java.math.BigDecimal;
import java.util.*;

import org.springframework.stereotype.Service;

@Service
public class InsertionService {
    public List<Insertion> getMostRecentInsertions() {

        // implementazione finta, da utilizzare per sviluppare la parte web
        List<Insertion> result = new ArrayList<Insertion>();
        result.add(new Insertion(1L, "Chitarra acustica", "Lorem ipsum docet bla bla bla", null,
                new BigDecimal("50.0"), new Date()));
        result.add(new Insertion(2L, "Collezione figurine Panini", "Lorem ipsum docet bla bla bla", null,
                new BigDecimal("100.0"), new Date()));
        result.add(new Insertion(3L, "Chitarra acustica", "Lorem ipsum docet bla bla bla", null, null, new Date()));

        return result;
    }

    public Insertion getInsertion(long id) {
        // implementazione finta, da utilizzare per sviluppare la parte web
        return new Insertion(id, "Chitarra acustica", "Lorem ipsum docet bla bla bla", null, new BigDecimal("50.0"),
                new Date());
    }
}

Più che l’implementazione dei metodi (completamente fasulla) ci interessa evidenziare l’utilizzo dell’annotazione @Service, che indica al container di Spring di configurare questa classe come servizio a disposizone dell’applicazione. Da questo momento, tutte le classi che dichiareranno di aver bisogno di un InsertionService, come IndexController, saranno accontentate!

Build dell’applicazione

Lanciando il comando:

mvn package

finalmente, se abbiamo scritto correttamente tutto, produrrà un rassicurante messaggio finale: BUILD SUCCESSFUL. Questo comando effettua diverse operazioni, le più importanti sono:

  • compilazione dei sorgenti;
  • ricopiatura di eventuali risorse come file di configurazione, ecc.;
  • esecuzione di tutti i test JUnit;
  • impacchettamento in un file con estensione .war

Se solo uno di questi passaggi non va a buon fine (ad esempio, fallisce un test), il build viene interrotto.

Sviluppi

Per completare lo strato web della homepage ci manca solo la realizzazione della pagina di visualizzazione, che vedremo nella prossima puntata.

A presto!

Linkografia