一、代理模式與靜態(tài)代理
代理模式
給某一個(gè)對象提供一個(gè)代理母债,并由代理對象控制對原對象的引用午磁。
代理模式包含如下角色:
Subject:抽象主題角色
Proxy:代理主題角色
RealSubject:真實(shí)主題角色
靜態(tài)代理
代理類在程序運(yùn)行前就已經(jīng)存在。通常情況下毡们,靜態(tài)代理中的代理類和委托類會實(shí)現(xiàn)同一接口或是派生自相同的父類迅皇。
靜態(tài)代理例子:
public class ProxyDemo {
public static void main(String[] args) {
consumer(new TargetObject());
consumer(new SimpleProxy(new TargetObject()));
}
public static void consumer(Interface inter) {
inter.doSomething();
inter.somethingElse("Tomy");
}
interface Interface { //抽象主題角色
void doSomething();
void somethingElse(String arg);
}
static class TargetObject implements Interface { //真實(shí)主題角色
public void doSomething() {
System.out.println("target doSomething");
}
public void somethingElse(String arg) {
System.out.println("target somethingElse " + arg);
}
}
static class SimpleProxy implements Interface { //代理主題角色
private Interface target;
public SimpleProxy(Interface target) {
this.target = target;
}
public void doSomething() {
System.out.println("SimpleProxy doSomething");
target.doSomething();
}
public void somethingElse(String arg) {
System.out.println("SimpleProxy somethingElse " + arg);
target.somethingElse(arg);
}
}
}
//輸出
target doSomething
target somethingElse Tomy
SimpleProxy doSomething
target doSomething
SimpleProxy somethingElse Tomy
target somethingElse Tomy
二、動(dòng)態(tài)代理
動(dòng)態(tài)代理概念
代理類在程序運(yùn)行時(shí)創(chuàng)建的方式被成為動(dòng)態(tài)代理衙熔。也就是說登颓,代理類并不是在Java代碼中定義的,而是在運(yùn)行時(shí)根據(jù)我們在Java代碼中的"指示"動(dòng)態(tài)生成的红氯。相比于靜態(tài)代理框咙,動(dòng)態(tài)代理的優(yōu)勢在于可以很方便的對代理類的函數(shù)進(jìn)行統(tǒng)一的處理,而不用修改每個(gè)代理類的函數(shù)痢甘。
動(dòng)態(tài)代理相關(guān)類和接口
java.lang.reflect.Proxy:
Java動(dòng)態(tài)代理機(jī)制的主類喇嘱,提供了一組靜態(tài)方法來為一組接口動(dòng)態(tài)地生成代理類及其實(shí)例。
Proxy的靜態(tài)方法:
//方法1: 該方法用于獲取指定動(dòng)態(tài)代理對象所關(guān)聯(lián)的調(diào)用處理器
static InvocationHandler getInvocationHandler(Object proxy)
//方法2:該方法用于獲取關(guān)聯(lián)于指定類裝載器和一組接口的動(dòng)態(tài)代理對象
static Class getProxyClass(ClassLoader loader, Class[] interfaces)
//方法3:該方法用于判斷指定類對象是否是一個(gè)動(dòng)態(tài)代理類
static boolean isProxyClass(Class cl)
//方法4:該方法用于為指定類裝載器塞栅、一組接口及調(diào)用處理器生成動(dòng)態(tài)代理對象
static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)
java.lang.reflect.InvocationHandler:
調(diào)用處理器接口者铜,它自定義了一個(gè)invoke方法题画,用于集中處理在動(dòng)態(tài)代理對象上的方法調(diào)用钝尸,通常在該方法中實(shí)現(xiàn)對委托類的代理訪問。每次生成動(dòng)態(tài)代理對象時(shí)都需要指定一個(gè)實(shí)現(xiàn)了該接口的調(diào)用處理器對象暇务。
InvocationHandler的核心方法:
//該方法負(fù)責(zé)集中處理動(dòng)態(tài)代理類上的所有方法調(diào)用砾医。
//第一個(gè)參數(shù)是代理對象拿撩,第二個(gè)參數(shù)是被調(diào)用的方法對象,第三個(gè)方法是調(diào)用參數(shù)藻烤。
//調(diào)用處理器根據(jù)這三個(gè)參數(shù)進(jìn)行預(yù)處理或分派到委托類實(shí)例上反射執(zhí)行绷雏。
Object invoke(Object proxy, Method method, Object[] args)
java.lang.ClassLoader:
類裝載器,負(fù)責(zé)將類的字節(jié)碼裝載到Java虛擬機(jī)(JVM)中并為其定義類對象怖亭,然后該類才能被使用涎显。Proxy靜態(tài)方法生成代理類同樣需要通過類裝載器來進(jìn)行裝載才能使用,它與普通類的唯一區(qū)別就是其字節(jié)碼是由JVM在運(yùn)行時(shí)動(dòng)態(tài)生成的而非預(yù)存在于任何一個(gè).class文件中兴猩。每次生成動(dòng)態(tài)代理對象時(shí)都需要指定一個(gè)類裝載器對象期吓。
動(dòng)態(tài)代理實(shí)現(xiàn)方法
方法一:
- 步驟1:通過實(shí)現(xiàn)InvocationHandler接口創(chuàng)建自己的調(diào)用處理器;
- 步驟2:通過為Proxy類指定ClassLoader對象和一組interface來創(chuàng)建動(dòng)態(tài)代理類;
- 步驟3:通過反射機(jī)制獲得動(dòng)態(tài)代理類的構(gòu)造函數(shù)讨勤,其唯一參數(shù)類型是調(diào)用處理器接口類型箭跳;
- 步驟4:通過構(gòu)造函數(shù)創(chuàng)建動(dòng)態(tài)代理對象,構(gòu)造時(shí)調(diào)用處理器對象作為參數(shù)被傳入潭千。
//InvocationHandlerImpl實(shí)現(xiàn)了InvocationHandler接口谱姓,并能實(shí)現(xiàn)方法調(diào)用從代理類到委托類的分派轉(zhuǎn)發(fā)
//其內(nèi)部通常包含指向委托類實(shí)例的引用,用于真正執(zhí)行分派轉(zhuǎn)發(fā)過來的方法調(diào)用
InvocationHandler handler = new InvocationHandlerImpl(...);
//通過Proxy為包括Interface接口在內(nèi)的一組接口動(dòng)態(tài)創(chuàng)建代理類的類對象
Class clazz = Proxy.getProxyClass(classLoader, new Class[] { Interface.class, ... });
//通過反射從生成的類對象獲得構(gòu)造函數(shù)對象
Constructor constructor = clazz.getConstructor(new Class[] { InvocationHandler.class });
//通過構(gòu)造函數(shù)對象創(chuàng)建動(dòng)態(tài)代理對象
Interface Proxy = (Interface)constructor.newInstance(new Object[] { handler });
方法二:
- 還有更簡便的動(dòng)態(tài)代理實(shí)現(xiàn)方法刨晴,Proxy的靜態(tài)方法newProxyInstance已經(jīng)為我們封裝了步驟2到步驟4的過程屉来。
//InvocationHandlerImpl實(shí)現(xiàn)了InvocationHandler接口,并能實(shí)現(xiàn)方法調(diào)用從代理類到委托類的分派轉(zhuǎn)發(fā)
InvocationHandler handler = new InvocationHandlerImpl(...);
//通過Proxy直接創(chuàng)建動(dòng)態(tài)代理對象
Interface proxy = (Interface)Proxy.newProxyInstance(classLoader, new Class[] { Interface.class }, handler );
動(dòng)態(tài)代理例子:
//測試代碼
public class DynamicProxy {
public static void main(String[] args) {
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true"); //保存生成的動(dòng)態(tài)代理.class文件
TargetObject target = new TargetObject();
consumer(target);
//Insert a proxy and call again:
Interface proxy = (Interface)Proxy.newProxyInstance(
Interface.class.getClassLoader(),
new Class[]{ Interface.class },
new DynamicProxyHandler(target));
consumer(proxy);
}
public static void consumer(Interface iface) {
iface.doSomething();
iface.somethingElse("Tomy");
}
interface Interface { //抽象主題角色
void doSomething();
void somethingElse(String arg);
}
static class TargetObject implements Interface { //真實(shí)主題角色
public void doSomething() {
System.out.println("target doSomething");
}
public void somethingElse(String arg) {
System.out.println("target somethingElse " + arg);
}
}
static class DynamicProxyHandler implements InvocationHandler { //動(dòng)態(tài)代理處理器
private Object target;
public DynamicProxyHandler(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("InvocationHandler: " + proxy.getClass() +
", method: " + method + ", args: " + args);
if(args != null) {
for(Object arg : args) {
System.out.println("InvocationHandler args: " + arg);
}
}
return method.invoke(target, args);
}
}
}
//輸出log
target doSomething
target somethingElse Tomy
InvocationHandler: class com.tomorrow.test.$Proxy0, method: public abstract void com.tomorrow.test.DynamicProxy$Interface.doSomething(), args: null
target doSomething
InvocationHandler: class com.tomorrow.test.$Proxy0, method: public abstract void com.tomorrow.test.DynamicProxy$Interface.somethingElse(java.lang.String), args: [Ljava.lang.Object;@16d3586
InvocationHandler args: Tomy
target somethingElse Tomy
生成的動(dòng)態(tài)代理類:
package com.tomorrow.test;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
final class $Proxy0 extends Proxy
implements DynamicProxy.Interface
{
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m4;
private static Method m0;
public $Proxy0(InvocationHandler paramInvocationHandler)
throws
{
super(paramInvocationHandler);
}
public final boolean equals(Object paramObject)
throws
{
try
{
return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final void doSomething()
throws
{
try
{
this.h.invoke(this, m3, null);
return;
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final String toString()
throws
{
try
{
return (String)this.h.invoke(this, m2, null);
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final void somethingElse(String paramString)
throws
{
try
{
this.h.invoke(this, m4, new Object[] { paramString });
return;
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final int hashCode()
throws
{
try
{
return ((Integer)this.h.invoke(this, m0, null)).intValue();
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
static
{
try
{
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
m3 = Class.forName("com.tomorrow.test.DynamicProxy$Interface").getMethod("doSomething", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m4 = Class.forName("com.tomorrow.test.DynamicProxy$Interface").getMethod("somethingElse", new Class[] { Class.forName("java.lang.String") });
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
return;
}
catch (NoSuchMethodException localNoSuchMethodException)
{
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
}
catch (ClassNotFoundException localClassNotFoundException)
{
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
}
}
動(dòng)態(tài)代理類的繼承關(guān)系
動(dòng)態(tài)代理對象的特點(diǎn)
- 每個(gè)動(dòng)態(tài)代理對象都會關(guān)聯(lián)一個(gè)調(diào)用處理器對象狈癞,可以通過Proxy提供的靜態(tài)方法getInvocationHandler獲得動(dòng)態(tài)代理對象關(guān)聯(lián)的調(diào)用處理器對象茄靠。
- 在動(dòng)態(tài)代理對象上調(diào)用其代理的接口中所聲明的方法時(shí),這些方法最終都會執(zhí)行調(diào)用處理器的invoke方法蝶桶。
- 動(dòng)態(tài)代理類的根類java.lang.Object中有三個(gè)方法同樣會被分派到調(diào)用處理器的invoke方法中執(zhí)行慨绳,它們是hashCode、equals和toString真竖。
可能的原因有:
(1)因?yàn)檫@些方法為public且非final類型脐雪,能夠被代理類覆寫;
(2)因?yàn)檫@些方法往往呈現(xiàn)出一個(gè)類的某種特征屬性恢共,具有一定的區(qū)分度喂江,所以為了保證代理類與委托類對外的一致性,這三個(gè)方法也應(yīng)該被分派到委托類執(zhí)行旁振。
動(dòng)態(tài)代理一組接口
當(dāng)動(dòng)態(tài)代理的一組接口有重復(fù)聲明的方法且該方法被調(diào)用時(shí)获询,動(dòng)態(tài)代理類總是從排在最前面的接口中獲取方法對象并分派給調(diào)用處理器,而無論動(dòng)態(tài)代理對象是否正在以該接口(或繼承于該接口的某個(gè)子接口)的形式被外部引用拐袜,因?yàn)樵趧?dòng)態(tài)代理類內(nèi)部無法區(qū)分其當(dāng)前的被引用類型吉嚣。
例子:
//測試代碼
public class DynamicProxy {
public static void main(String[] args) {
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true"); //保存生成的動(dòng)態(tài)代理.class文件
try {
TargetObject object = new TargetObject();
InvocationHandler handler = new DynamicProxyHandler(object);
Class<?> clazz = Proxy.getProxyClass(
TargetObject.class.getClassLoader(),
new Class[] {InterfaceB.class, InterfaceA.class}); //注意:這里先寫接口B,然后寫接口A
Constructor<?> constructor = clazz.getConstructor(new Class[] {InvocationHandler.class});
InterfaceA proxy_a = (InterfaceA) constructor.newInstance(new Object[] {handler});
InterfaceB proxy_b = (InterfaceB) constructor.newInstance(new Object[] {handler});
proxy_a.doSomething();
proxy_a.doA();
proxy_b.doSomething();
proxy_b.doB();
} catch(Exception e) {
e.printStackTrace();
}
}
public interface InterfaceA {
void doSomething();
void doA();
}
public interface InterfaceB {
void doSomething();
void doB();
}
static class TargetObject implements InterfaceA, InterfaceB {
public void doSomething() {
System.out.println("target doSomething");
}
public void doA() {
System.out.println("target doA");
}
public void doB() {
System.out.println("target doB");
}
}
static class DynamicProxyHandler implements InvocationHandler { //動(dòng)態(tài)代理處理器
private Object target;
public DynamicProxyHandler(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("InvocationHandler: " + proxy.getClass() +
", method: " + method + ", args: " + args);
if(args != null) {
for(Object arg : args) {
System.out.println("InvocationHandler args: " + arg);
}
}
return method.invoke(target, args);
}
}
}
//輸出log
InvocationHandler: class com.sun.proxy.$Proxy0, method: public abstract void com.tomorrow.test.DynamicProxy$InterfaceB.doSomething(), args: null
==> 雖然動(dòng)態(tài)代理對象proxy_a正以接口InterfaceA的形式被外部引用蹬铺,但獲取的方法對象是在getProxyClass方法排前面的InterfaceB中的尝哆。
target doSomething
InvocationHandler: class com.sun.proxy.$Proxy0, method: public abstract void com.tomorrow.test.DynamicProxy$InterfaceA.doA(), args: null
target doA
InvocationHandler: class com.sun.proxy.$Proxy0, method: public abstract void com.tomorrow.test.DynamicProxy$InterfaceB.doSomething(), args: null
target doSomething
InvocationHandler: class com.sun.proxy.$Proxy0, method: public abstract void com.tomorrow.test.DynamicProxy$InterfaceB.doB(), args: null
target doB
生成的動(dòng)態(tài)代理類:
package com.sun.proxy;
import com.tomorrow.test.DynamicProxy.InterfaceA;
import com.tomorrow.test.DynamicProxy.InterfaceB;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class $Proxy0 extends Proxy
implements DynamicProxy.InterfaceB, DynamicProxy.InterfaceA
{
private static Method m1;
private static Method m3;
private static Method m5;
private static Method m2;
private static Method m4;
private static Method m0;
public $Proxy0(InvocationHandler paramInvocationHandler)
throws
{
super(paramInvocationHandler);
}
public final boolean equals(Object paramObject)
throws
{
try
{
return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final void doSomething()
throws
{
try
{
this.h.invoke(this, m3, null);
return;
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final void doA()
throws
{
try
{
this.h.invoke(this, m5, null);
return;
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final String toString()
throws
{
try
{
return (String)this.h.invoke(this, m2, null);
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final void doB()
throws
{
try
{
this.h.invoke(this, m4, null);
return;
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final int hashCode()
throws
{
try
{
return ((Integer)this.h.invoke(this, m0, null)).intValue();
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
static
{
try
{
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
m3 = Class.forName("com.tomorrow.test.DynamicProxy$InterfaceB").getMethod("doSomething", new Class[0]); //注意:獲取的是排在前面的InterfaceB的doSomething方法
m5 = Class.forName("com.tomorrow.test.DynamicProxy$InterfaceA").getMethod("doA", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m4 = Class.forName("com.tomorrow.test.DynamicProxy$InterfaceB").getMethod("doB", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
return;
}
catch (NoSuchMethodException localNoSuchMethodException)
{
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
}
catch (ClassNotFoundException localClassNotFoundException)
{
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
}
}
被動(dòng)態(tài)代理的一組接口的特點(diǎn)
- 要注意不能有重復(fù)的接口,以避免動(dòng)態(tài)代理類代碼生成時(shí)出現(xiàn)編譯錯(cuò)誤甜攀。
- 這些接口對于類裝載器必須可見秋泄,否則類裝載器無法鏈接它們,將會導(dǎo)致動(dòng)態(tài)代理類定義失敗规阀。
- 需要被代理的所有非public的接口必須在同一個(gè)包中恒序,否則動(dòng)態(tài)代理類代碼生成也會失敗。
- 接口的數(shù)目不能超過65535谁撼,這是JVM設(shè)定的限制歧胁。