ProgrammatieJava architect

Wat is een proxy, de soorten proxies in Java en hoe wordt dynamisch gedrag van objecten gerealiseerd met behulp van een proxy?

Slaag voor sollicitatiegesprekken met de Hintsage AI-assistent

Antwoord.

Achtergrond:

Proxy-objecten en het Proxy-patroon zijn in Java geïntroduceerd om vervangende objecten te creëren die de toegang tot een echt object kunnen controleren, het gedrag ervan kunnen wijzigen of cross-cutting-functies (beveiliging, logging, metrics) kunnen invoegen. Sinds Java 1.3 is er ondersteuning voor standaard dynamische proxies in het pakket java.lang.reflect, en later populaire bibliotheken zoals CGLIB en ByteBuddy.

Probleem:

Het is niet altijd mogelijk of handig om de logica van een bestaand klasse rechtstreeks te wijzigen. Vaak is het nodig om gedrag transparant toe te voegen (bijvoorbeeld logging, caching, transactionele verwerking) zonder de broncode van het object te wijzigen, wat niet mogelijk is met standaard overerving.

Oplossing:

Proxy-objecten kunnen statisch (door handmatige subclassing) en dynamisch (door middel van reflectie of bytecode) worden geïmplementeerd. Een dynamische proxy maakt het mogelijk om on-the-fly objecten te creëren die de vereiste interface implementeren en de aanroepen doorgeven aan het echte object binnen de methode invoke(), wat flexibiliteit biedt voor het invoegen van extern gedrag.

Codevoorbeeld:

import java.lang.reflect.*; interface Service { void doWork(); } class RealService implements Service { public void doWork() { System.out.println("Doing work!"); } } class LoggingInvocationHandler implements InvocationHandler { private final Object target; public LoggingInvocationHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("Calling: " + method.getName()); return method.invoke(target, args); } } Service real = new RealService(); Service proxy = (Service) Proxy.newProxyInstance( real.getClass().getClassLoader(), new Class[] {Service.class}, new LoggingInvocationHandler(real)); proxy.doWork(); // Het aanroepen wordt gelogd

Belangrijkste kenmerken:

  • Statische proxies vereisen vooraf beschreven code voor elke functie
  • Dynamische proxies kunnen on-the-fly tijdens runtime worden gemaakt voor elke interface
  • Voor klassen zonder interfaces zijn externe bibliotheken nodig (bijvoorbeeld CGLIB)

Misleidende vragen.

Kan de standaard dynamische Proxy in Java werken met klassen die geen interfaces implementeren?

Nee. De standaard Proxy in Java werkt alleen met interfaces. Voor klassen zonder interfaces (bijvoorbeeld als je een gewone klasse wilt proxyfying) zijn externe middelen nodig (bijvoorbeeld CGLIB, ByteBuddy).

Kan er een proxy worden gemaakt voor een interface met private methoden?

Nee. Interfaces in Java kunnen geen private methoden bevatten die moeten worden geproxyfied. Dynamische proxy's implementeren de publieke methoden van de interface. Sinds Java 9 zijn er private default methoden, maar deze zijn niet toegankelijk via proxy.

Wat is het verschil tussen InvocationHandler en MethodInterceptor (CGLIB)?

InvocationHandler is de standaard JDK-interface die wordt gebruikt voor dynamische proxies. Het accepteert aanroepen van de methoden van de interface. MethodInterceptor is een CGLIB-interface waarmee aanroepen van methoden in willekeurige klassen kunnen worden onderschept via dynamische overerving. Het verschil zit in toepasbaarheid en niveau: JDK werkt alleen met interfaces, CGLIB met willekeurige klassen.

Veelvoorkomende fouten en anti-patronen

  • Typefout: poging om Proxy te gebruiken voor een klasse die geen interface implementeert
  • Impliciete cyclische aanroepen die leiden tot StackOverflow
  • Proxificeren van klassen die daar niet voor bedoeld zijn, of poging om final klassen te proxificeren

Voorbeeld uit de praktijk

Negatieve case

Een engineer herhaalt handmatig de logcode in elke methode van alle services. Bij het toevoegen van nieuwe methoden herhaalt hij constant de logica, vergeet vaak de benodigde aanroepen toe te voegen.

Pluspunten:

  • Transparant, gemakkelijk te begrijpen hoe de uitvoering verloopt

Minpunten:

  • Enorme tijdsverspilling, veel eentonige code, moeilijk te onderhouden

Positieve case

AOP is in het project geïmplementeerd via dynamische proxy: logging en transactiecontrole wordt uitgevoerd via vervangende wrappers die centraal worden geïmplementeerd.

Pluspunten:

  • Sneller invoeren en wijzigen van elke cross-cutting functionaliteit, geen duplicatie

Minpunten:

  • Noodzaak om te begrijpen hoe proxies werken, potentiële moeilijkheden bij debugging