什么是代理模式?
被代理對(duì)象的動(dòng)作交由代理對(duì)象執(zhí)行。
代理模式有什么用?
代理模式的初衷是解決不直接調(diào)用目標(biāo)對(duì)象的實(shí)現(xiàn),而是把功能實(shí)現(xiàn)委托給代理對(duì)象對(duì)象的場(chǎng)景。
代理對(duì)象拿到目標(biāo)對(duì)象的實(shí)現(xiàn)后,可以對(duì)邏輯進(jìn)行增強(qiáng),比如像Aop思想,可以在連接點(diǎn)前后增加邏輯。
代理模式有靜態(tài)代理和動(dòng)態(tài)代理兩種,動(dòng)態(tài)代理較為熟知的有jdk和cgLib這兩種。
以下是模仿我們點(diǎn)外賣,外賣小哥幫我們送外賣場(chǎng)景。
1、靜態(tài)代理
定義送外賣行為
public interface Want2Do {
void getFood();
}
我要去拿外賣
public class OrderPerson implements Want2Do{
@Override
public void getFood() {
System.out.println("要去拿外賣");
}
}
外賣小哥幫我拿外賣
public class Brother implements Want2Do{
private Want2Do real;
public Brother(Want2Do real){
this.real = real;
}
@Override
public void getFood() {
real.getFood();
System.out.println("拿外賣");
}
}
2、jdk動(dòng)態(tài)代理
我們自己需要去拿外賣
public class Myself implements Want2Do {
private String name;
public Myself(String name){
this.name = name;
}
@Override
public void getFood() {
System.out.println(name + "正在拿外賣");
}
}
通過jdk動(dòng)態(tài)代理生成外賣小哥,替我們?nèi)ツ猛赓u
public class Main {
public static void main(String[] args) {
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
Object result = Proxy.newProxyInstance(Want2Do.class.getClassLoader(),
Myself.class.getInterfaces(), new MyInvoke("小王"));
Want2Do want2Do = (Want2Do) result;
want2Do.getFood();
}
}
這個(gè)外賣小哥可以幫很多人送外賣,所以你得告訴他,他幫誰拿外賣
class MyInvoke implements InvocationHandler {
String name;
public MyInvoke(String name) {
this.name = name;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("proxy : " + proxy.getClass());
System.out.println("method : " + method + " 所屬類 : " + method.getDeclaringClass());
System.out.println("args : " + args);
method.invoke(new Myself(name));
return null;
}
}
Jdk動(dòng)態(tài)代理大致流程:
1)拿到被代理類的類信息。
2)根據(jù)類信息生成新的Proxy0類信息。
3)將Proxy0類加載到j(luò)vm方法區(qū)中。
4)根據(jù)Proxy0類創(chuàng)建實(shí)例返回。
sun.misc.ProxyGenerator.saveGeneratedFiles設(shè)置為true可以保存代理對(duì)象的類信息
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package com.sun.proxy;
import designpatten.proxy.jdkProxy.Want2Do;
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 Want2Do {
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;
public $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 void getFood() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
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 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"));
m3 = Class.forName("designpatten.proxy.jdkProxy.Want2Do").getMethod("getFood");
m2 = Class.forName("java.lang.Object").getMethod("toString");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
看到這個(gè)代理對(duì)象的類信息,我們可以看到getFood調(diào)用的是外賣小哥的統(tǒng)一方法invoke方法,但是你得告訴他要干嘛,就是傳入的m2。等他拿到外賣你得告訴他送給誰method.invoke(new Myself(name));。
其實(shí)不用理解我送外賣的例子,這個(gè)$Proxy0類的邏輯很清晰,看下就知道怎么調(diào)用的了。