{"id":322,"date":"2026-08-21T12:41:31","date_gmt":"2026-08-21T10:41:31","guid":{"rendered":"https:\/\/craftcode.be\/?p=322"},"modified":"2026-08-21T12:44:17","modified_gmt":"2026-08-21T10:44:17","slug":"spring-framework-junior-medior-senior-developers","status":"publish","type":"post","link":"https:\/\/craftcode.be\/nl\/spring-framework-junior-medior-senior-developers\/","title":{"rendered":"De verborgen lessen van Spring: \u00e9\u00e9n framework, drie perspectieven"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Spring behoort al jaren tot de meest gebruikte frameworks binnen het Java-ecosysteem. Met Dependency Injection en een uitgebreid containermodel neemt het heel wat complexiteit weg voor developers.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Toch verandert je kijk op Spring naarmate je ervaring groeit. De vragen die je jezelf stelt als junior developer verschillen fundamenteel van die van een medior of senior developer. Niet omdat het Spring framework verandert, maar omdat je steeds meer ontdekt wat er achter de schermen gebeurt.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In deze blog bekijken we hoe developers op verschillende ervaringsniveaus naar het Spring framework kijken en welke inzichten daarbij horen.<\/p>\n\n\n\n<h2 class=\"wp-block-heading has-primary-color has-text-color has-link-color wp-elements-1\">1. Junior developer: leren vertrouwen op de container<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Wanneer je start met Spring, draait alles rond \u00e9\u00e9n fundamenteel principe: de container neemt verantwoordelijkheden over die je vroeger zelf beheerde. In plaats van objecten zelf aan te maken met het new-keyword, laat je de Spring-container bepalen wanneer en hoe afhankelijkheden worden ge\u00efnjecteerd via Inversion of Control (IoC) en Dependency Injection (DI).<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Best practice: Constructor vs. Setter Injection<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Gebruik <strong>Constructor Injection <\/strong>voor verplichte afhankelijkheden. Dit maakt onveranderlijke (immutable) objecten mogelijk, garandeert dat afhankelijkheden nooit null zijn en maakt je code veel beter testbaar.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Gebruik <strong>Setter Injection<\/strong> uitsluitend voor optionele afhankelijkheden die een zinvolle standaardwaarde kunnen hebben.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">De &#8220;gotcha&#8221;: circulaire afhankelijkheden<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Een klassieke valkuil voor juniors is het cre\u00ebren van een circulaire afhankelijkheid. Als Klasse A Klasse B nodig heeft in de constructor en Klasse B op zijn beurt Klasse A nodig heeft, kan Spring de applicatie niet opstarten en wordt er een <em>BeanCurrentlyInCreationException<\/em> gegooid.<\/p>\n\n\n\n<p class=\"has-accent-color has-text-color has-link-color wp-elements-2 wp-block-paragraph\"><strong>De oplossing<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Vaak ligt de oplossing niet in een technische workaround (zoals setter-injectie als laatste redmiddel), maar in een <strong>beter ontwerp waarbij je de componenten ontkoppelt.<\/strong><\/p>\n\n\n\n<h2 class=\"wp-block-heading has-primary-color has-text-color has-link-color wp-elements-3\">2. Medior developer: begrijpen waarom het Spring framework zich anders gedraagt dan verwacht<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Op dit niveau verschuift de focus. Je gebruikt het Spring framework niet langer blindelings, maar probeert ook te begrijpen waarom bepaalde zaken gebeuren. De focus ligt hier op het beheersen van Bean scopes, proxy&#8217;s en Aspect-Oriented Programming (AOP).<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Veelvoorkomende valkuil #1: Singleton versus Prototype<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Veel developers verwachten dat een Prototype Bean altijd een nieuwe instantie oplevert. Wat gebeurt er echter als een Singleton Bean (\u00e9\u00e9n keer aangemaakt) afhankelijk is van een Prototype Bean (elke keer een nieuwe instantie)?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">De Prototype Bean wordt slechts \u00e9\u00e9n keer ge\u00efnjecteerd op het moment dat de Singleton Bean wordt aangemaakt. Je krijgt daarna telkens dezelfde &#8220;prototype&#8221;-instantie terug, wat vaak een ander resultaat geeft dan verwacht.<\/p>\n\n\n\n<p class=\"has-accent-color has-text-color has-link-color wp-elements-4 wp-block-paragraph\"><strong>De oplossing<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Gebruik <strong>Method Injection <\/strong>(zoals @Lookup) of een <strong>ObjectProvider <\/strong>om telkens een nieuwe instantie op te halen wanneer dat nodig is.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Veelvoorkomende valkuil #2: de &#8220;lite&#8221; mode-valstrik (@Configuration vs. @Component)<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Een @Bean-method binnen een klasse met @Component werkt fundamenteel anders dan binnen een klasse met @Configuration. Een klein verschil in annotatie leidt tot een volledig ander resultaat.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Full mode (@Configuration):<\/strong> Spring gebruikt CGLIB-subclassing om te garanderen dat een aanroep naar een @Bean-method altijd dezelfde gecachte singleton-instantie teruggeeft.<\/li>\n\n\n\n<li><strong>Lite mode (@Component):<\/strong> Spring ziet dit als een gewone factory-method. Als je de method handmatig aanroept (bijvoorbeeld otherBean() binnen een andere @Bean-method), krijg je een nieuwe instantie buiten het beheer van de container om. Scoping, AOP en andere post-processors worden dan niet meer correct toegepast op die aanroep.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Veelvoorkomende valkuil #3: de AOP self-invocation-valkuil (waarom werkt @Transactional soms niet?)<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">In de standaard (proxy-based) Spring AOP worden proxy&#8217;s gebruikt om functionaliteiten zoals transacties toe te voegen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Als een klasse haar eigen method intern aanroept (bijvoorbeeld this.bar() vanuit foo()), loopt de oproep niet via de proxy. Hierdoor worden aspecten op bar() (zoals @Transactional of @Async) onverwacht niet uitgevoerd.<\/p>\n\n\n\n<h2 class=\"wp-block-heading has-primary-color has-text-color has-link-color wp-elements-5\">3. Senior developer: begrijpen wat er achter de schermen gebeurt<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Op senior niveau verschuift de aandacht opnieuw. De focus ligt niet langer op het gebruik of het verklaren van gedrag, maar op de werking van de container zelf, de bean lifecycle, container extensibility en de onderliggende Spring-infrastructuur.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Je gebruikt de container niet alleen meer; je breidt deze uit.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Belangrijk concept #1: de &#8220;power move&#8221; (BFPP vs. BPP)<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Het begrijpen van het verschil tussen een BeanFactoryPostProcessor (BFPP) en een BeanPostProcessor (BPP) is essentieel voor infrastructuurcode. Dit vormt de basis van veel Spring-functionaliteiten:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>BFPP (BeanFactoryPostProcessor):<\/strong> werkt op de bean-configuratiemetadata voordat er \u00fcberhaupt beans worden aangemaakt. Hier kun je eigenschappen aanpassen via bijvoorbeeld placeholders.<\/li>\n\n\n\n<li><strong>BPP (BeanPostProcessor): <\/strong>werkt op de daadwerkelijke bean-instanties nadat ze zijn aangemaakt en geconfigureerd. Dit is hoe Spring @Autowired en AOP-proxy&#8217;s afhandelt.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">De senior valkuil: vroege initialisatie (early initialization)<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Wees uiterst voorzichtig met het injecteren van afhankelijkheden in een @Configuration-klasse of een BeanPostProcessor. Omdat deze zeer vroeg in de levenscyclus worden verwerkt, kunnen ze ervoor zorgen dat andere beans voortijdig worden ge\u00efnitialiseerd. Hierdoor worden die beans niet correct verwerkt door latere BPP&#8217;s (zoals AOP auto-proxying).<\/p>\n\n\n\n<p class=\"has-accent-color has-text-color has-link-color wp-elements-6 wp-block-paragraph\"><strong>De oplossing<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Gebruik<\/strong> <strong>static @Bean-methodes<\/strong> voor post-processordefinities zoals BPP\/BFPP om te voorkomen dat de omliggende configuratieklasse te vroeg wordt ge\u00efnstantieerd.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Belangrijk concept #2: geavanceerd lifecycle beheer<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Bij grotere applicaties wordt de volgorde waarin componenten opstarten steeds belangrijker. Vertrouw voor complexe opstartvereisten niet alleen op @PostConstruct. Kijk naar SmartLifecycle.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Hiermee kun je specifieke fasen (getPhase()) defini\u00ebren, zodat bijvoorbeeld een message listener pas start nadat databaseverbindingen en caches volledig gereed zijn.<\/p>\n\n\n\n<h2 class=\"wp-block-heading has-primary-color has-text-color has-link-color wp-elements-7\">Conclusie<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Hoewel elke developer met hetzelfde Spring framework werkt, verschuift de focus naarmate de ervaring groeit. Een junior developer leert vertrouwen op de container. Een medior developer probeert onverwacht gedrag te verklaren. Een senior developer richt zich op de mechanismen achter de schermen en begrijpt hoe Spring zichzelf organiseert en hoe dat gedrag kan worden uitgebreid of be\u00efnvloed.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">Het Spring framework verandert niet. Het perspectief waarmee je naar het framework kijkt wel.<\/p>\n<\/blockquote>\n","protected":false},"excerpt":{"rendered":"<p>Hoe verandert je kijk op het Spring framework naarmate je ervaring groeit? Ontdek de belangrijkste lessen en valkuilen voor junior, medior en senior developers.<\/p>\n","protected":false},"author":1,"featured_media":336,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[55],"tags":[57,78,73,79,77],"class_list":["post-322","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-blog","tag-back-end","tag-dependency-injection","tag-java","tag-spring-aop","tag-spring-framework"],"acf":[],"_links":{"self":[{"href":"https:\/\/craftcode.be\/nl\/wp-json\/wp\/v2\/posts\/322","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/craftcode.be\/nl\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/craftcode.be\/nl\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/craftcode.be\/nl\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/craftcode.be\/nl\/wp-json\/wp\/v2\/comments?post=322"}],"version-history":[{"count":4,"href":"https:\/\/craftcode.be\/nl\/wp-json\/wp\/v2\/posts\/322\/revisions"}],"predecessor-version":[{"id":326,"href":"https:\/\/craftcode.be\/nl\/wp-json\/wp\/v2\/posts\/322\/revisions\/326"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/craftcode.be\/nl\/wp-json\/wp\/v2\/media\/336"}],"wp:attachment":[{"href":"https:\/\/craftcode.be\/nl\/wp-json\/wp\/v2\/media?parent=322"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/craftcode.be\/nl\/wp-json\/wp\/v2\/categories?post=322"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/craftcode.be\/nl\/wp-json\/wp\/v2\/tags?post=322"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}