什么是代理模式?
被代理对象的动作交由代理对象执行。
代理模式有什么用?
代理模式的初衷是解决不直接调用目标对象的实现,而是把功能实现委托给代理对象对象的场景。
代理对象拿到目标对象的实现后,可以对逻辑进行增强,比如像Aop思想,可以在连接点前后增加逻辑。
代理模式有静态代理和动态代理两种,动态代理较为熟知的有jdk和cgLib这两种。
以下是模仿我们点外卖,外卖小哥帮我们送外卖场景。
1、静态代理
定义送外卖行为
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动态代理
我们自己需要去拿外卖
public class Myself implements Want2Do {
private String name;
public Myself(String name){
this.name = name;
}
@Override
public void getFood() {
System.out.println(name + "正在拿外卖");
}
}
通过jdk动态代理生成外卖小哥,替我们去拿外卖
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();
}
}
这个外卖小哥可以帮很多人送外卖,所以你得告诉他,他帮谁拿外卖
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动态代理大致流程:
1)拿到被代理类的类信息。
2)根据类信息生成新的Proxy0类信息。
3)将Proxy0类加载到jvm方法区中。
4)根据Proxy0类创建实例返回。
sun.misc.ProxyGenerator.saveGeneratedFiles设置为true可以保存代理对象的类信息
//
// 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());
}
}
}
看到这个代理对象的类信息,我们可以看到getFood调用的是外卖小哥的统一方法invoke方法,但是你得告诉他要干嘛,就是传入的m2。等他拿到外卖你得告诉他送给谁method.invoke(new Myself(name));。
其实不用理解我送外卖的例子,这个$Proxy0类的逻辑很清晰,看下就知道怎么调用的了。