詳解設(shè)計(jì)模式中的proxy代理模式及在Java程序中的實(shí)現(xiàn)
一、代理模式定義
給某個(gè)對(duì)象提供一個(gè)代理對(duì)象,并由代理對(duì)象控制對(duì)于原對(duì)象的訪問(wèn),即客戶不直接操控原對(duì)象,而是通過(guò)代理對(duì)象間接地操控原對(duì)象。
著名的代理模式的例子就是引用計(jì)數(shù)(reference counting): 當(dāng)需要一個(gè)復(fù)雜對(duì)象的多份副本時(shí), 代理模式可以結(jié)合享元模式以減少存儲(chǔ)器的用量。典型做法是創(chuàng)建一個(gè)復(fù)雜對(duì)象以及多個(gè)代理者, 每個(gè)代理者會(huì)引用到原本的對(duì)象。而作用在代理者的運(yùn)算會(huì)轉(zhuǎn)送到原本對(duì)象。一旦所有的代理者都不存在時(shí), 復(fù)雜對(duì)象會(huì)被移除。
要理解代理模式很簡(jiǎn)單,其實(shí)生活當(dāng)中就存在代理模式:
我們購(gòu)買(mǎi)火車(chē)票可以去火車(chē)站買(mǎi),但是也可以去火車(chē)票代售處買(mǎi),此處的火車(chē)票代售處就是火車(chē)站購(gòu)票的代理,即我們?cè)诖埸c(diǎn)發(fā)出買(mǎi)票請(qǐng)求,代售點(diǎn)會(huì)把請(qǐng)求發(fā)給火車(chē)站,火車(chē)站把購(gòu)買(mǎi)成功響應(yīng)發(fā)給代售點(diǎn),代售點(diǎn)再告訴你。
但是代售點(diǎn)只能買(mǎi)票,不能退票,而火車(chē)站能買(mǎi)票也能退票,因此代理對(duì)象支持的操作可能和委托對(duì)象的操作有所不同。
再舉一個(gè)寫(xiě)程序會(huì)碰到的一個(gè)例子:
如果現(xiàn)在有一個(gè)已有項(xiàng)目(你沒(méi)有源代碼,只能調(diào)用它)能夠調(diào)用 int compute(String exp1) 實(shí)現(xiàn)對(duì)于后綴表達(dá)式的計(jì)算,你想使用這個(gè)項(xiàng)目實(shí)現(xiàn)對(duì)于中綴表達(dá)式的計(jì)算,那么你可以寫(xiě)一個(gè)代理類(lèi),并且其中也定義一個(gè)compute(String exp2),這個(gè)exp2參數(shù)是中綴表達(dá)式,因此你需要在調(diào)用已有項(xiàng)目的 compute() 之前將中綴表達(dá)式轉(zhuǎn)換成后綴表達(dá)式(Preprocess),再調(diào)用已有項(xiàng)目的compute(),當(dāng)然你還可以接收到返回值之后再做些其他操作比如存入文件(Postprocess),這個(gè)過(guò)程就是使用了代理模式。
在平時(shí)用電腦也會(huì)碰到代理模式的應(yīng)用:
遠(yuǎn)程代理:我們?cè)趪?guó)內(nèi)因?yàn)镚FW,所以不能訪問(wèn) facebook,我們可以用翻墻(設(shè)置代理)的方法訪問(wèn)。訪問(wèn)過(guò)程是:
(1)用戶把HTTP請(qǐng)求發(fā)給代理
(2)代理把HTTP請(qǐng)求發(fā)給web服務(wù)器
(3)web服務(wù)器把HTTP響應(yīng)發(fā)給代理
(4)代理把HTTP響應(yīng)發(fā)回給用戶
二、靜態(tài)代理
所謂靜態(tài)代理, 就是在編譯階段就生成代理類(lèi)來(lái)完成對(duì)代理對(duì)象的一系列操作。下面是代理模式的結(jié)構(gòu)類(lèi)圖:
1、代理模式的參與者
代理模式的角色分四種:
主題接口: 即代理類(lèi)的所實(shí)現(xiàn)的行為接口。
目標(biāo)對(duì)象: 也就是被代理的對(duì)象。
代理對(duì)象: 用來(lái)封裝真是主題類(lèi)的代理類(lèi)
客戶端
下面是代理模式的類(lèi)圖結(jié)構(gòu):
2、代理模式的實(shí)現(xiàn)思路
代理對(duì)象和目標(biāo)對(duì)象均實(shí)現(xiàn)同一個(gè)行為接口。
代理類(lèi)和目標(biāo)類(lèi)分別具體實(shí)現(xiàn)接口邏輯。
在代理類(lèi)的構(gòu)造函數(shù)中實(shí)例化一個(gè)目標(biāo)對(duì)象。
在代理類(lèi)中調(diào)用目標(biāo)對(duì)象的行為接口。
客戶端想要調(diào)用目標(biāo)對(duì)象的行為接口,只能通過(guò)代理類(lèi)來(lái)操作。
3、靜態(tài)代理的實(shí)例
下面以一個(gè)延遲加載的例子來(lái)說(shuō)明一下靜態(tài)代理。我們?cè)趩?dòng)某個(gè)服務(wù)系統(tǒng)時(shí), 加載某一個(gè)類(lèi)時(shí)可能會(huì)耗費(fèi)很長(zhǎng)時(shí)間。為了獲取更好的性能, 在啟動(dòng)系統(tǒng)的時(shí)候, 我們往往不去初始化這個(gè)復(fù)雜的類(lèi), 取而代之的是去初始化其代理類(lèi)。這樣將耗費(fèi)資源多的方法使用代理進(jìn)行分離, 可以加快系統(tǒng)的啟動(dòng)速度, 減少用戶等待的時(shí)間。
定義一個(gè)主題接口
public interface Subject { public void sayHello(); public void sayGoodBye(); }
定義一個(gè)目標(biāo)類(lèi), 并實(shí)現(xiàn)主題接口
public class RealSubject implements Subject { public void sayHello() { System.out.println("Hello World"); } public void sayGoodBye() { System.out.println("GoodBye World"); } }
定義一個(gè)代理類(lèi), 來(lái)代理目標(biāo)對(duì)象。
public class StaticProxy implements Subject { Private RealSubject realSubject = null; public StaticProxy() {} public void sayHello() { //用到時(shí)候才加載, 懶加載 if(realSubject == null) { realSubject = new RealSubject(); } realSubject.sayHello(); } //sayGoodbye方法同理 ... }
定義一個(gè)客戶端
public class Client { public static void main(String [] args) { StaticProxy sp = new StaticProxy(); sp.sayHello(); sp.sayGoodBye(); } }
以上就是靜態(tài)代理的一個(gè)簡(jiǎn)單測(cè)試?yán)?。感覺(jué)可能沒(méi)有實(shí)際用途。然而并非如此。使用代理我們還可以將目標(biāo)對(duì)象的方法進(jìn)行改造, 比如數(shù)據(jù)庫(kù)連接池中創(chuàng)建了一系列連接, 為了保證不頻繁的打開(kāi)連接,這些連接是幾乎不會(huì)關(guān)閉的。然而我們編程總有習(xí)慣去將打開(kāi)的Connection去close。 這樣我們就可以利用代理模式來(lái)重新代理Connection接口中的close方法, 改變?yōu)榛厥盏綌?shù)據(jù)庫(kù)連接池中而不是真正的執(zhí)行Connection#close方法。其他的例子還有很多, 具體需要自己體會(huì)。
三、動(dòng)態(tài)代理
動(dòng)態(tài)代理是指在運(yùn)行時(shí)動(dòng)態(tài)生成代理類(lèi)。即,代理類(lèi)的字節(jié)碼將在運(yùn)行時(shí)生成并載入當(dāng)前代理的 ClassLoader。與靜態(tài)處理類(lèi)相比,動(dòng)態(tài)類(lèi)有諸多好處。
不需要為真實(shí)主題寫(xiě)一個(gè)形式上完全一樣的封裝類(lèi),假如主題接口中的方法很多,為每一個(gè)接口寫(xiě)一個(gè)代理方法也很麻煩。如果接口有變動(dòng),則真實(shí)主題和代理類(lèi)都要修改,不利于系統(tǒng)維護(hù);
使用一些動(dòng)態(tài)代理的生成方法甚至可以在運(yùn)行時(shí)制定代理類(lèi)的執(zhí)行邏輯,從而大大提升系統(tǒng)的靈活性。
生成動(dòng)態(tài)代理的方法有很多: JDK中自帶動(dòng)態(tài)代理, CGlib, javassist等。這些方法各有優(yōu)缺點(diǎn)。本文主要探究JDK中的動(dòng)態(tài)代理的使用和源碼分析。
下面用一個(gè)實(shí)例講解一下JDK中動(dòng)態(tài)代理的用法:
public class dynamicProxy implements InvocationHandler { private RealSubject = null; public Object invoke(Object proxy, Method method, Object[] args){ if(RealSubject == null) { RealSubject = new RealSubject(); } method.invoke(RealSubject, args); return RealSubject; } }
客戶端代碼實(shí)例
public class Client { public static void main(Strings[] args) { Subject subject = (Subject)Proxy.newInstance(ClassLoader.getSystemLoader(), RealSubject.class.getInterfaces(), new DynamicProxy()); Subject.sayHello(); Subject.sayGoodBye(); } }
從上面的代碼可以看出, 要利用JDK中的動(dòng)態(tài)代理。利用靜態(tài)方法Proxy.newInstance(ClassLoader, Interfaces[], InvokeHandler)可以創(chuàng)建一個(gè)動(dòng)態(tài)代理類(lèi)。 newInstance方法有三個(gè)參數(shù), 分別表示類(lèi)加載器, 一個(gè)希望該代理類(lèi)實(shí)現(xiàn)的接口列表, 以及實(shí)現(xiàn)InvokeHandler接口的實(shí)例。 動(dòng)態(tài)代理將每個(gè)方法的執(zhí)行過(guò)程則交給了Invoke方法處理。
JDK動(dòng)態(tài)代理要求, 被代理的必須是個(gè)接口, 單純的類(lèi)則不行。JDK動(dòng)態(tài)代理所生成的代理類(lèi)都會(huì)繼承Proxy類(lèi),同時(shí)代理類(lèi)會(huì)實(shí)現(xiàn)所有你傳入的接口列表。因此可以強(qiáng)制類(lèi)型轉(zhuǎn)換成接口類(lèi)型。 下面是Proxy的結(jié)構(gòu)圖。
可以看出Proxy全是靜態(tài)方法, 因此如果代理類(lèi)沒(méi)有實(shí)現(xiàn)任何接口, 那么他就是Proxy類(lèi)型, 沒(méi)有實(shí)例方法。
當(dāng)然加入你要是非要代理一個(gè)沒(méi)有實(shí)現(xiàn)某個(gè)接口的類(lèi), 同時(shí)該類(lèi)的方法與其他接口定義的方法相同, 利用反射也是可以輕松實(shí)現(xiàn)的。
public class DynamicProxy implements InvokeHandler { //你想代理的類(lèi) private TargetClass targetClass = null; //初始化該類(lèi) public DynamicProxy(TargetClass targetClass) { this.targetClass = targetClass; } public Object invoke(Object proxy, Method method, Object[] args) { //利用反射獲取你想代理的類(lèi)的方法 Method myMethod = targetClass.getClass().getDeclaredMethod(method.getName(), method.getParameterTypes()); myMethod.setAccessible(true); return myMethod.invoke(targetClass, args); } }
四、JDK動(dòng)態(tài)代理源碼分析(JDK7)
看了上面的例子, 我們只是簡(jiǎn)單會(huì)用動(dòng)態(tài)代理。但是對(duì)于代理類(lèi)是如何創(chuàng)建出來(lái)的, 是誰(shuí)調(diào)用Invoke方法等還云里霧里。下面通過(guò)分析
1、代理對(duì)象是如何創(chuàng)建出來(lái)的?
首先看Proxy.newInstance方法的源碼:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { } //獲取接口信息 final Class<?>[] intfs = interfaces.clone(); final SecurityManager sm = System.getSecurityManager(); if (sm != null) { checkProxyAccess(Reflection.getCallerClass(), loader, intfs); } //生成代理類(lèi) Class<?> cl = getProxyClass0(loader, intfs); // ...OK我們先看前半截 }
從源碼看出代理類(lèi)的生成是依靠getProxyClass0這個(gè)方法, 接下來(lái)看getProxyClass0源碼:
private static Class<?> getProxyClass0(ClassLoader loader, Class<?>... interfaces) { //接口列表數(shù)目不能超過(guò)0xFFFF if (interfaces.length > 65535) { throw new IllegalArgumentException("interface limit exceeded"); } //注意這里, 下面詳細(xì)解釋 return proxyClassCache.get(loader, interfaces); }
對(duì)proxyClassCache.get的解釋是: 如果實(shí)現(xiàn)接口列表的代理類(lèi)已經(jīng)存在,那么直接從cache中拿。如果不存在, 則通過(guò)ProxyClassFactory生成一個(gè)。
在看proxyClassCache.get源碼之前,先簡(jiǎn)單了解一下proxyClassCache:
private static final WeakCache<ClassLoader, Class<?>[], Class<?>> proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
proxyClassCache是一個(gè)WeakCache類(lèi)型的緩存, 它的構(gòu)造函數(shù)有兩個(gè)參數(shù), 其中一個(gè)就是用于生成代理類(lèi)的ProxyClassFactory, 下面是proxyClassCache.get的源碼:
final class WeakCache<K, P, V> { ... public V get(K key, P parameter) {} }
這里K表示key, P表示parameters, V表示value
public V get(K key, P parameter) { //java7 NullObject判斷方法, 如果parameter為空則拋出帶有指定消息的異常。 如果不為空則返回。 Objects.requireNonNull(parameter); //清理持有弱引用的WeakHashMap這種數(shù)據(jù)結(jié)構(gòu),一般用于緩存 expungeStaleEntries(); //從隊(duì)列中獲取cacheKey Object cacheKey = CacheKey.valueOf(key, refQueue); //利用懶加載的方式填充Supplier, Concurrent是一種線程安全的map ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey); if (valuesMap == null) { ConcurrentMap<Object, Supplier<V>> oldValuesMap = map.putIfAbsent(cacheKey, valuesMap = new ConcurrentHashMap<>()); if (oldValuesMap != null) { valuesMap = oldValuesMap; } } // create subKey and retrieve the possible Supplier<V> stored by that // subKey from valuesMap Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter)); Supplier<V> supplier = valuesMap.get(subKey); Factory factory = null; while (true) { if (supplier != null) { // 從supplier中獲取Value,這個(gè)Value可能是一個(gè)工廠或者Cache的實(shí) //下面這三句代碼是核心代碼, 返回實(shí)現(xiàn)InvokeHandler的類(lèi)并包含了所需要的信息。 V value = supplier.get(); if (value != null) { return value; } } // else no supplier in cache // or a supplier that returned null (could be a cleared CacheValue // or a Factory that wasn't successful in installing the CacheValue) //下面這個(gè)過(guò)程就是填充supplier的過(guò)程 if(factory == null) { //創(chuàng)建一個(gè)factory } if(supplier == null) { //填充supplier }else { //填充supplier } }
while循環(huán)的作用就是不停的獲取實(shí)現(xiàn)InvokeHandler的類(lèi), 這個(gè)類(lèi)可以是從緩存中拿到,也可是是從proxyFactoryClass生成的。
Factory是一個(gè)實(shí)現(xiàn)了Supplier<V>接口的內(nèi)部類(lèi)。這個(gè)類(lèi)覆蓋了get方法, 在get方法中調(diào)用了類(lèi)型為proxyFactoryClass的實(shí)例方法apply。這個(gè)方法才是真正創(chuàng)建代理類(lèi)的方法。下面看ProxyFactoryClass#apply方法的源碼:
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) { Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length); for (Class<?> intf : interfaces) { /* Verify that the class loader resolves the name of this interface to the same Class object.*/ Class<?> interfaceClass = null; try { //加載每一個(gè)接口運(yùn)行時(shí)的信息 interfaceClass = Class.forName(intf.getName(), false, loader); } catch (ClassNotFoundException e) { } //如果使用你自己的classload加載的class與你傳入的class不相等,拋出異常 if (interfaceClass != intf) { throw new IllegalArgumentException( intf + " is not visible from class loader"); } //如果傳入不是一個(gè)接口類(lèi)型 if (!interfaceClass.isInterface()) { throw new IllegalArgumentException( interfaceClass.getName() + " is not an interface"); } //驗(yàn)證接口是否重復(fù) if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) { throw new IllegalArgumentException("repeated interface: " + interfaceClass.getName()); } } String proxyPkg = null; // package to define proxy class in /* Record the package of a non-public proxy interface so that the proxy class will be defined in the same package. * Verify that all non-public proxy interfaces are in the same package. */ //這一段是看你傳入的接口中有沒(méi)有不是public的接口,如果有,這些接口必須全部在一個(gè)包里定義的,否則拋異常 for (Class<?> intf : interfaces) { int flags = intf.getModifiers(); if (!Modifier.isPublic(flags)) { String name = intf.getName(); int n = name.lastIndexOf('.'); String pkg = ((n == -1) ? "" : name.substring(0, n + 1)); if (proxyPkg == null) { proxyPkg = pkg; } else if (!pkg.equals(proxyPkg)) { throw new IllegalArgumentException( "non-public interfaces from different packages"); } } } if (proxyPkg == null) { // if no non-public proxy interfaces, use com.sun.proxy package proxyPkg = ReflectUtil.PROXY_PACKAGE + "."; } /* * Choose a name for the proxy class to generate. */ long num = nextUniqueNumber.getAndIncrement(); //生成隨機(jī)代理類(lèi)的類(lèi)名, $Proxy + num String proxyName = proxyPkg + proxyClassNamePrefix + num; /* * 生成代理類(lèi)的class文件, 返回字節(jié)流 */ byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces); try { return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length); } catch (ClassFormatError e) { //結(jié)束 throw new IllegalArgumentException(e.toString()); } } }
前文提到ProxyFactoryClass#apply是真正生成代理類(lèi)的方法, 這其實(shí)是不準(zhǔn)確的。源代碼讀到這里,我們會(huì)發(fā)現(xiàn)ProxyGenerator#generateProxyClass才是真正生成代理類(lèi)的方法。根據(jù)Java class字節(jié)碼組成(可以參見(jiàn)我的另一篇文章Java字節(jié)碼學(xué)習(xí)筆記)來(lái)生成相應(yīng)的Clss文件。具體ProxyGenerator#generateProxyClass源碼如下:
private byte[] generateClassFile() { /* * Step 1: Assemble ProxyMethod objects for all methods to * generate proxy dispatching code for. */ //addProxyMethod方法,就是將方法都加入到一個(gè)列表中,并與對(duì)應(yīng)的class對(duì)應(yīng)起來(lái) //這里給Object對(duì)應(yīng)了三個(gè)方法hashCode,toString和equals addProxyMethod(hashCodeMethod, Object.class); addProxyMethod(equalsMethod, Object.class); addProxyMethod(toStringMethod, Object.class); //將接口列表中的接口與接口下的方法對(duì)應(yīng)起來(lái) for (int i = 0; i < interfaces.length; i++) { Method[] methods = interfaces[i].getMethods(); for (int j = 0; j < methods.length; j++) { addProxyMethod(methods[j], interfaces[i]); } } /* * For each set of proxy methods with the same signature, * verify that the methods' return types are compatible. */ for (List<ProxyMethod> sigmethods : proxyMethods.values()) { checkReturnTypes(sigmethods); } /* * Step 2: Assemble FieldInfo and MethodInfo structs for all of * fields and methods in the class we are generating. */ //方法中加入構(gòu)造方法,這個(gè)構(gòu)造方法只有一個(gè),就是一個(gè)帶有InvocationHandler接口的構(gòu)造方法 //這個(gè)才是真正給class文件,也就是代理類(lèi)加入方法了,不過(guò)還沒(méi)真正處理,只是先加進(jìn)來(lái)等待循環(huán),構(gòu)造方法在class文件中的名稱描述是<init> try { methods.add(generateConstructor()); for (List<ProxyMethod> sigmethods : proxyMethods.values()) { for (ProxyMethod pm : sigmethods) { //給每一個(gè)代理方法加一個(gè)Method類(lèi)型的屬性,數(shù)字10是class文件的標(biāo)識(shí)符,代表這些屬性都是private static的 fields.add(new FieldInfo(pm.methodFieldName, "Ljava/lang/reflect/Method;", ACC_PRIVATE | ACC_STATIC)); //將每一個(gè)代理方法都加到代理類(lèi)的方法中 methods.add(pm.generateMethod()); } } //加入一個(gè)靜態(tài)初始化塊,將每一個(gè)屬性都初始化,這里靜態(tài)代碼塊也叫類(lèi)構(gòu)造方法,其實(shí)就是名稱為<clinit>的方法,所以加到方法列表 methods.add(generateStaticInitializer()); } catch (IOException e) { throw new InternalError("unexpected I/O Exception"); } //方法和屬性個(gè)數(shù)都不能超過(guò)65535,包括之前的接口個(gè)數(shù)也是這樣, //這是因?yàn)樵赾lass文件中,這些個(gè)數(shù)都是用4位16進(jìn)制表示的,所以最大值是2的16次方-1 if (methods.size() > 65535) { throw new IllegalArgumentException("method limit exceeded"); } if (fields.size() > 65535) { throw new IllegalArgumentException("field limit exceeded"); } //接下來(lái)就是寫(xiě)class文件的過(guò)程, 包括魔數(shù), 類(lèi)名,常量池等一系列字節(jié)碼的組成,就不一一細(xì)說(shuō)了。需要的可以參考JVM虛擬機(jī)字節(jié)碼的相關(guān)知識(shí)。 cp.getClass(dotToSlash(className)); cp.getClass(superclassName); for (int i = 0; i < interfaces.length; i++) { cp.getClass(dotToSlash(interfaces[i].getName())); } cp.setReadOnly(); ByteArrayOutputStream bout = new ByteArrayOutputStream(); DataOutputStream dout = new DataOutputStream(bout); try { // u4 magic; dout.writeInt(0xCAFEBABE); // u2 minor_version; dout.writeShort(CLASSFILE_MINOR_VERSION); // u2 major_version; dout.writeShort(CLASSFILE_MAJOR_VERSION); cp.write(dout); // (write constant pool) // u2 access_flags; dout.writeShort(ACC_PUBLIC | ACC_FINAL | ACC_SUPER); // u2 this_class; dout.writeShort(cp.getClass(dotToSlash(className))); // u2 super_class; dout.writeShort(cp.getClass(superclassName)); // u2 interfaces_count; dout.writeShort(interfaces.length); // u2 interfaces[interfaces_count]; for (int i = 0; i < interfaces.length; i++) { dout.writeShort(cp.getClass( dotToSlash(interfaces[i].getName()))); } // u2 fields_count; dout.writeShort(fields.size()); // field_info fields[fields_count]; for (FieldInfo f : fields) { f.write(dout); } // u2 methods_count; dout.writeShort(methods.size()); // method_info methods[methods_count]; for (MethodInfo m : methods) { m.write(dout); } // u2 attributes_count; dout.writeShort(0); // (no ClassFile attributes for proxy classes) } catch (IOException e) { throw new InternalError("unexpected I/O Exception"); } return bout.toByteArray(); }
經(jīng)過(guò)層層調(diào)用, 一個(gè)代理類(lèi)終于生成了。
2、是誰(shuí)調(diào)用了Invoke?
我們模擬JDK自己生成一個(gè)代理類(lèi), 類(lèi)名為T(mén)estProxyGen:
public class TestGeneratorProxy { public static void main(String[] args) throws IOException { byte[] classFile = ProxyGenerator.generateProxyClass("TestProxyGen", Subject.class.getInterfaces()); File file = new File("/Users/yadoao/Desktop/TestProxyGen.class"); FileOutputStream fos = new FileOutputStream(file); fos.write(classFile); fos.flush(); fos.close(); } }
用JD-GUI反編譯該class文件, 結(jié)果如下:
import com.su.dynamicProxy.ISubject; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.UndeclaredThrowableException; public final class TestProxyGen extends Proxy implements ISubject { private static Method m3; private static Method m1; private static Method m0; private static Method m4; private static Method m2; public TestProxyGen(InvocationHandler paramInvocationHandler) throws { super(paramInvocationHandler); } public final void sayHello() throws { try { this.h.invoke(this, m3, null); return; } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } public final boolean equals(Object paramObject) throws { try { return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue(); } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } public final int hashCode() throws { try { return ((Integer)this.h.invoke(this, m0, null)).intValue(); } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } public final void sayGoodBye() throws { try { this.h.invoke(this, m4, null); return; } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } public final String toString() throws { try { return (String)this.h.invoke(this, m2, null); } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } static { try { m3 = Class.forName("com.su.dynamicProxy.ISubject").getMethod("sayHello", new Class[0]); m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") }); m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); m4 = Class.forName("com.su.dynamicProxy.ISubject").getMethod("sayGoodBye", new Class[0]); m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); return; } catch (NoSuchMethodException localNoSuchMethodException) { throw new NoSuchMethodError(localNoSuchMethodException.getMessage()); } catch (ClassNotFoundException localClassNotFoundException) { throw new NoClassDefFoundError(localClassNotFoundException.getMessage()); } } }
首先注意到生成代理類(lèi)的構(gòu)造函數(shù), 它傳入一個(gè)實(shí)現(xiàn)InvokeHandler接口的類(lèi)作為參數(shù), 并調(diào)用父類(lèi)Proxy的構(gòu)造器, 即將Proxy中的成員變量protected InvokeHander h進(jìn)行了初始化。
再次注意到幾個(gè)靜態(tài)的初始化塊, 這里的靜態(tài)初始化塊就是對(duì)代理的接口列表以及hashcode,toString, equals方法進(jìn)行初始化。
最后就是這幾個(gè)方法的調(diào)用過(guò)程, 全都是回調(diào)Invoke方法。
就此代理模式分析到此結(jié)束。
相關(guān)文章
Javaweb項(xiàng)目session超時(shí)解決方案
這篇文章主要介紹了Javaweb項(xiàng)目session超時(shí)解決方案,關(guān)于解決方案分類(lèi)比較明確,內(nèi)容詳細(xì),需要的朋友可以參考下。2017-09-09面試官:詳細(xì)談?wù)凧ava對(duì)象的4種引用方式
這篇文章主要給大家介紹了java面試官常會(huì)問(wèn)到的,關(guān)于Java對(duì)象的4種引用方式的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用Java具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-05-05Java實(shí)現(xiàn)文件批量重命名具體實(shí)例
這篇文章主要介紹了Java實(shí)現(xiàn)文件批量重命名具體實(shí)例,需要的朋友可以參考下2014-02-02java 實(shí)現(xiàn)websocket的兩種方式實(shí)例詳解
這篇文章主要介紹了java 實(shí)現(xiàn)websocket的兩種方式實(shí)例詳解,一種使用tomcat的websocket實(shí)現(xiàn),一種使用spring的websocket,本文通過(guò)代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2018-07-07Spring boot實(shí)現(xiàn)一個(gè)簡(jiǎn)單的ioc(2)
這篇文章主要為大家詳細(xì)介紹了Spring boot實(shí)現(xiàn)一個(gè)簡(jiǎn)單ioc的第二篇,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-04-04AJAX+JAVA用戶登陸注冊(cè)驗(yàn)證的實(shí)現(xiàn)代碼
這篇文章主要介紹了AJAX+JAVA用戶登陸注冊(cè)驗(yàn)證的實(shí)現(xiàn)代碼,通過(guò)ajax異步刷新頁(yè)面驗(yàn)證用戶輸入的賬號(hào)密碼是否在數(shù)據(jù)庫(kù)中存在。非常具有實(shí)用價(jià)值,需要的朋友可以參考下2018-06-06詳解Java中字符串緩沖區(qū)StringBuffer類(lèi)的使用
StringBuffer與String類(lèi)似,只不過(guò)StringBuffer在進(jìn)行字符串處理時(shí)不生成新的對(duì)象,下面我們就來(lái)詳解Java中字符串緩沖區(qū)StringBuffer類(lèi)的使用:2016-06-06java 域?qū)ο蠊蚕頂?shù)據(jù)的實(shí)現(xiàn)
本文主要介紹了java 域?qū)ο蠊蚕頂?shù)據(jù)的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-03-03java 實(shí)現(xiàn)單鏈表逆轉(zhuǎn)詳解及實(shí)例代碼
這篇文章主要介紹了java 實(shí)現(xiàn)單鏈表逆轉(zhuǎn)實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下2017-02-02