Geschiedenis van de vraag:
Het Dynamische Proxy-patroon is geïntroduceerd in Java 1.3 en stelde gebruikers in staat om AOP-patronen, logging, beveiliging en dynamische codegeneratie te implementeren zonder handmatig proxy-classes te schrijven. Dit werd zeer populair in Spring, Hibernate en andere frameworks.
Probleem:
Soms is het nodig om functionaliteit toe te voegen aan een bestaand interface of om het gedrag van methoden tijdens runtime te wijzigen zonder de oorspronkelijke code aan te passen. Voorbeeld: logging van elke methode-aanroep, toegang controleren, profileren. Het grootste probleem is dat het onpraktisch is om dit "handmatig" voor alle classes te doen, vooral als er veel classes zijn of als ze dynamisch worden gegenereerd.
Oplossing:
Java biedt een standaard API in het pakket java.lang.reflect voor het creëren van proxy-objecten "on-the-fly" voor elke interface met behulp van Proxy.newProxyInstance. Een InvocationHandler-object wordt geïmplementeerd dat elke methode-aanroep ontvangt en zijn eigen logica uitvoert.
Voorbeeldcode:
import java.lang.reflect.*; interface Service { void serve(); } class ServiceImpl implements Service { public void serve() { System.out.println("Serving..."); } } class LoggingHandler implements InvocationHandler { private final Object target; public LoggingHandler(Object target) { this.target = target; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("Calling " + method.getName()); return method.invoke(target, args); } } Service service = (Service) Proxy.newProxyInstance( Service.class.getClassLoader(), new Class[]{Service.class}, new LoggingHandler(new ServiceImpl())); service.serve();
Belangrijke kenmerken:
Kan je met een standaard dynamisch proxy een gewone klasse "verpakken" die geen interfaces implementeert?
Nee. Dynamische Proxy werkt alleen met interfaces. Voor classes is bytecode-manipulatie nodig (bijvoorbeeld CGLIB).
Wat gebeurt er als de interface methoden bevat met een returntype dat niet compatibel is met de returnwaarde van de InvocationHandler?
Er zal een ClassCastException worden gegooid, daarom moet de returnwaarde strikt compatibel zijn met de definitie van de interface.
Kan een dynamisch proxy worden gebruikt voor final-methoden?
Nee. Proxy werkt alleen op het niveau van interfaces, en daar zijn geen final-methoden. Final-methoden kunnen in het geheel niet worden overridden.
Negatieve case
Een ontwikkelaar probeerde auditlogging te implementeren via Proxy op classes zonder interface
Voordelen:
Nadelen:
Positieve case
Er werd een contract gedefinieerd met een interface, en er werd al een proxy-wrapper voor gemaakt
Voordelen:
Nadelen: