Dinamikus proxyk Java-ban

1. Bemutatkozás

Ez a cikk a Java dinamikus proxykiról szól - amely az egyik elsődleges proxy mechanizmus, amely a nyelven elérhető.

Egyszerűen fogalmazva, a proxyk olyan frontok vagy burkolók, amelyek a funkciók meghívását saját létesítményeiken keresztül adják át (általában valós módszerekre) - potenciálisan hozzáadva bizonyos funkciókat.

A dinamikus proxyk lehetővé teszik egyetlen osztály egyetlen metódussal, hogy tetszőleges számú metódusokkal több metódushívást önkényes osztályokba szolgáltassanak. A dinamikus proxy egyfajta Homlokzat, de olyan, amely bármely felület megvalósításának tettetheti magát. A fedél alatt, az összes metódus meghívást egyetlen kezelőhöz irányítja - a invoke () módszer.

Bár ez nem a mindennapi programozási feladatokra szánt eszköz, a dinamikus proxyk nagyon hasznosak lehetnek a keretrendszer írói számára. Használható azokban az esetekben is, amikor a konkrét osztálymegvalósítások csak futásig lesznek ismertek.

Ez a szolgáltatás a standard JDK-ba van beépítve, ezért nincs szükség további függőségekre.

2. Meghíváskezelő

Készítsünk egy egyszerű proxyt, amely valójában nem csinál semmit, csak azt, hogy kinyomtatja a kért módszer metódusát, és adjon meg egy keményen kódolt számot.

Először létre kell hoznunk a. Altípust java.lang.reflect.InvocationHandler:

public class DynamicInvocationHandler implementálja az InvocationHandler {private static Logger LOGGER = LoggerFactory.getLogger (DynamicInvocationHandler.class); @Orride public Object invoke (Object proxy, Method method, Object [] args) dobja a Throwable {LOGGER.info ("Meghívott módszer: {}", method.getName ()); visszatérés 42; }}

Itt definiáltunk egy egyszerű proxyt, amely naplózza, hogy melyik metódust hívták meg, és 42-t ad vissza.

3. Proxy példány létrehozása

Az általunk definiált meghíváskezelő által kiszolgált proxy példány egy gyári metódus hívással jön létre java.lang.reflect.Proxy osztály:

Map proxyInstance = (Térkép) Proxy.newProxyInstance (DynamicProxyTest.class.getClassLoader (), új osztály [] {Map.class}, új DynamicInvocationHandler ());

Ha van egy proxy példányunk, a szokásos módon meghívhatjuk az interfész metódusait:

proxyInstance.put ("hello", "world");

A várakozásoknak megfelelő üzenet arról put () A hivatkozott metódus ki van nyomtatva a naplófájlba.

4. Meghíváskezelő a Lambda Expressions segítségével

Mivel InvocationHandler egy funkcionális interfész, a lambda kifejezés használatával meghatározható a kezelő inline:

Map proxyInstance = (Térkép) Proxy.newProxyInstance (DynamicProxyTest.class.getClassLoader (), új osztály [] {Map.class}, (proxy, method, methodArgs) -> {if (method.getName (). Egyenlő ("get ")) {return 42;} else {dobjon új UnsupportedOperationException (" Nem támogatott módszer: "+ method.getName ());}});

Itt meghatároztunk egy kezelőt, amely 42-et ad vissza minden lekérési művelet és dobás esetén UnsupportedOperationException minden másért.

Pontosan ugyanúgy hívják meg:

(int) proxyInstance.get ("hello"); // 42 proxyInstance.put ("hello", "world"); // kivétel

5. Dinamikus proxy időzítése

Vizsgáljuk meg a dinamikus proxyk egyik lehetséges valós forgatókönyvét.

Tegyük fel, hogy fel akarjuk jegyezni, hogy a funkcióink végrehajtása mennyi ideig tart. Ennyire meghatározunk először egy kezelőt, amely képes a „valódi” objektum beburkolására, az időzítési információk nyomon követésére és a reflektív invokációra:

public class TimingDynamicInvocationHandler megvalósítja az InvocationHandler {private static Logger LOGGER = LoggerFactory.getLogger (TimingDynamicInvocationHandler.class); privát végleges térképi módszerek = new HashMap (); private Object target; public TimingDynamicInvocationHandler (Object target) {this.target = target; for (Metódus módszer: target.getClass (). getDeclaredMethods ()) {this.methods.put (method.getName (), metódus); }} @Orride public Object invoke (Object proxy, Method method, Object [] args) dob dobható {long start = System.nanoTime (); Objektum eredménye = method.get (method.getName ()). Invoke (target, args); rég eltelt = System.nanoTime () - start; LOGGER.info ("Futtatás {} befejeződött a (z) {} ns fájlban", method.getName (), eltelt); visszatérési eredmény; }}

Ezt követően ez a proxy különböző objektumtípusokon használható:

Map mapProxyInstance = (Térkép) Proxy.newProxyInstance (DynamicProxyTest.class.getClassLoader (), új osztály [] {Map.class}, új TimingDynamicInvocationHandler (új HashMap ())); mapProxyInstance.put ("hello", "world"); CharSequence csProxyInstance = (CharSequence) Proxy.newProxyInstance (DynamicProxyTest.class.getClassLoader (), új osztály [] {CharSequence.class}, új TimingDynamicInvocationHandler ("Hello World"); csProxyInstance.length ()

Itt egy térképet és egy char-szekvenciát (String) helyeztünk el.

A proxy módszerek meghívásai delegálják a becsomagolt objektumot, valamint naplózási utasításokat állítanak elő:

A végrehajtás befejeződött 19153 ns alatt A végrehajtás befejeződik 8891 ns alatt A charAt végrehajtása 11152 ns alatt befejeződött A végrehajtás hossza 10087 ns alatt befejeződött

6. Következtetés

Ebben a gyors bemutatóban megvizsgáltuk a Java dinamikus proxykat, valamint néhány lehetséges felhasználását.

Mint mindig, a példákban szereplő kód megtalálható a GitHubon.