Ett alternativ till Spring Boot

Teckenkodning

Sällan har något varit så hypat inom IT-branschen som Microservices konceptet. Hos oss i “Javaland” har Spring Boot blivit i det närmaste synonymt med Microservices. Visst är Spring-stacken smidig och de flesta av oss kan den idag som rinnande vatten, men man ska inte glömma bort att se sig om då och då. Ibland finns det faktiskt grönare gräs på andra sidan. Vi kommer därför här presentera en alternativ stack för den som vill prova något nytt.

Scenario

Vi vill ha en applikation där vi kan persistera ett objekt med ett automatgenererat ID och ett sträng-värde, vi kallar detta objekt Tag. Applikationen ska ta in data från ett webbformulär och den ska även kunna lista alla sparade objekt i JSON-format.

Konfiguration

Det första vi slås av är att vårt ramverksval Java Spark är otroligt minimalistiskt. Det är verkligen det minsta möjliga som behövs för att få igång en webbapplikation som lyssnar/svarar på en port. Ramverket löser detta genom att använda sig av en inbäddad JeTTy-container kontra Spring Boots default val av Tomcat. Vill man logga krävs det att man själv väljer en Log-implementation. Samma mönster gäller för Dependency Injection, JSON-generering, databas-koppling etc.
Flexibelt? Utan tvekan, men det gör också att det plötsligt uppstår osedvanligt många val att göra. Vi har därför valt ut en möjlig uppsättning för den som är sugen på att snabbt komma igång.

Bibliotek Sammanfattning
Spark-core Vår Microserviceramverkskärna. Essentiell för vårt exempel.
SLF4J Simple Logging Facade for Java. En klassiker och den som Java Spark rekommenderar.
GSON Googles uppskattade konverterare mellan Java och JSON. Ett alternativ till JSON-B.
Guice Googles Dependency Injection ramverk, ett ypperligt val om man ogillar XML.
Hibernate Gammal beprövad Object-relation mapper (ORM). Vårt val av nostalgiska skäl mer än något annat.
PostgreSQL En uppskattad Open Source Databas som vi ofta återvänder till när vi sätter upp nya projekt.



Den kompletta listan över beroenden, inklusive versioner, ser ut som följer:

dependencies {
    compile 'com.sparkjava:spark-core:2.7.2'
    compile 'org.slf4j:slf4j-simple:1.7.25' 
    compile 'com.google.code.gson:gson:2.8.4'
    compile 'com.google.inject:guice:4.2.0'
    compile 'org.hibernate:hibernate-core:5.3.0.Final'
    compile 'javax.xml.bind:jaxb-api:2.3.0'
    compile 'org.postgresql:postgresql:42.2.2'
}

Eftersom det vi vill åstadkomma är ett komplett alternativ till en Spring Boot setup så önskar vi kunna starta applikationen med kommandot “java -jar demo.jar”. Det stöds inte out-of-the-box av Spark Java utan vi behöver plocka in något som löser det åt oss. Vi använder Gradle som byggverktyg och lägger därför till Shadow-pluginen. För att sedan bygga en “fat jar” används kommandot “gradle shadowJar”. Byggtiden påverkas inte nämnvärt och resultatet fungerar mycket bra.

Kod

Då är alla beståndsdelar på plats. Låt oss därför kika på lite kod. Precis som i Spring Boot så utgår vi från en klass med Javas signum metod: static void main(String[] args).

public static void main(String[] args) {
    var injector = Guice.createInjector(new DependencyModule());
    var tagRepository = injector.getInstance(TagRepository.class);
    var logger = injector.getInstance(Logger.class);
    ...

Den första snutten visar hur man kan använda Guice för Dependency Injection. Kanske inte det snyggaste sättet att använda Guice, men det löser uppgiften.

    ...
    staticFiles.location("/public");
    ...

Raden ovan talar om för Spark Java var vi lagt våra statiska filer. I vårt exempel är det endast en fil som återfinns där, nämligen index.html. Index.html i sin tur innehåller bara ett enkelt formulär för att ange namn på nya taggar som sparas ner i databasen. När användaren klickar “Save” så postas formuläret till “/api/tag” och fångas där upp av nedan kodsnutt.

    ...
    post("/api/tag", (req, res) -> {
        req.attribute("org.eclipse.jetty.multipartConfig", new MultipartConfigElement(""));

        var tag = new Tag();
        tag.name = req.queryParams("name");

        tagRepository.saveTag(tag);

        res.redirect("/list");
        return "";
    });
    ...

Notera hur vi använder post-prefixet för att indikera att denna kodsnutt, i sann RESTful-anda, endast kan anropas via POST.
“/api/tag” talar om på vilken path funktionen lyssnar på.
Req är HTTPRequestet och Res är HTTPResponse-objektet.
Sedan följer en inre funktion där vår faktiska logik ligger. Här markerar vi först att requestet är av mutipart natur (d.v.s. ett form). Efter det skapar vi en ny Tag där namnet plockas ut från “name”-parametern. Tagen sparas direkt ner i databasen via tagRepository. Avslutningsvis lägger vi en redirect till “/list” som listar alla sparade taggar och returnerar en tom sträng.

    ...
    get("/list", (req, res) -> {
        before((request, response) -> response.type("application/json"));
        return tagRepository.getAllTags();
    }, new JsonTransformer());
    ...

Listnings-funktionen svarar på GET-anrop och “/list”-pathen. Det första som händer i det här logik-blocket är att vi definierar att svaret kommer vara av typen “application/json”. Sedan returnerar vi en List innehållande alla taggar.
Notera att vi lägger till en JsonTransformer() på slutet. Det är en klass vi skapat för att Java Spark ska konvertera svaret till JSON innan det skickas till den som anropade funktionen.

import com.google.gson.Gson;
import spark.ResponseTransformer;

public class JsonTransformer implements ResponseTransformer {
    private Gson gson = new Gson();

    @Override
    public String render(Object model) {
        return gson.toJson(model);
    }
}

Transformator-klassen i sin helhet. Vi implementerar Java Sparks ResponseTransformer interface och låter GSON konvertera den modell som ska renderas till JSON. Avslutningsvis returnerar vi JSON-objektet i sträng-form.

Sammanfattning

Överlag är vårt intryck positivt, dokumentationen är helt OK och det var enkelt att komma igång. Koden som skrivs fokuserar på logiken med minimal mängd syntaktiskt socker. Java Spark har helt enkelt valt att lägga sig på en riktigt bra nivå. För att hålla nere storleken på applikationen skulle vi högst sannolikt valt ett annat ramverk än Hibernate för att kommunicera mot databasen. Utöver det ser vi ingen anledning att inte prova denna stack för att känna på hur ett liv utan Spring kan te sig. Stacken är relativt beprövad och det finns även gott om resurser på nätet för de använda biblioteken i det fall man stöter på patrull.

Om ni tycker Spark Java verkar intressant är ni varmt välkomna att höra av er via formuläret nedan!

Vi har mottagit ditt meddelande och återkommer inom kort.
Hoppsan! Något gick dessvärre fel, vänligen verifiera att du inte är en robot eller ladda om sidan och försök igen.
Vi vill göra dig uppmärksam på att vi behandlar dina uppgifter i strikt enlighet med vår Integritetspolicy. Allt för att du ska känna dig trygg i att vi värnar om din integritet.