Dynamické proxy v Jave

1. Úvod

Tento článok je o dynamických proxy serveroch Java - čo je jeden z primárnych mechanizmov proxy, ktoré máme v tomto jazyku k dispozícii.

Zjednodušene povedané, proxy sú fronty alebo obálky, ktoré prenášajú vyvolanie funkcií prostredníctvom svojich vlastných zariadení (zvyčajne do skutočných metód) - čo potenciálne pridáva niektoré funkcie.

Dynamické proxy umožňujú jednej triede s jednou metódou obsluhovať viac volania metód do ľubovoľných tried s ľubovoľným počtom metód. Dynamický proxy server možno považovať za akýsi druh servera Fasáda, ale taký, ktorý sa môže vydávať za implementáciu ľubovoľného rozhrania. Pod krytom smeruje všetky vyvolania metód do jedného obslužného programu - the vzývať() metóda.

Aj keď to nie je nástroj určený na každodenné programovacie úlohy, dynamické proxy môžu byť pre tvorcov rámcov celkom užitočné. Môže sa použiť aj v tých prípadoch, keď implementácie konkrétnych tried nebudú známe až za behu.

Táto funkcia je zabudovaná do štandardného JDK, a preto nie sú potrebné žiadne ďalšie závislosti.

2. Obsluha vybavenia

Vytvorme jednoduchý server proxy, ktorý v skutočnosti nerobí nič, okrem tlače požadovanej metódy, ktorá má byť vyvolaná, a vrátenia pevne zakódovaného čísla.

Najskôr musíme vytvoriť podtyp java.lang.reflect.InvocationHandler:

verejná trieda DynamicInvocationHandler implementuje InvocationHandler {private static Logger LOGGER = LoggerFactory.getLogger (DynamicInvocationHandler.class); @Override verejné vyvolanie objektu (proxy objektu, metóda metódy, objekt [] args) hodí Throwable {LOGGER.info ("vyvolaná metóda: {}", method.getName ()); návrat 42; }}

Tu sme definovali jednoduchý proxy server, ktorý zaznamenáva, ktorá metóda bola vyvolaná, a vráti 42.

3. Vytvorenie inštancie proxy

Inštancia proxy obsluhovaná obsluhou vyvolania, ktorú sme práve definovali, sa vytvorí pomocou volania továrenskej metódy na serveri java.lang.reflect.Proxy trieda:

Map proxyInstance = (Map) Proxy.newProxyInstance (DynamicProxyTest.class.getClassLoader (), nová trieda [] {Map.class}, nová DynamicInvocationHandler ());

Keď už máme inštanciu proxy, môžeme vyvolať jej metódy rozhrania ako obvykle:

proxyInstance.put ("ahoj", "svet");

Podľa očakávania správa o put () vyvolaná metóda je vytlačená v protokolovom súbore.

4. Obsluha vyvolania prostredníctvom výrazov Lambda

Odkedy InvocationHandler je funkčné rozhranie, je možné definovať obslužný program inline pomocou výrazu lambda:

Map proxyInstance = (Map) Proxy.newProxyInstance (DynamicProxyTest.class.getClassLoader (), nová trieda [] {Map.class}, (proxy, method, methodArgs) -> {if (method.getName (). Equals ("get ")) {return 42;} else {throw new UnsupportedOperationException (" Unsupported method: "+ method.getName ());}});

Tu sme definovali obslužnú rutinu, ktorá vráti 42 pre všetky operácie get a hody UnsupportedOperationException na všetko ostatné.

Je vyvolaná úplne rovnakým spôsobom:

(int) proxyInstance.get ("ahoj"); // 42 proxyInstance.put ("ahoj", "svet"); // výnimka

5. Príklad načasovania dynamického proxy servera

Poďme preskúmať jeden potenciálny scenár pre dynamické proxy v reálnom svete.

Predpokladajme, že chceme zaznamenať, ako dlho trvá vykonanie našich funkcií. V tomto rozsahu najskôr definujeme obslužnú rutinu schopnú zabaliť „skutočný“ objekt, sledovať informácie o načasovaní a reflexívne vyvolanie:

verejná trieda TimingDynamicInvocationHandler implementuje InvocationHandler {súkromný statický Logger LOGGER = LoggerFactory.getLogger (TimingDynamicInvocationHandler.class); súkromné ​​konečné metódy mapy = nový HashMap (); súkromný objektový cieľ; public TimingDynamicInvocationHandler (cieľ objektu) {this.target = cieľ; pre (Metóda metódy: target.getClass (). getDeclaredMethods ()) {this.methods.put (method.getName (), metóda); }} @Override verejné vyvolanie objektu (proxy objektu, metóda metódy, objekt [] args) hodí Throwable {long start = System.nanoTime (); Výsledok objektu = methods.get (method.getName ()). Invoke (target, args); dlho uplynul = System.nanoTime () - štart; LOGGER.info ("Vykonávanie {} bolo dokončené v {} ns", method.getName (), uplynulo); návratový výsledok; }}

Tento server proxy je možné následne použiť na rôzne typy objektov:

Mapa mapProxyInstance = (Mapa) Proxy.newProxyInstance (DynamicProxyTest.class.getClassLoader (), nová trieda [] {Map.class}, nová TimingDynamicInvocationHandler (nová HashMap ())); mapProxyInstance.put ("ahoj", "svet"); CharSequence csProxyInstance = (CharSequence) Proxy.newProxyInstance (DynamicProxyTest.class.getClassLoader (), nová trieda [] {CharSequence.class}, nová TimingDynamicInvocationHandler ("Hello World")); csProxyInstance.length ()

Tu sme namapovali mapu a postupnosť znakov (reťazec).

Invocation of the proxy methods will delegate to the wrapped object as well as produce logging statements:

Spúšťanie bolo dokončené v roku 19153 ns Vykonávanie bolo dokončené v 8891 ns Spúšťanie charAt skončené v 11152 ns Dĺžka vykonávania bola ukončená v 10087 ns

6. Záver

V tomto rýchlom výučbe sme preskúmali dynamické proxy Java a niektoré z jeho možných využití.

Ako vždy, kód v príkladoch nájdete na GitHub.


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