Dynamiske Fuldmagtsklasser

indhold

introduktion
dynamisk fuldmagt API
serialisering
eksempler

introduktion

en dynamisk fuldmagtsklasse er en klasse, der implementerer en liste over grænseflader, der er specificeret ved kørsel, således at en metodeindkaldelse gennem en af grænsefladerne på en forekomst af klassen vil blive kodet og sendt til et andet objekt gennem en uniforminterface. Således kan en dynamisk fuldmagts klasse bruges til at oprette atypesikker fuldmagtsobjekt til en liste over grænseflader uden at kræve forudgenerering af fuldmagtsklassen, f.eks.Metodeindkaldelser på en forekomst af en dynamisk fuldmagtsklasse er overført til en enkelt metode i instansens invokationhandler, og de er kodet med etjava.lang.reflect.Method objekt, der identificerer den metode, der blev påberåbt, og et array af typen Object, der indeholder argumenterne.

dynamiske fuldmagtsklasser er nyttige til et program eller bibliotek, der skal give typesikker reflekterende afsendelse af påkaldelser på objekter, der præsenterer interface API ‘ er. For eksempel kan en applikation bruge en dynamisk fuldmagtsklasse til at oprette et objekt, der implementerer flere vilkårlige hændelseslyttergrænseflader-grænseflader, der strækker sig java.util.EventListener – til at behandle en række begivenheder af forskellige typer på en ensartet måde, såsom ved at logge alle sådanne begivenheder til en fil.

dynamisk Fuldmagts klasse API

en dynamisk fuldmagts klasse (blot kaldet en fuldmagtsgruppe nedenfor) er en klasse, der implementerer en liste over grænsefladerspecificeret ved kørsel, når klassen oprettes.

en fuldmagtsgrænseflade er sådan en grænseflade, der erimplementeret af en fuldmagts klasse.

en fuldmagt er en forekomst af en fuldmagt.

oprettelse af en Fuldmagts klasse

Fuldmagts klasser, såvel som forekomster af dem, oprettes ved hjælp afDe statiske metoder i klassen java.lang.afspejle.Proxy.

metodenProxy.getProxyClass returnerer objektet java.lang.Class for en fuldmægtigsklasse givet en classloader og en række grænseflader. Fuldmagtsklassen vil blive definereti den angivne klasse loader og vil implementere alle de leverede grænseflader. Hvis der allerede er defineret en fuldmagts klasse for den samme permutation af grænseflader i klasselæseren, returneres den eksisterende fuldmagts klasse; ellers genereres en fuldmagts klasse for disse grænseflader dynamisk og defineres i klasselæseren.

der er flere begrænsninger på de parametre, der kan værebestået til Proxy.getProxyClass:

  • alleClass objekterne i interfaces – arrayet skal repræsentere grænseflader, ikkeklasser eller primitive typer.
  • ingen to elementer i interfaces arrayet kan referere til identiske Class objekter.
  • alle interfacetyperne skal være synlige ved navn gennem specificed class loader. Med andre ord, for klasse loadercl og hver grænseflade i, skal følgende udtryk være sandt:
     Class.forName(i.getName(), false, cl) == i
  • alle ikke-offentlige grænseflader skal være i samme pakke;ellers ville det ikke være muligt for fuldmagtsklassen at gennemføre alle grænsefladerne, uanset hvilken pakke den er defineret i.
  • For ethvert sæt medlemsmetoder af de angivne grænseflader, somhar samme signatur:
    • hvis returtypen for en af metoderne er en primitiv type orvoid, skal alle metoderne have den samme returtype.
    • ellers skal en af metoderne have en returtype, der kan tildeles alle returtyperne for resten af metoderne.
  • den resulterende fuldmagts klasse må ikke overstige nogen grænser, der pålægges klasser af den virtuelle maskine. For eksempel kan VM begrænse antallet af grænseflader, som en klasse kan implementere til 65535; i det tilfælde må størrelsen af interfaces arrayet ikke overstige 65535.

hvis nogen af disse begrænsninger overtrædes,Proxy.getProxyClassvil kaste enIllegalArgumentException. Hvisinterfaces array-argumentet eller et af dets elementer er null, vil en NullPointerException blive kastet.

Bemærk, at rækkefølgen af de angivne fuldmagtsgrænseflader erbetydelig: to anmodninger om en fuldmagts klasse med den sammekombination af grænseflader, men i en anden rækkefølge, vil resultere i to forskellige fuldmagts klasser. Fuldmægtige klasser er kendetegnet ved theorder af deres fuldmægtige grænseflader for at tilvejebringe deterministicmethod påkaldelseskodning i tilfælde, hvor to eller flere af fuldmægtiggrænsefladerne deler en metode med samme navn og parametersignatur; denne begrundelse er beskrevet mere detaljeret i afsnittet nedenfor med titlen metoder duplikeret iflere fuldmægtige grænseflader.

så at en ny fuldmægtig klasse ikke behøver at blive genereret hver gang Proxy.getProxyClass påberåbes med sameclass loader og liste over grænseflader, implementeringen af thedynamic fuldmægtig klasse API bør holde en cache af genererede fuldmagter, indtastet af deres tilsvarende læssere og interface liste.Implementeringen skal være forsigtig med ikke at henvise til klasselæssere, grænseflader og fuldmægtigklasser på en sådan måde, at det forhindres, at klasselæssere og alle deres klasser indsamles, når det er relevant.

fuldmægtig klasse egenskaber

en fuldmægtig klasse har følgende egenskaber:

  • fuldmægtige klasser er offentlige, endelige og ikke abstrakte.
  • det ukvalificerede navn på en fuldmægtig klasse er uspecificeret. Spaceof – klassenavnene, der begynder med strengen "$Proxy", skal dog reserveres til fuldmagtsklasser.
  • en fuldmægtig klasse udvider java.lang.reflect.Proxy.
  • en fuldmagtsklasse implementerer nøjagtigt de grænseflader, der er angivet atits oprettelse, i samme rækkefølge.
  • hvis en fuldmagtsklasse implementerer en ikke-offentlig grænseflade, vil den blive defineret i samme pakke som den pågældende grænseflade. Ellers er pakken af en fuldmægtig klasse også uspecificeret. Bemærk, at pakkeforsegling ikke forhindrer, at en fuldmagtsklasse defineres med succes i en bestemt pakke ved kørsel, og heller ikke vilklasser, der allerede er defineret i den samme klasselæsser og den samme pakke med bestemte underskrivere.
  • da en fuldmagtsklasse implementerer alle de grænseflader, der er specificeret ved oprettelsen,vil påberåbelse af getInterfaces på densClass objekt returnere et array, der indeholder den samme liste over grænseflader (i den rækkefølge, der er angivet ved oprettelsen), påberåbelse af getMethods på dens Class objekt vil returnere en række Method objekter, der inkludereralle metoderne i disse grænseflader, og påberåbelse afgetMethod vil finde metoder i fuldmagten grænseflader som forventet.
  • Proxy.isProxyClass – metoden Returnerer SAND, hvis den er bestået en fuldmægtig klasse – en klasse returneret afProxy.getProxyClass eller klassen af et objekt returneret af Proxy.newProxyInstance – og falsk ellers. Detpålideligheden af denne metode er vigtig for evnen til at bruge denat træffe sikkerhedsbeslutninger, så dens gennemførelse bør ikke baretest, hvis den pågældende klasse strækker sigjava.lang.reflect.Proxy.
  • java.security.ProtectionDomain for en fuldmægtig er den samme som for systemklasser, der er indlæst af bootstrapclass-læsseren, såsom java.lang.Object, fordi koden for en fuldmægtigsklasse genereres af systemkode, der er tillid til. Dette beskyttelsesdomæne vil typisk blive tildeltjava.security.AllPermission.

oprettelse af en Fuldmagtsinstans

hver fuldmagts klasse har en offentlig konstruktør, der tager enargument, en implementering af grænsefladen InvocationHandler.

hver fuldmægtiginstans har et tilknyttet påkaldelseshåndteringsobjekt,det, der blev sendt til dets konstruktør. I stedet for at skulle bruge reflection API for at få adgang til den offentlige konstruktør, kan der også oprettes en fuldmægtiginstans ved at kalde Proxy.newProxyInstance – metoden, som kombinerer handlinger ved at kalde Proxy.getProxyClass med invokingkonstruktøren med en påkaldelseshandler.Proxy.newProxyInstance kasterIllegalArgumentException af de samme grunde, som Proxy.getProxyClass gør.

egenskaber for Fuldmagtsinstans

en fuldmagtsinstans har følgende egenskaber:

  • givet en fuldmagtsinstans proxy og en afinterfaces implementeret af sin fuldmagts klasse Foo, vil det følgende udtryk vende tilbage sandt:
     proxy instanceof Foo

    og følgende cast-operation vil lykkes (snarere end at kaste en ClassCastException):

     (Foo) proxy
  • den statiske Proxy.getInvocationHandler – metode vilreturnere den påkaldelseshåndterer, der er knyttet til fuldmagtsinstansen, der er bestået som sit argument. Hvis objektet, der overføres tilProxy.getInvocationHandler , ikke er en fuldmægtig instans,vil en IllegalArgumentException blive kastet.
  • en interface metode påkaldelse på en fuldmægtig instans vil blive kodet og afsendt til invocation handlersinvoke metode som beskrevet nedenfor.

    selve fuldmagtsinstansen vil blive bestået som det første argumentof invoke, som er af typen Object.

    det andet argument, der overføres tilinvoke, vil være java.lang.reflect.Method – forekomsten svarende tilinterface-metoden, der påberåbes på fuldmagtsinstansen. Den deklarerende klasse af Method – objektet vil være den grænseflade, som metoden blev erklæret i, hvilket kan være en superinterface af fuldmægtigeninterface, som fuldmægtigklassen arver metoden igennem.

    det tredje argument, der overføres til invoke, vil være anarray af objekter, der indeholder værdierne for de argumenter, der er bestået imetoden påkaldelse på fuldmægtiginstansen. Argumenter for primitivtyper er pakket ind i en forekomst af den passende primitivindpakningsklasse, såsom java.lang.Integer ellerjava.lang.Boolean. Implementeringen afinvoke – metoden er fri til at ændre indholdet af thisarray.

    den værdi, der returneres af invoke – metoden, bliverreturværdien af metodens påkaldelse på fuldmagtsinstansen. Hvis den deklarerede returværdi af grænseflademetoden er en primitivtype,skal værdien, der returneres med invoke, være en forekomst af den tilsvarende primitive indpakningsklasse; ellers skal den være en type, der kan tildeles den deklarerede returtype. Hvis værdien returneret af invoke er null, og grænseflademetodens returtype er primitiv, vil enNullPointerException blive kastet af metodeninvokation på fuldmagtsinstansen. Hvis værdien, der returneres afinvoke, ellers ikke er kompatibel med metodens erklærede returtype som beskrevet ovenfor, kastes enClassCastException af fuldmægtiginstansen.

    hvis en undtagelse kastes af invoke – metoden, vil den også blive kastet af metoden påkaldelse på fuldmagtsinstansen.Undtagelsestypen skal kunne tildeles enten en hvilken som helst af de undtagelsestyper, der er angivet i grænseflademetodens underskrift, eller til de ikke-markerede undtagelsestyperjava.lang.RuntimeException ellerjava.lang.Error. Hvis en kontrolleret undtagelse kastes afinvoke, der ikke kan tildeles nogen af de undtagelsestyper, der er erklæret i throws – klausulen i grænseflademetoden, vil en UndeclaredThrowableException blive kastet ved metoden påkaldelse på fuldmægtiginstansen. UndeclaredThrowableException vil blive konstrueret medden undtagelse, der blev kastet af invoke – metoden.

  • en påkaldelse afhashCode, equals ellertoString metoder, der er angivet i java.lang.Objectpå en fuldmægtiginstans, vil blive kodet og sendt til invokationshåndtererens invoke metode på samme måde som interface method invocations er indkodet og afsendt som beskrevet ovenfor. Den erklærende klasse af Method objektet overført til invokevil være java.lang.Object. Andre offentlige metoder til en fuldmagt, der er arvet fra java.lang.Object, overstyres ikke af en fuldmagtsklasse, så påkaldelser af disse metoder opfører sig som de gør i tilfælde af java.lang.Object.

metoder duplikeret i Multipleproksygrænseflader

når to eller flere grænseflader i en fuldmægtigsklasse indeholder en metodemed samme navn og parametersignatur bliver rækkefølgen af fuldmagtens grænseflader signifikant. Når en sådan duplikatmetode påberåbes på en fuldmægtiginstans ,vil Method – objektet, der sendes til påkaldelseshåndtereren, ikke nødvendigvis være den, hvis erklæringsklasse kan tildeles fra referencetypen den grænseflade, som fuldmægtigens metode blev påberåbt gennem. Denne begrænsning eksisterer, fordi den tilsvarende metodeimplementering i den genererede fuldmagtsklasse ikke kan bestemme, hvilken grænseflade den blev påberåbt gennem. Derfor, når en duplikatmetode er invokedon en fuldmægtig instans, Method objekt for metodeni den forreste grænseflade, der indeholder metoden (enten direkte eller arvet gennem en superinterface) i fuldmægtig klassens liste overgrænseflader overføres til invocation handlersinvoke metode, uanset hvilken referencetype metoden påkaldelse fandt sted.

hvis en fuldmagtsgrænseflade indeholder en metode med samme navn og parametersignatur som hashCode,equals eller toString metoder tiljava.lang.Object, når en sådan metode påberåbes på ca. Med andre ord går de offentlige, ikke-endelige metoder tiljava.lang.Object logisk forud for alle fuldmagtsgrænseflader til bestemmelse af, hvilke Methodobjekt der skal overføres til invokationshåndtereren.

Bemærk også, at når en duplikatmetode sendes til aninvokationshåndterer, må invoke – metoden kun kaste kontrollerede undtagelsestyper, der kan tildeles en af undtagelsestyperne i throws – klausulen i metoden i alle de fuldmagtsgrænseflader, som den kan påberåbes gennem. Hvis invoke – metoden kaster en kontrolleret undtagelse, der ikke kan tildeles nogen af de undtagelsestyper, der er erklæret ved metoden i en af de fuldmægtige grænseflader, som den kan påberåbes gennem, vil anunchecked UndeclaredThrowableException blive kastet af påkaldelsen på fuldmægtiginstansen. Denne begrænsning betyder, at ikke alle undtagelsestyper, der returneres ved at påberåbe siggetExceptionTypesMethod objectpased til invoke – metoden, nødvendigvis kan smidesmed succes ved invoke – metoden.

serialisering

sidenjava.lang.reflect.Proxy implementerer java.io.Serializable kan fuldmægtige tilfælde væreserialiseret som beskrevet i dette afsnit. Hvis en fuldmægtiginstansindeholder en påkaldelseshåndterer, der ikke kan tildelesjava.io.Serializable, vil enjava.io.NotSerializableException blive kastet, hvis en sådan forekomst er skrevet til enjava.io.ObjectOutputStream. Bemærk, at implementering af java.io.Externalizable for fuldmagter har den samme effekt med hensyn til serialisering som implementering afjava.io.Serializable: writeExternalog readExternal metoder tilExternalizable interface vil aldrig blive påberåbt på aproksi-instans (eller en påkaldelseshandler) som en del af densserialiseringsproces. Som med alle Class objekter erClass objektet for en fuldmægtig klasse altidserialiserbar.

en fuldmagtsklasse har ingen serialiserbare felter og en serialVersionUID af 0L. Med andre ord, når Class objektet til en fuldmagtsklasse overføres tilstatisk lookup metode tiljava.io.ObjectStreamClass, vil den returnerede ObjectStreamClass instans have følgendeegenskaber:

  • påkaldelse af dens getSerialVersionUID metode vilreturnere 0L.
  • påberåber sin getFields metode vil returnere en arrayof længde nul.
  • påberåber sin getField metode med ethvertString argument vender tilbage null.

streamprotokollen til Objektserialisering understøtter en typekode med navnet TC_PROXYCLASSDESC, som er et terminalsymbol i grammatikken for streamformatet; dens type og værdi er defineret af følgende konstant felt ijava.io.ObjectStreamConstants interface:

 final static byte TC_PROXYCLASSDESC = (byte)0x7D;

grammatikken indeholder også følgende to regler, den førstebliver en alternativ udvidelse af den oprindelige nyeklassedescrule:

nyklassedesc:
TC_PROXYCLASSDESC nyhåndtags fuldmagtsinfo

fuldmagtsinfo:
(int)<count>fuldmagtsgrænseflade klasseanmærkningsuperclassdesc

fuldmagtsgrænsefladenavn:
(utf)

når en ObjectOutputStream serialiserer classdescriptoren for en klasse, der er en fuldmagtsklasse, som bestemt ved at omgå dens Class objekt tilProxy.isProxyClass metode, bruger denTC_PROXYCLASSDESC typekode i stedet forTC_CLASSDESC efter ovenstående regler. Iudvidelsen af fuldmagtsinfo er sekvensen af grænsefladeelementer navnene på alle de grænseflader, der er implementeret af fuldmagtsgruppen, i den rækkefølge, at de returneres ved at påberåbe getInterfaces – metoden på Class – objektet. Klasseannotationen og superklassensc-elementer har samme betydning som de gør i klassedescinfo-reglen. For en fuldmægtig klasse, superklassedescis klassebeskrivelsen for sin superklasse,java.lang.reflect.Proxy; inklusive denne beskrivelse tillader udviklingen af den serielle repræsentation af klassen Proxy for fuldmægtige tilfælde.

for ikke-fuldmægtige klasser kalder ObjectOutputStream itsprotected annotateClass metode til at tillade underklasser tilskriv brugerdefinerede data til strømmen for en bestemt klasse. For fuldmagter, i stedet for annotateClass, kaldes følgendemetode i java.io.ObjectOutputStream med Class objekt til fuldmægtigklassen:

 protected void annotateProxyClass(Class cl) throws IOException;

standardimplementeringen af annotateProxyClass iObjectOutputStream gør intet.

når en ObjectInputStream støder på typekodenTC_PROXYCLASSDESC, deserialiserer den classdescriptoren for en fuldmagtsklasse fra strømmen, formateret sombeskrevet ovenfor. I stedet for at kalde sin resolveClassmetode til at løse Class objektet til classdescriptoren, kaldes følgende metode ijava.io.ObjectInputStream :

 protected Class resolveProxyClass(String interfaces) throws IOException, ClassNotFoundException;

listen over grænsefladenavne, der blev deserialiseret i fuldmagtsbeskrivelsen, sendes som interfaces argumentto resolveProxyClass.

standardimplementeringen af resolveProxyClass iObjectInputStream returnerer resultaterne af opkaldProxy.getProxyClass med listen overClass objekter til grænsefladerne navngivet i parametereninterfaces. Det Class objekt, der anvendes for hvert interfacenavni, er den værdi, der genindstilles ved at ringe til

 Class.forName(i, false, loader)

hvorloaderer den første ikke-null-klasselæsser op i udførelses-stakken, ellernullhvis der ikke er nogen ikke-nul-klasselastere på stakken. Dette er det samme klasse loader valg foretaget af Default opførsel afresolveClassmetoden. Denne samevalue afloaderer også klassen loader gået tilProxy.getProxyClass. HvisProxy.getProxyClasskaster enIllegalArgumentException, vilresolveClasskaste enClassNotFoundExceptionindeholdendeIllegalArgumentException.

da en fuldmægtigsklasse aldrig har sine egne serialiserbare felter, består klassedata i stream-repræsentationen af en fuldmægtiginstans helt af forekomstdataene for sin superklasse,java.lang.reflect.Proxy. Proxy har oneserialiserbart felt, h, som indeholder invocationhandler for fuldmagtsinstansen.

eksempler

her er et simpelt eksempel, der udskriver en besked før ogefter en metode påkaldelse på et objekt, der implementerer en vilkårligliste over grænseflader:

public interface Foo { Object bar(Object obj) throws BazException;}public class FooImpl implements Foo { Object bar(Object obj) throws BazException { // ... }}public class DebugProxy implements java.lang.reflect.InvocationHandler { private Object obj; public static Object newInstance(Object obj) { return java.lang.reflect.Proxy.newProxyInstance( obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), new DebugProxy(obj)); } private DebugProxy(Object obj) { this.obj = obj; } public Object invoke(Object proxy, Method m, Object args) throws Throwable { Object result; try { System.out.println("before method " + m.getName()); result = m.invoke(obj, args); } catch (InvocationTargetException e) { throw e.getTargetException(); } catch (Exception e) { throw new RuntimeException("unexpected invocation exception: " + e.getMessage()); } finally { System.out.println("after method " + m.getName()); } return result; }}

at konstruere en DebugProxy til en implementering af Foo interface og kalde en af dens metoder:

 Foo foo = (Foo) DebugProxy.newInstance(new FooImpl()); foo.bar(null);

her er et eksempel på en utility invocation handlerklasse, der giver standard fuldmægtig adfærd for metoder arvet frajava.lang.Object og implementerer delegering af visseproksi metode påkaldelser til forskellige objekter afhængigt afinterface af den påberåbte metode:

import java.lang.reflect.*;public class Delegator implements InvocationHandler { // preloaded Method objects for the methods in java.lang.Object private static Method hashCodeMethod; private static Method equalsMethod; private static Method toStringMethod; static { try { hashCodeMethod = Object.class.getMethod("hashCode", null); equalsMethod = Object.class.getMethod("equals", new Class { Object.class }); toStringMethod = Object.class.getMethod("toString", null); } catch (NoSuchMethodException e) { throw new NoSuchMethodError(e.getMessage()); } } private Class interfaces; private Object delegates; public Delegator(Class interfaces, Object delegates) { this.interfaces = (Class) interfaces.clone(); this.delegates = (Object) delegates.clone(); } public Object invoke(Object proxy, Method m, Object args) throws Throwable { Class declaringClass = m.getDeclaringClass(); if (declaringClass == Object.class) { if (m.equals(hashCodeMethod)) { return proxyHashCode(proxy); } else if (m.equals(equalsMethod)) { return proxyEquals(proxy, args); } else if (m.equals(toStringMethod)) { return proxyToString(proxy); } else { throw new InternalError( "unexpected Object method dispatched: " + m); } } else { for (int i = 0; i < interfaces.length; i++) { if (declaringClass.isAssignableFrom(interfaces)) { try { return m.invoke(delegates, args); } catch (InvocationTargetException e) { throw e.getTargetException(); } } } return invokeNotDelegated(proxy, m, args); } } protected Object invokeNotDelegated(Object proxy, Method m, Object args) throws Throwable { throw new InternalError("unexpected method dispatched: " + m); } protected Integer proxyHashCode(Object proxy) { return new Integer(System.identityHashCode(proxy)); } protected Boolean proxyEquals(Object proxy, Object other) { return (proxy == other ? Boolean.TRUE : Boolean.FALSE); } protected String proxyToString(Object proxy) { return proxy.getClass().getName() + '@' + Integer.toHexString(proxy.hashCode()); }}

underklasser af Delegator kan tilsidesætteinvokeNotDelegatedfor at implementere adfærd af fuldmægtigmetodpåkaldelser,der ikke skal delegeres direkte til andre objekter, og de kan tilsidesætte proxyHashCode,proxyEquals og proxyToStringforoverride standardadfærden for de metoder, fuldmægtig arverfra java.lang.Object.

at konstruere en Delegator til en implementering af Foo interface:

 Class proxyInterfaces = new Class { Foo.class }; Foo foo = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(), proxyInterfaces, new Delegator(proxyInterfaces, new Object { new FooImpl() }));

Bemærk, at implementeringen af Delegator – klassen ovenfor er beregnet til at være mere illustrativ end optimeret; for eksempel, i stedet for caching og sammenligning af Methodobjekter for hashCode, equals ogtoString metoder, kunne det bare matche dem med derestring navne, fordi ingen af disse metodenavne er overbelastet ijava.lang.Object.

Skriv et svar

Din e-mailadresse vil ikke blive publiceret.