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
:
- alle
Class
objekterne iinterfaces
– arrayet skal repræsentere grænseflader, ikkeklasser eller primitive typer. - ingen to elementer i
interfaces
arrayet kan referere til identiskeClass
objekter. - alle interfacetyperne skal være synlige ved navn gennem specificed class loader. Med andre ord, for klasse loader
cl
og hver grænsefladei
, 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.getProxyClass
vil 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 afgetMethods
på densClass
objekt vil returnere en rækkeMethod
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 afProxy.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åsomjava.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 klasseFoo
, 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 enIllegalArgumentException
blive kastet. - en interface metode påkaldelse på en fuldmægtig instans vil blive kodet og afsendt til invocation handlers
invoke
metode som beskrevet nedenfor.selve fuldmagtsinstansen vil blive bestået som det første argumentof
invoke
, som er af typenObject
.det andet argument, der overføres til
invoke
, vil værejava.lang.reflect.Method
– forekomsten svarende tilinterface-metoden, der påberåbes på fuldmagtsinstansen. Den deklarerende klasse afMethod
– 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åsomjava.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 medinvoke
, 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 afinvoke
ernull
, 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 ithrows
– klausulen i grænseflademetoden, vil enUndeclaredThrowableException
blive kastet ved metoden påkaldelse på fuldmægtiginstansen.UndeclaredThrowableException
vil blive konstrueret medden undtagelse, der blev kastet afinvoke
– metoden. - en påkaldelse af
hashCode
,equals
ellertoString
metoder, der er angivet ijava.lang.Object
på en fuldmægtiginstans, vil blive kodet og sendt til invokationshåndtererensinvoke
metode på samme måde som interface method invocations er indkodet og afsendt som beskrevet ovenfor. Den erklærende klasse afMethod
objektet overført tilinvoke
vil værejava.lang.Object
. Andre offentlige metoder til en fuldmagt, der er arvet frajava.lang.Object
, overstyres ikke af en fuldmagtsklasse, så påkaldelser af disse metoder opfører sig som de gør i tilfælde afjava.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 Method
objekt 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 siggetExceptionTypes
på Method
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
: writeExternal
og 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 vilreturnere0L
. - påberåber sin
getFields
metode vil returnere en arrayof længde nul. - påberåber sin
getField
metode med ethvertString
argument vender tilbagenull
.
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 resolveClass
metode 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)
hvorloader
er den første ikke-null-klasselæsser op i udførelses-stakken, ellernull
hvis der ikke er nogen ikke-nul-klasselastere på stakken. Dette er det samme klasse loader valg foretaget af Default opførsel afresolveClass
metoden. Denne samevalue afloader
er også klassen loader gået tilProxy.getProxyClass
. HvisProxy.getProxyClass
kaster enIllegalArgumentException
, vilresolveClass
kaste enClassNotFoundException
indeholdendeIllegalArgumentException
.
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ætteinvokeNotDelegated
for at implementere adfærd af fuldmægtigmetodpåkaldelser,der ikke skal delegeres direkte til andre objekter, og de kan tilsidesætte proxyHashCode
,proxyEquals
og proxyToString
foroverride 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 Method
objekter for hashCode
, equals
ogtoString
metoder, kunne det bare matche dem med derestring navne, fordi ingen af disse metodenavne er overbelastet ijava.lang.Object
.