Dynamic Proxies i Java

1. Introduksjon

Denne artikkelen handler om Java's dynamiske proxyer - som er en av de primære proxy-mekanismene som er tilgjengelige for oss på språket.

Enkelt sagt, fullmakter er fronter eller innpakninger som sender funksjonstilkalling gjennom sine egne fasiliteter (vanligvis på virkelige metoder) - noe som potensielt gir litt funksjonalitet.

Dynamiske fullmakter tillater en enkelt klasse med en enkelt metode å betjene flere metodeanrop til vilkårlige klasser med et vilkårlig antall metoder. En dynamisk proxy kan betraktes som en slags Fasade, men en som kan late som å være en implementering av hvilket som helst grensesnitt. Under dekselet, den dirigerer alle metodeanrop til en enkelt behandler - den påkalle () metode.

Selv om det ikke er et verktøy ment for hverdagsprogrammeringsoppgaver, kan dynamiske proxyer være ganske nyttige for rammeskribenter. Den kan også brukes i de tilfeller der konkrete klasseimplementeringer ikke vil være kjent før kjøretiden.

Denne funksjonen er innebygd i standard JDK, og det er derfor ikke behov for flere avhengigheter.

2. Påkallerhandler

La oss bygge en enkel proxy som faktisk ikke gjør noe annet enn å skrive ut hvilken metode som ble bedt om å bli påkalt, og returnere et hardkodet nummer.

Først må vi lage en undertype av java.lang.reflect.InvocationHandler:

offentlig klasse DynamicInvocationHandler implementerer InvocationHandler {private static Logger LOGGER = LoggerFactory.getLogger (DynamicInvocationHandler.class); @ Override public Object invoke (Object proxy, Method method, Object [] args) throw Throwable {LOGGER.info ("Invoked method: {}", method.getName ()); retur 42; }}

Her har vi definert en enkel proxy som logger hvilken metode som ble påkalt og returnerer 42.

3. Opprette proxy-forekomst

En proxy-forekomst som betjenes av påkallingsbehandleren vi nettopp har definert, opprettes via en fabrikkmetodeanrop på java.lang.reflect.Proxy klasse:

Map proxyInstance = (Map) Proxy.newProxyInstance (DynamicProxyTest.class.getClassLoader (), new Class [] {Map.class}, new DynamicInvocationHandler ());

Når vi har en proxy-forekomst, kan vi påberope grensesnittmetodene som normalt:

proxyInstance.put ("hei", "verden");

Som forventet en melding om sette() metoden som påberopes skrives ut i loggfilen.

4. Innkallingsbehandler via Lambda Expressions

Siden InvocationHandler er et funksjonelt grensesnitt, er det mulig å definere handler inline ved hjelp av lambda-uttrykk:

Map proxyInstance = (Map) Proxy.newProxyInstance (DynamicProxyTest.class.getClassLoader (), new Class [] {Map.class}, (proxy, method, methodArgs) -> {if (method.getName (). Equals ("get ")) {retur 42;} annet {kast ny UnsupportedOperationException (" Ikke-støttet metode: "+ method.getName ());}});

Her definerte vi en handler som returnerer 42 for alle get-operasjoner og -kast Ikke-støttetOperationException for alt annet.

Det påberopes på nøyaktig samme måte:

(int) proxyInstance.get ("hei"); // 42 proxyInstance.put ("hei", "verden"); // unntak

5. Eksempel på timing dynamisk proxy

La oss undersøke et potensielt scenario for dynamiske fullmakter.

Anta at vi vil registrere hvor lang tid funksjonene våre tar å utføre. I denne grad definerer vi først en behandler som er i stand til å pakke inn det “virkelige” objektet, spore timinginformasjon og reflekterende påkalling:

offentlig klasse TimingDynamicInvocationHandler implementerer InvocationHandler {private static Logger LOGGER = LoggerFactory.getLogger (TimingDynamicInvocationHandler.class); private final Kartmetoder = ny HashMap (); privat objekt mål; public TimingDynamicInvocationHandler (Object target) {this.target = target; for (Metodemetode: target.getClass (). getDeclaredMethods ()) {this.methods.put (method.getName (), metode); }} @ Override public Object invoke (Object proxy, Method method, Object [] args) throws Throwable {long start = System.nanoTime (); Objektresultat = methods.get (method.getName ()). Påkalle (target, args); lang forløpt = System.nanoTime () - start; LOGGER.info ("Eksekvering {} ferdig i {} ns", method.getName (), forløpt); returresultat; }}

Deretter kan denne proxyen brukes på forskjellige objekttyper:

Map mapProxyInstance = (Map) Proxy.newProxyInstance (DynamicProxyTest.class.getClassLoader (), new Class [] {Map.class}, new TimingDynamicInvocationHandler (new HashMap ())); mapProxyInstance.put ("hei", "verden"); CharSequence csProxyInstance = (CharSequence) Proxy.newProxyInstance (DynamicProxyTest.class.getClassLoader (), ny klasse [] {CharSequence.class}, ny TimingDynamicInvocationHandler ("Hello World")); csProxyInstance.length ()

Her har vi lagt et kart og en røyesekvens (String).

Påkallelser av proxy-metodene vil delegere til det innpakkede objektet, samt produsere loggingsuttalelser:

Utførelse satt ferdig i 19153 ns Utførelse ble ferdig i 8891 ns Utførelse charAt ferdig i 11152 ns Utførelseslengde ferdig i 10087 ns

6. Konklusjon

I denne raske opplæringen har vi undersøkt Java's dynamiske proxyer samt noen av dens mulige bruksområder.

Som alltid kan koden i eksemplene bli funnet på GitHub.


$config[zx-auto] not found$config[zx-overlay] not found