一制市、代理模式
代理模式會給某個對象提供一個代理對象些膨,并由代理對象控制原對象的引用湖笨。通俗的來講代理模式就是我們生活中常見的中介黄刚。代理模式一般會有三個角色:
抽象接口:代理角色和真實(shí)角色對外提供的公共方法黔漂,一般為一個接口诫尽。
真實(shí)角色:需要實(shí)現(xiàn)抽象接口,定義了真實(shí)角色所要實(shí)現(xiàn)的業(yè)務(wù)邏輯炬守,以便供代理角色調(diào)用牧嫉。真正的業(yè)務(wù)邏輯在此。
代理角色:需要實(shí)現(xiàn)抽象接口,是真實(shí)角色的代理酣藻,通過真實(shí)角色的業(yè)務(wù)邏輯方法來實(shí)現(xiàn)抽象方法曹洽,并可以附加自己的操作。將統(tǒng)一的流程控制都放到代理角色中處理辽剧。
一個簡單的例子就是送淆,業(yè)務(wù)中需要統(tǒng)計某些方法的執(zhí)行時間。由于這是和業(yè)務(wù)邏輯不相干的功能怕轿,我們不想把它放在真實(shí)角色中偷崩,而是通過代理角色在調(diào)用執(zhí)行真實(shí)角色方法的前后插入代碼來統(tǒng)計執(zhí)行時間。那么就可以把被統(tǒng)計的方法抽取到接口中:
public interface IFunction {
void action();
}
public class RealSubject implements ITime {
@Override
public void action() {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("RealSubject action() execute!");
}
}
public class ProxySubject implements IFunction {
private IFunction iFunction;
public ProxySubject(IFunction iTime) {
this.iFunction = iTime;
}
@Override
public void action() {
// 計算時間的操作是對真實(shí)對象方法的增強(qiáng)
long startTime = System.currentTimeMillis();
// 實(shí)際執(zhí)行還是執(zhí)行真實(shí)對象的方法
iFunction.action();
long endTime = System.currentTimeMillis();
System.out.println("Run time:" + (endTime - startTime));
}
}
public class Test {
public static void main(String[] args) {
RealSubject realSubject = new RealSubject();
ProxySubject proxySubject = new ProxySubject(realSubject);
proxySubject.action();
}
}
輸出結(jié)果為:
RealSubject action() execute!
Run time:500
可以看到代理起到隔離調(diào)用者與真實(shí)對象的作用撞羽,不會使二者產(chǎn)生直接聯(lián)系阐斜。
上邊這種實(shí)現(xiàn)模式是靜態(tài)代理,靜態(tài)代理類需要持有真實(shí)對象的引用诀紊,一個代理可以代理一個或多個被代理對象谒出。
二、靜態(tài)代理的弊端
一對一的問題是時靜態(tài)代理對象量多渡紫、代碼量大到推,從而導(dǎo)致代碼復(fù)雜,可維護(hù)性差惕澎;而一對多代理對象會出現(xiàn)擴(kuò)展能力差的問題(在一對多時會出現(xiàn)100個真實(shí)對象共用一個代理莉测,需要1個代理和100個真實(shí)對象同時產(chǎn)生關(guān)系,從而因?yàn)殛P(guān)系復(fù)雜而很難擴(kuò)展)唧喉。
比如消費(fèi)者 Consumer 想要買房捣卤,那么他就要去找房產(chǎn)中介 HouseAgent,二者可以抽象出 IHouse 接口:
public interface IHouse {
void buyHouse();
}
public class Consumer implements IHouse {
@Override
public void buyHouse() {
System.out.println("Consumer buyHouse()");
}
}
public class HouseAgent implements IHouse {
private IHouse iHouse;
public HouseAgent(IHouse iHouse) {
this.iHouse = iHouse;
}
@Override
public void buyHouse() {
before();
iHouse.buyHouse();
after();
}
private void before() {
System.out.println("Before buying house");
}
private void after() {
System.out.println("After buying house");
}
}
public class Test {
public static void main(String[] args) {
HouseAgent houseAgent = new HouseAgent(new Consumer());
houseAgent.buyHouse();
}
}
接下來 Consumer 又想買車八孝,那么他就又要去找一個買車的代理 CarAgent(根據(jù)軟件設(shè)計的單一職責(zé)原則和開閉原則董朝,不應(yīng)該在 IHouse 接口中增加買車的方法 buyCar(),而是新增 ICar 接口)干跛,這樣的話就新增如下代碼:
public interface ICar {
void buyCar();
}
public class CarAgent implements ICar {
private ICar iCar;
public CarAgent(ICar iCar) {
this.iCar = iCar;
}
@Override
public void buyCar() {
before();
iCar.buyCar();
after();
}
private void before() {
System.out.println("Before buying car");
}
private void after() {
System.out.println("After buying car");
}
}
public class Consumer implements IHouse, ICar {
@Override
public void buyHouse() {
System.out.println("Consumer buyHouse()");
}
@Override
public void buyCar() {
System.out.println("Consumer buyCar()");
}
}
public class Test {
public static void main(String[] args) {
Consumer consumer = new Consumer();
HouseAgent houseAgent = new HouseAgent(consumer);
houseAgent.buyHouse();
CarAgent carAgent = new CarAgent(consumer);
carAgent.buyCar();
}
}
在一個代理只維護(hù)一個接口的情況下子姜,每新增一個接口就要增加一個代理類,接口數(shù)量增多時也會造成代理類數(shù)量增多楼入。
三哥捕、JDK 動態(tài)代理
JDK 動態(tài)代理可以有效的彌補(bǔ)靜態(tài)代理的不足,可以通過一個代理類實(shí)現(xiàn)全部的代理功能嘉熊,但是 JDK 動態(tài)代理只能代理接口遥赚,不能代理一個類。
動態(tài)代理使用方式為:
public class Test {
public static void main(String[] args) {
final Consumer consumer = new Consumer();
// 生成動態(tài)代理對象
Object object = Proxy.newProxyInstance(Test.class.getClassLoader(), new Class[]{IHouse.class, ICar.class},
new InvocationHandler() {
@Override
public Object invoke(Object object, Method method, Object[] args) throws Throwable {
return method.invoke(consumer, args);
}
});
// 代理對象轉(zhuǎn)換成真實(shí)對象實(shí)例并執(zhí)行業(yè)務(wù)方法
IHouse iHouse = (IHouse) object;
iHouse.buyHouse();
}
}
使用 Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler invocationHandler) 可以創(chuàng)建一個動態(tài)代理對象阐肤,需要傳入三個參數(shù):
- loader:加載生成的代理類到 JVM 中的 ClassLoader凫佛。
- interfaces:動態(tài)代理類要代理(實(shí)現(xiàn))的接口讲坎。
- invocationHandler:一個接口對象,動態(tài)代理對象在執(zhí)行方法時愧薛,會回調(diào)到接口中唯一的方法 invoke()晨炕。
invoke(Object object, Method method, Object[] args) 有三個參數(shù),它們的含義分別是:
- object:動態(tài)代理對象厚满,其實(shí)就是通過 Proxy.newProxyInstance() 生成的那個代理對象府瞄。
- method:動態(tài)代理調(diào)用的方法對象碧磅。
- args:method 方法的參數(shù)碘箍。
使用動態(tài)代理時注意不要犯這種錯誤:
public static void main(String[] args) {
Object object = Proxy.newProxyInstance(Test.class.getClassLoader(), new Class[]{IHouse.class, ICar.class},
new InvocationHandler() {
@Override
public Object invoke(Object object, Method method, Object[] args) throws Throwable {
System.out.println(object);
return null;
}
});
IHouse iHouse = (IHouse) object;
iHouse.buyHouse();
}
這樣會造成堆溢出。原因是動態(tài)代理對象執(zhí)行了方法之后回調(diào) invoke()鲸郊,在 invoke() 中輸出時相當(dāng)于調(diào)用了 object.toString() 方法丰榴,因?yàn)橹灰{(diào)用動態(tài)代理的方法就會先回調(diào)到 invoke(),這樣就形成了死循環(huán)似的調(diào)用秆撮,造成堆溢出四濒。
再來看下 Proxy 的源碼:
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h);
final Class<?>[] intfs = interfaces.clone();
Class<?> cl = getProxyClass0(loader, intfs);
try {
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
cons.setAccessible(true);
}
return cons.newInstance(new Object[]{h});
} // catch ....
}
private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
// If the proxy class defined by the given loader implementing
// the given interfaces exists, this will simply return the cached copy;
// otherwise, it will create the proxy class via the ProxyClassFactory
return proxyClassCache.get(loader, interfaces);
}
先克隆一份 interfaces,如果 <loader,interfaces> 所對應(yīng)的 Class 對象 已經(jīng)被緩存职辨,那么就拿緩存數(shù)據(jù)的拷貝盗蟆,否則通過 ProxyClassFactory 創(chuàng)建出一個 Class。拿到 Class 對象后舒裤,通過構(gòu)造方法和傳進(jìn)來的 InvocationHandler 創(chuàng)建出動態(tài)代理對象喳资。
那么動態(tài)代理的 Class 是如何生成的呢?它與一般的 Class 生成方式不同:
- 一般的 Class 都是由 Java 源文件經(jīng)過編譯先在硬盤上生成字節(jié)碼 .class 文件腾供,再通過 ClassLoader 進(jìn)行類加載仆邓,加載進(jìn)內(nèi)存稱為 Class 對象。
- 動態(tài)代理的 Class 不會在硬盤上生成伴鳖,而是直接在內(nèi)存中產(chǎn)生节值。
生成動態(tài)代理 Class 類及其對象的正是前面提到過的,Proxy 的靜態(tài)內(nèi)部類 ProxyClassFactory榜聂,其代碼如下:
/**
* A factory function that generates, defines and returns the proxy class given
* the ClassLoader and array of interfaces.
*/
private static final class ProxyClassFactory
implements BiFunction<ClassLoader, Class<?>[], Class<?>> {
// prefix for all proxy class names
private static final String proxyClassNamePrefix = "$Proxy";
// next number to use for generation of unique proxy class names
private static final AtomicLong nextUniqueNumber = new AtomicLong();
@Override
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 {
interfaceClass = Class.forName(intf.getName(), false, loader);
} catch (ClassNotFoundException e) {
}
if (interfaceClass != intf) {
throw new IllegalArgumentException(
intf + " is not visible from class loader");
}
/*
* Verify that the Class object actually represents an
* interface.
*/
if (!interfaceClass.isInterface()) {
throw new IllegalArgumentException(
interfaceClass.getName() + " is not an interface");
}
/*
* Verify that this interface is not a duplicate.
*/
if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
throw new IllegalArgumentException(
"repeated interface: " + interfaceClass.getName());
}
}
String proxyPkg = null; // package to define proxy class in
int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
/*
* 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.
*/
for (Class<?> intf : interfaces) {
int flags = intf.getModifiers();
if (!Modifier.isPublic(flags)) {
accessFlags = Modifier.FINAL;
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 the default package.
proxyPkg = "";
}
{
// Android-changed: Generate the proxy directly instead of calling
// through to ProxyGenerator.
List<Method> methods = getMethods(interfaces);
Collections.sort(methods, ORDER_BY_SIGNATURE_AND_SUBTYPE);
validateReturnTypes(methods);
List<Class<?>[]> exceptions = deduplicateAndGetExceptions(methods);
Method[] methodsArray = methods.toArray(new Method[methods.size()]);
Class<?>[][] exceptionsArray = exceptions.toArray(new Class<?>[exceptions.size()][]);
/*
* Choose a name for the proxy class to generate.
*/
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num;
return generateProxy(proxyName, interfaces, loader, methodsArray,
exceptionsArray);
}
}
}
ProxyClassFactory 實(shí)現(xiàn)了函數(shù)式接口 BiFunction<T, U, R>搞疗,其中泛型 T,U 是其接口方法 apply(T,U) 的參數(shù)類型须肆,R 是 apply(T,U) 的返回值類型匿乃。在這里其實(shí)就是傳入 ClassLoader 和接口的 Class 數(shù)組,得到動態(tài)代理的 Class 對象休吠。
ProxyClassFactory 類中定義的兩個常量 proxyClassNamePrefix 和 nextUniqueNumber 是用來定義生成的動態(tài)代理類文件的名稱的扳埂,從倒數(shù)第二行代碼來看,命名規(guī)則為:代理所在的包名 + proxyClassNamePrefix + nextUniqueNumber瘤礁,即形如 com.frank.agent$0阳懂。
apply() 方法內(nèi),先對我們傳入的代理接口進(jìn)行檢查。先通過接口名和 loader 反射拿到 interfaceClass 對象岩调,并檢查這個 interfaceClass 對象是否和提供名字的接口 intf 相同巷燥;再看 interfaceClass 是否是一個接口;最后檢查 interfaceClass 是否已經(jīng)被添加到 interfaceSet 中了(interfaceSet 是一個 IdentityHashMap号枕,即不以 equals() 而是以 == 的結(jié)果作為比較標(biāo)準(zhǔn))缰揪。
接口篩選完畢后,開始構(gòu)造要生成的動態(tài)代理類的包名葱淳,這個包名由 interfaces 中接口的訪問控制符決定:
- 如果所有接口都由 public 修飾钝腺,那么就使用默認(rèn)的包名"";
- 如果存在非 public 屬性的接口赞厕,那么所有非 public 的接口包名必須相同艳狐,動態(tài)代理類也就使用這個包名。
最后就是生成代理類了皿桑。API 29 不再使用 ProxyGenerator 而是直接生成毫目。先獲取到所有接口中的所有方法,然后再收集所有方法聲明的異常诲侮,把它們轉(zhuǎn)換成數(shù)組傳遞給最終生成動態(tài)代理 Class 對象的 generateProxy()镀虐。
我們?nèi)匀豢梢允褂?ProxyGenerator 生成動態(tài)代理的 .class 文件:
private static void proxy() {
String name = IHouse.class.getName() + "$Proxy0";
byte[] bytes = ProxyGenerator.generateProxyClass(name, new Class[]{IHouse.class});
File file = new File(name+".class");
try {
if (!file.exists() && file.createNewFile()) {
FileOutputStream fos = new FileOutputStream(file);
fos.write(bytes);
fos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
這樣就會生成一個名字為 com.frank.agent2.IHouse$Proxy0.class 的 .class 文件:
public final class IHouse$Proxy0 extends Proxy implements IHouse {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
public IHouse$Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void buyHouse() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("com.frank.agent2.IHouse").getMethod("buyHouse");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
動態(tài)代理類 IHouse$Proxy0 繼承 Proxy 實(shí)現(xiàn)了 IHouse 接口,有4個 Method 類型成員沟绪,從 m0~m3 依次對應(yīng) hashCode()刮便、equals()、toString() 和 IHouse 接口中的 buyHouse()近零。
當(dāng)動態(tài)代理對象調(diào)用 buyHouse() 時诺核,會執(zhí)行到 super.h.invoke(),就是調(diào)用創(chuàng)建動態(tài)代理對象時創(chuàng)建的那個 InvocationHandler 的 invoke()久信。invoke() 的參數(shù)依次傳遞的是動態(tài)代理對象窖杀、動態(tài)代理對象調(diào)用方法的 Method 對象以及調(diào)用方法的參數(shù)(方法沒參數(shù)就傳 null)。這與前邊介紹的 invoke() 方法參數(shù)含義是一致的裙士。