Inversion of Control (of dependency injection)
De hoofdgedachte achter Spring is dat een container (een ding dat een technische infrastructuur biedt aan een bundel zakelijke functionaliteit) een goed idee is, maar dat deze container op geen enkele wijze eisen mag stellen aan de implementatie van de zakelijke functionaliteit. De zakelijke functionaliteit moet dus geïmplementeerd worden met klassieke Java-objecten (Engels: Plain Old Java Object, afgekort POJO) en dat deze objecten ook niets meer doen dan het implementeren van die functionaliteit. Een POJO mag niet gedwongen worden om een bepaalde vorm aan te nemen om met de container samen te kunnen werken, mag geen extra interfaces of hulpklassen nodig hebben ten behoeve van de container en ook geen lang configuratiebestand met allerlei rarigheden die alleen maar van toepassing zijn op een specifiek soort component.
Door inversion of control of dependency injection wordt iedere component en iedere implementatie van zakelijke functionaliteit als een simpele JavaBean geschouwd. Een eenvoudige JavaBean weet (in principe) niets over de rest van het systeem: het is een eenvoudige klasse in Java, gecombineerd met methoden om informatie op te vragen ("getters") en informatie in te voeren ("setters"). De werking van deze beans hangt af van andere beans, maar heeft een setter of constructor nodig om een andere bean ingevoerd te kunnen krijgen (beans voeren andere beans niet zelf in en instantiëren de beans ook niet zelf). Beans die nodig zijn worden aangeleverd door een externe entiteit (container).
De Spring-container is het systeem dat ervoor zorgt dat een bean voorzien wordt van alle faciliteiten die nodig zijn voor die bean om te kunnen functioneren. Heeft een bean een andere bean nodig, dan weet de Springcontainer
- welke bean nodig is
- waar hij nodig is
- welke setter gebruikt moet worden om die bean in te voeren.
De Springcontainer instantieert alle beans die nodig zijn op het moment dat ze nodig zijn en voorziet ze op hun beurt weer van alle beans die zij nodig hebben. Dit is dependency injection: wat een bean nodig heeft, wordt door de Springcontainer in die bean geïnjecteerd. Dit is een wezenlijk verschil met de EJB-manier waarin iedere component zelf op zoek moet naar alles wat hij nodig heeft. Dit principe maakt het ook mogelijk om componenten te ontwerpen en te bouwen die werkelijk vrijwel alleen maar zakelijke functionaliteit in zich hebben -- een stuk infrastructuur of een andere dienst om te gebruiken is voor een dergelijke component alleen maar het invoeren van een variabele om een object in vast te houden en een setter om het object door ingevoerd te krijgen. Het eigenlijke opzoeken, aanmaken en andere moeilijke zaken worden door de Spring container gedaan en de component (de POJO) hoeft verder van niets te weten (zelfs niet dat er een Springcontainer is).
De Springcontainer op zijn beurt wordt gevoed door een configuratiebestand dat een lijst bevat van alle beans in het systeem en waar per bean in staat welke andere beans nodig zijn. Daarnaast zijn er extra configuratie-opties per bean, die voornamelijk neerkomen op het instellen van initiële waarden voor de bean bij aanmaken.
Dit betekent dat Spring moet zondigen tegen zijn eigen principes: een bean moet wel degelijk een kleine vormaanpassing ondergaan voor dependency injection (een extra variabele en een setter of een parameter in de constructor) en een configuratiebestand blijft nodig. Dit configuratiebestand is veel simpeler dan de J2EE dependency descriptors: de vorm is veel simpeler en eenduidiger en er zijn veel minder soorten van configuratie die gedaan kunnen worden. Dit volgt overigens ook direct uit het feit dat voor de Springcontainer alles een Spring-bean en dus feitelijk een POJO is: als je als container niets meer weet dan dat iets een bean is en dat je wat setters kunt verwachten, kun je ook geen configuratie invoeren voor iets anders dan setters op een POJO.
Aspect-georiënteerd programmeren
Het is vrijwel onmogelijk om een zakelijke applicatie te schrijven die alleen maar uit zakelijke functionaliteit bestaat. Een moderne applicatie voor het bedrijfsleven biedt ook andere functionaliteiten -- beveiliging bijvoorbeeld, of andere regels voor het toepassen van de zakelijke functionaliteit. Allerhande zaken die om de zakelijke functionaliteit heen gebeuren, vaak terugkomen, applicatie-specifiek zijn (en dus niet in de container verwerkt kunnen worden) en die in de applicatie tussen de container en de beans in moet zitten.
Een dergelijke functionaliteit vraagt eigenlijk om een manier van code schrijven die aangeroepen kan worden op verschillende, specifieke punten in het programma -- rondom of midden in de zakelijke functionaliteit. Maar natuurlijk zonder dit in de implementatie van de zakelijke functionaliteit op te nemen, want anders wordt die weer vervuild met technische details.
Spring voorziet in een (basale) voorziening voor aspectgeoriënteerd programmeren zodat programma's op bepaalde punten "onderbroken" kunnen worden door code van buitenaf. Deze code doet iets, verandert mogelijk de toestand van het programma of het verloop ervan, en laat het programma daarna verder lopen alsof er niets gebeurd is. Binnen Spring is het mogelijk om een programma in de Spring-container voor en na iedere methode-aanroep te laten onderbreken door een apart stukje code. Hierdoor wordt het mogelijk om zaken als beveiliging te regelen op ieder punt, zonder een stuk code meerdere keren in het programma op te hoeven nemen en zelfs zonder dat het hoofdprogramma "weet wat er aan de hand is". Het is mogelijk om in de onderbrekende code de loop van het programma te wijzigen, argumenten aan onderbroken methodes aan te passen of zelfs de methode volledig af te vangen en het programma met een foutmelding te onderbreken.
Model-View-Controller
Een andere functionaliteit die Spring biedt, speciaal aan de webkant van webapplicaties, is een model-view-controller-structuur. Deze faciliteit (een alternatief voor zaken als Apache Struts) maakt het mogelijk een nette scheiding aan te brengen in de presentatie van informatie via een webpagina en het gebruik van zakelijke functionaliteit achter die webpagina.
Verdere functionaliteit
Voor zaken als databasetoegang (persistentie), transactionaliteit en dergelijke hebben de makers van Spring gezegd "er zijn genoeg goede implementaties van deze zaken, dat hoeven we niet over te doen". Spring ondersteunt de transparante toegang en het gebruik van allerlei andere frameworks en API's, waaronder Hibernate, iBATIS, JDO, JTA en JMS. Deze ondersteuning bestaat over het algemeen uit een gesimuleerde bean over het betreffende framework of API heen. Een gesimuleerde bean is een bean die opgenomen kan worden in de Spring-configuratie en zo geïntegreerd kan worden in andere beans in het programma, middels dependency injection.
Naast de ondersteuning voor aparte persistentie-frameworks biedt Spring ook een interface naar directe databasecommunicatie via de JDBC-API; dit voor mensen die geen ander framework kunnen of willen gebruiken maar wel een aantal klassieke nadelen van JDBC willen vermijden (waaronder de grote hoeveelheden foutafhandelingscode die normaal voor JDBC-communicatie geschreven moet worden). Deze ondersteuning verdient meestal echter niet de voorkeur boven het gebruik van aparte persistentie-frameworks en levert ook meestal meer codeerwerk op dan bij externe frameworks noodzakelijk is.
Bronnen, noten en/of referenties
Referenties
(en) Johnson, Rod (2003). Expert one-on-one J2EE design and development. Wiley ; Chichester : John Wiley [distributor], cop. 2003., Indianapolis, IN. ISBN 9780764543852 0764543857.