Spring Boot: de standaard van Koreaanse ontwikkelaars en inversion of control

📖 15min read

Is Zuid-Korea een “Spring-republiek”?

Als je in Korea als backendontwikkelaar aan de slag wilt, is er een vraag waar je bijna niet aan ontsnapt: “Kun je met Spring werken?”

Op vacaturesites staat in negen van de tien vacatures expliciet “ervaring met Spring Boot” als eis. Zo erg zelfs dat men weleens spreekt van een “Spring-republiek”. Toen ik nog studeerde, dacht ik dat het gewoon een trend was.

“Je kunt het toch gewoon in Java schrijven, waarom zou je per se dat zware Spring gebruiken?” “Is Node.js of Python (Django) niet veel makkelijker?”

Maar toen ik eenmaal in de praktijk grote projecten meemaakte, begreep ik het. Spring was geen simpele hype, maar een standaardwerkbank die door talloze ervaren ontwikkelaars was aangescherpt zodat enorme en complexe enterprise-systemen veilig en snel gebouwd konden worden.

Deze serie gaat precies daarover: hoe je op die standaardwerkbank een kasteel bouwt dat niet instort, een Monolith.

Frameworks: waarom je niet alles op je eigen manier kunt doen

Voordat we echt technisch worden, moeten we eerst iets rechtzetten. Wat is een framework nu precies? En wat is het verschil met een library?

Ik heb het in de vorige Re: Booting-serie al even aangestipt, maar het doorslaggevende verschil draait uiteindelijk om één vraag: wie heeft de regie in handen?

Spring is een framework. Spring gebruiken voelt dus bijna als het afleggen van een eed: “ik zet mijn eigen stijl even opzij en werk zoals Spring het voorschrijft.” De kern van dat “zoals Spring het voorschrijft” is precies waar het vandaag over gaat: Inversion of Control (IoC).

“Willen jullie alsjeblieft stoppen met overal new te gebruiken”

De eerste regel van die eed was behoorlijk schokkend. Je moest stoppen met het gebruik van new, een van de meest basale onderdelen van Java.

Tijdens mijn studie was het vanzelfsprekend: als ik een object nodig had, schreef ik MemberService service = new MemberService();. Als ik iets nodig heb, maak ik het zelf en gebruik ik het. Wat is daar vreemd aan?

Maar toen ik na mijn indiensttreding een Spring Boot-project opende, was het sleutelwoord new nergens te vinden. In plaats daarvan stonden er overal onbekende annotaties zoals @Autowired en @RequiredArgsConstructor.

“Wacht even, als niemand dat object aanmaakt, hoe wordt die methode dan aangeroepen? Draait dit echt?”

Toen ik het aan een senior developer vroeg, klonk het antwoord frustrerend vaag. “De Spring Container injecteert het vanzelf, dus wij hoeven het niet zelf aan te maken. Sterker nog, als je direct new gebruikt, wordt de code later veel moeilijker te onderhouden.”

Ik kon dat niet accepteren. Ik was toch de eigenaar van mijn eigen code, dus waarom mocht ik niet eens vrij een object maken? Het voelde bijna als een dictatuur van Spring.

Maak ik het zelf, of gebruik ik iets dat door iemand anders is klaargezet?

De chef en de ingrediëntenmanager (IoC & DI)

Om te begrijpen waarom deze ogenschijnlijk autoritaire regel nodig is, veranderen we het decor en gaan we naar de keuken van een restaurant.

1. De traditionele manier (ik heb de controle)

Ik ben een chef, dus een Class. Om te koken heb ik een mes nodig, dus een Dependency.

2. De manier van Spring (Spring heeft de controle)

Ik ben nog steeds de chef. Maar deze keer is er een manager die de ingrediënten en gereedschappen regelt: de Spring Container.

Met andere woorden: ik beheer de middelen die ik nodig heb niet meer zelf, maar laat ze door iets buiten mij beheren en aan mij aanreiken. Dat is Inversion of Control (IoC), en de concrete techniek waarmee dat gebeurt heet Dependency Injection (DI).

Geïnjecteerd krijgen betekent dat je de zorg om gereedschap te kiezen en klaar te zetten bij iemand anders neerlegt.

[Code Verification] Waarom is new gevaarlijk?

Met alleen woorden voelt het nog abstract. Laten we het rechtstreeks in code vergelijken. Stel dat we een kaartbetalingssysteem bouwen.

1. Slecht voorbeeld: direct aanmaken (strakke koppeling)

public class PaymentService {
    // Ik koos en maakte zelf direct een Samsung Card aan (met new)
    private final SamsungCard card = new SamsungCard();

    public void pay() {
        card.payment(); // Alleen betalen met Samsung Card mogelijk
    }
}

Het probleem: op een dag zegt de baas: “Laten we overstappen naar Hyundai Card, daar zijn de kosten lager!” Dan moet ik de code van PaymentService openen en overal SamsungCard vervangen door HyundaiCard. En als die code op honderd plekken staat? Dan ben je de avond kwijt.

2. Goed voorbeeld: dependency injection (losse koppeling)

public class PaymentService {
    private final Card card; // Geen concrete kaartuitgever opgeven (interface gebruiken)

    // Constructor injectie: vertrouwen dat iemand van buiten de kaart aanreikt
    public PaymentService(Card card) {
        this.card = card;
    }

    public void pay() {
        card.payment();
    }
}

Nu maakt het voor PaymentService niet meer uit welke concrete kaartimplementatie wordt doorgegeven.

    Dat is precies de kern van wat men in de praktijk bedoelt met “onderhoudbare code” of het OCP-principe. En juist die flexibiliteit is de reden waarom Spring Boot zo veel wordt gebruikt.

    Spring Container: een hotel voor objecten

    Wie is dan die “manager” die objecten maakt en uitdeelt? Dat is precies de Spring Container.

    Op het moment dat we een Spring Boot-server starten met run, gebeurt er achter de schermen ontzettend veel.

    Onze taak is alleen maar om op de class een sticker te plakken die zegt: “beheer dit alsjeblieft”, bijvoorbeeld met @Service. De rest neemt Spring over, als een enorme hotelmanager die alle gasten ordelijk onderbrengt.

    Praktisch advies: gebruik constructor-injectie

    Spring kent meerdere manieren om afhankelijkheden te injecteren: veldinjectie met @Autowired, setter-injectie en constructor-injectie. Maar in echte projecten wordt constructor-injectie bijna altijd aangeraden. Dat is ook het patroon dat vaak samen met Lomboks @RequiredArgsConstructor wordt gebruikt.

    @Service
    @RequiredArgsConstructor // Dit maakt automatisch de constructor injectie.
    public class OrderService {
        // final kan toegepast worden, dus veilig
        private final PaymentService paymentService;
    }
    
    

    Waarom?

    Tot slot: geen tovenaar, maar een dirigent

    In het begin voelde Spring voor mij alsof het zomaar met mijn code kon zwaaien, en precies daarom stond het me tegen. Nu zie ik het anders. Spring is geen dictator die mij lastigvalt, maar een bekwame dirigent die complexe relaties tussen objecten ordent.

    Daardoor besteed ik minder tijd aan de vraag “welk object moet ik maken?” en kan ik me meer richten op “hoe bouw ik met dit object goede businesslogica?”. Waarschijnlijk is dat ook waarom zo veel ontwikkelaars in Korea zo trouw blijven aan Spring.

    Nu hebben we het beheer van objecten aan Spring overgedragen. De server is klaar om te starten. Maar een webapplicatie draait niet alleen: die moet praten met de frontend. In de PHP-tijd was dat handig, omdat HTML en code samen in één bestand zaten. Waarom scheidt men tegenwoordig frontend-ontwikkeling, met Vue of React, van de backend? En waarom krijg je meteen een rode CORS-fout zodra je die twee met elkaar verbindt?

    De volgende keer duiken we in de verandering van webarchitectuur, SSR vs CSR, en in de ware aard van CORS, de muur die tussen beide in staat.

    Plaats een reactie