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.