Proxy-uri dinamice

Proxy-uri dinamice

Unele clasa teorie ... proxy este creată prin apelarea metodei Proxy.getProxyClass care adoptă încărcător de clasă și o interfață array (interfețe), și se întoarce java.lang.Class obiect de clasă, care este încărcat cu ajutorul de clasă încărcător transmise și instrumente transmise interfețe matrice .







Există o serie de restricții privind parametrii transmiși:
  1. Toate obiectele din matricea de interfețe trebuie să fie interfețe. Ele nu pot fi clase sau primitive.
  2. Gama de interfețe nu poate avea două obiecte identice.
  3. Toate interfețele din matricea de interfețe trebuie să fie încărcate cu acel încărcător de clasă care este trecut la metoda getProxyClass.
  4. Toate interfețele non-publice trebuie să fie definite în același pachet, altfel clasa proxy generată nu va putea să le implementeze pe toate.
  5. În nici două interfețe nu poate exista o metodă cu același nume și semnătura parametrilor, dar cu diferite tipuri de valoare de retur.
  6. Lungimea seriei de interfețe este limitată la 65535 interfețe. Nici o clasă Java nu poate implementa mai mult de 65535 interfețe (și așa a fost dorit!).

Dacă oricare dintre restricțiile de mai sus sunt încălcate, IllegalArgumentException va fi aruncat. și dacă matricea de interfețe interfețe este nulă, atunci NullPointerException va fi aruncat.

Proprietăți din clasa proxy dinamică
  1. Clasa proxy este publică, are un modificator final și nu este abstractă.
  2. Numele clasei proxy nu este definită în mod prestabilit, dar începe cu $ Proxy. Toți spațiile de nume începând cu $ Proxy sunt rezervate pentru clasele proxy.
  3. Clasa proxy este moștenită de la java.lang.reflect.Proxy.
  4. Clasa proxy implementează toate interfețele care sunt transmise la crearea, în ordinea transmisiei.
  5. Dacă clasa proxy implementează o interfață non-publică, ea va fi generată în pachetul în care este definită cea mai mare interfață non-publică. În general, pachetul în care va fi generat clasa proxy este nedefinit.
  6. Metoda Proxy.isProxyClass returnează true pentru clasele, create folosind Proxy.getProxyClass și pentru clasele de obiecte create folosind Proxy.newProxyInstance și false în caz contrar. Această metodă folosește subsistemul de securitate Java și trebuie să înțeleagă că pentru o clasă doar moștenit de la java.lang.reflect.Proxy returnează false.
  7. java.security.ProtectionDomain pentru clasa proxy este aceeași ca pentru clasele de sistem încărcate de încărcătorul bootstrap, de exemplu - pentru java.lang.Object. Acest lucru este logic, deoarece codul de clasă proxy este creat de JVM însuși și nu are nici un motiv să nu se încreadă.
O instanță de clasă proxy dinamic și proprietățile sale

Constructorul de clasă proxy are un argument - implementarea interfeței InvocationHandler. În consecință, obiectul clasei proxy poate fi creat folosind reflecție, apelând metoda newInstance a obiectului Class. Cu toate acestea, există o altă modalitate - de a apela metoda Proxy.newProxyInstance. care acceptă încărcătorul de clase, o serie de interfețe care vor implementa clasa proxy și un obiect care implementează InvocationHandler. De fapt, această metodă combină primirea unei clase proxy cu Proxy.getProxyClass și crearea unei instanțe a acestei clase prin reflecție.

Proprietățile instanței create a clasei proxy sunt următoarele:

  1. Obiectul clasei proxy se referă la toate interfețele transferate în matricea interfețelor. Dacă IDemo este una dintre interfețele intermediare, atunci exemplul proxy al operației IDemo întoarce întotdeauna adevărat. iar proxy-ul de operare (IDemo) va fi completat corect.
  2. Metoda statică Proxy.getInvocationHandler returnează procedura de preluare a apelului când a fost creată instanța de clasă proxy. Dacă obiectul trecut la această metodă nu este o instanță de clasă proxy, va fi aruncată excepția IllegalArgumentException.
  3. Clasa handler de apel implementează interfața InvocationHandler. în care este definită metoda invoke. având următoarea semnătură:






public Object invoke (Object proxy, Metoda metode, Object [] args) aruncă Throwable

Aici proxy este o instanță a clasei proxy care poate fi utilizată atunci când procesează un apel la o anumită metodă. Al doilea parametru, metoda, este o instanță a clasei java.lang.reflect.Method. Valoarea acestui parametru este una dintre metodele definite în oricare dintre interfețele transferate în timpul creării clasei proxy sau a super-interfețelor acestora. Al treilea parametru este o serie de valori de argument ale metodei. Argumentele tipurilor primitive vor fi înlocuite de exemplele clasei lor de împachetare, cum ar fi java.lang.Boolean sau java.lang.Integer. O implementare particulară a metodei invoke poate schimba această matrice.

Valoarea returnată de metoda invoke trebuie să aibă un tip compatibil cu tipul de valoare returnat de metoda de interfață pentru care este invocată această împachetare. În special, dacă metoda de interfață returnează o valoare primitivă, trebuie să returnați o instanță a clasei de înfășurare a acestui tip primitiv. Dacă se întoarce nul și se așteaptă o valoare primitivă, NullPointerException va fi aruncată. În cazul tipurilor non-primitive, clasa valorii returnate a metodei invoke trebuie returnată clasei de valoare a returului metodei interfeței, în caz contrar ClassCastException va fi aruncată.

În cadrul metodei invoke, ar trebui aruncate numai acele excepții verificabile care sunt definite în semnătura metodei interfeței chemate sau care le sunt aduse. În plus față de aceste tipuri de excepții, pot fi aruncate numai excepții necontrolate (cum ar fi java.lang.RuntimeException) sau erori (de exemplu, java.lang.Error). Dacă invoca o metodă în interiorul unei excepții verificate aruncat incomparabilă cu cele descrise în interfața metoda de semnătură - aceasta va fi doar o excepție este aruncată UndeclaredThrowableException.

Metodele hashCode, equals și toString definite în clasa Object vor fi de asemenea invocate direct, dar prin metoda invoke împreună cu toate metodele de interfață. Alte metode publice ale clasei Obiect vor fi chemați direct.

Obiectul User este destul de normal, fără magie.

Proxy.newProxyInstance - ele însele surse de magie, parametrii de apel sunt după cum urmează:

  • Clasa ClassLoader User, despre aceasta un pic mai mic;
  • O serie de clase de tip. trebuie să accepte o serie de interfețe. care implementează clasa noastră (Utilizator). METODE DE ACESTE INTERFACI VOR FI EXECUTATE (invocateHandler).
  • Instanța de invocare a apelurilor. care va intercepta metodele solicitate pentru obiectul utilizatorului (de fapt, apelurile vor trece prin noul userProxy creat).

Pe ieșire obținem o copie a unei anumite clase (proxy), care dă următoarele funcții magice:

  • Se efectuează toate metodele de interfață, transmise în al doilea parametru de intrare în Proxy.newProxyInstance de apel (în acest exemplu getName, setName, redenumire). In aceasta este similar cu persoane;
  • Când apelați aceste metode în instanța noastră (de exemplu, userProxy.setName), se va apela metoda InvocationHandler INVOKE (). InvocationHandler decide deja cum să o facă -
    • apelați metoda corespunzătoare a clasei reale Person
    • faceți altceva, în cazul nostru

    Aceasta este, în exemplul nostru, InvocationHandler afișează pur și simplu numele metodei numite în consola și o numește pentru obiectul salvat. Astfel, înainte de fiecare executare a metodelor utilizatorului, se va afișa numele metodei chemate.

    Spuneți că avem o clasă "grasă" cu o grămadă de metode în care am vrut să realizăm aceeași acțiune - de exemplu, logare, traseu de audit, securitate, serializare a rezultatelor. Într-un OOP clasic o astfel de problemă este rezolvată fie prin copiere paste, fie prin alocarea fiecărei metode unei clase separate, ceea ce nu este de asemenea convenabil. Și chiar în acest moment, magia ajunge la ajutorul numit reflexie sub forma Proxy. Este suficient să selectați metodele clasei din interfață pentru a crea un înveliș care va efectua aceleași acțiuni pentru toate metodele (înainte sau după logica metodei). În general, acesta este deja un AOP junglă care are nevoie de detalii, vă rog.

    De unde a venit această "anumită clasă", copia pe care am obținut-o la ieșirea din Proxy.newProxyInstance?

    Aceasta este o clasă creată dinamic, creată din BYT MASSIVE.
    Call Chain: Proxy.newProxyInstance -> Proxy.getProxyClass -> sun.misc.ProxyGenerator.generateProxyClass
    Această ultimă metodă returnează o serie de octeți, care apoi se convertesc prin ClassLoader.defineClass la clasă. și apoi newInstance.
    Ca rezultat, obținem un program care se generează.
    Desigur, vreau să văd ce a fost generat de Peter Jones.

    Din fericire, puteți obține o clasă de bytecode generat, stabilind un magic (și nimeni nu știe) sistem de proprietate «sun.misc.ProxyGenerator.saveGeneratedFi les» la adevărat înainte de a crea clasa.







    Articole similare

    Trimiteți-le prietenilor: