什么是代理模式?
为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
以上为百度百科对代理模式的定义。嗯,有点拗口,也不太好理解。那么我们换一个通俗点的例子:我们去餐厅吃饭,点了一盘大盘鸡。做这道菜的肯定是餐厅的厨师对吧,但是厨师人家做的技术活,才不想直接跟这些个食客当面聊呢,人家也是很高傲的,所以呢就由餐厅的服务员与食客做对接,由服务员将我们的需求传递给厨师,厨师做好了之后在由服务员传递到食客桌上。
这中间有3个角色:1、食客 2、服务员 3、厨师
提需求的是食客,正真做菜的是厨师,服务员只在中间起到了传递信息的角色。但请主意,如果只是这么简单那你可就太小看这个服务员了。假设你是带着你的现女友去吃饭,而服务员恰好是你的前女友,那么可能会出现点啥菜都没有的情况对不?厨师做好了菜服务员可能帮你加点葱啊啥的不?对了,这其实才是代理模式的真正精髓:预处理消息、过滤消息、添加额外的功能。
代理模式分为静态代理和动态代理,静态代理比较简单但缺乏灵活性。动态代理编写比较复杂,相对比较灵活,性能没静态代理高。
静态代理:
/**
* 接口
*/
public interface HelloInterface {
void sayHello();
}
/**
* 委托类 真正干活的角色
*/
public class Hello implements HelloInterface{
@Override
public void sayHello() {
System.out.println("Hello World!");
}
}
/**
* 代理类
*/
public class HelloProxy implements HelloInterface{
private HelloInterface helloInterface = new Hello();
@Override
public void sayHello() {
System.out.println("Hello do before");
helloInterface.sayHello();
System.out.println("Hello do after");
}
}
代码非常简单,在代理类中我们可以进行额外的一些处理。执行结果如下。
Hello do before
Hello World!
Hello do after
好了,我们看完了静态代理在看下动态代理。但在看动态代理之前我们先看以下的代码。
//测试生成.class文件
File file = new File("E:\\CalmJava\\Proxy\\src\\com\\calm\\proxy\\HelloImpl.class");
byte[] helloImpls = ProxyGenerator.generateProxyClass("HelloImpl", new Class[]{HelloInterface.class});
try {
FileOutputStream outputStream = new FileOutputStream(file);
outputStream.write(helloImpls);
outputStream.flush();
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
额,请忽略掉这个绝对路径,我也只是为了演示下对不。当执行这段代码后,我们会得到一个HelloImpl.class文件,其中的代码如下:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
import com.calm.proxy.HelloInterface;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class HelloImpl extends Proxy implements HelloInterface {
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;
public HelloImpl(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 sayHello() 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("com.calm.proxy.HelloInterface").getMethod("sayHello");
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());
}
}
}
请记住这份代码不是.java文件而是.class文件,如果你不是用编译器打开的话你看到的会是看不懂的乱码,这里能看到是因为编译器为我们进行反解释,然后请注意下这个函数ProxyGenerator.generateProxyClass。好,我们继续看动态代理。
Hello hello = new Hello();
HelloInterface helloInterface = (HelloInterface) Proxy.newProxyInstance(hello.getClass().getClassLoader(),
hello.getClass().getInterfaces(),new ProxyHandler(hello));
helloInterface.sayHello();
static class ProxyHandler implements InvocationHandler{
private final Object object;
public ProxyHandler(Object o){
this.object = o;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before invoke method");
Object invoke = method.invoke(object, args);
System.out.println("After invoke method");
return invoke;
}
}
接口还是那个接口,委托类还是那个委托类。但代理类我不在直接写死,而是通过Proxy.newProxyInstance这个来生成。执行结果如下:
Before invoke method
Hello World!
After invoke method
执行结果与静态代理基本类似,但并没完全写死我的委托类,相对更加灵活。你是否用过大名鼎鼎的Retrofit,是不是感觉它做了好多事情,但实际真正做网络请求的是它么?我只能告诉你它真正是把动态代理玩到了极致。好了,以上的代码相信对于你来说肯定是小儿科的存在了,那么今天就到这里吧。想啥呢?只了解了基本的代码编写怎么行,知其然知其所以然方为知知。我们还是可以继续往下深入看下的嘛。
Proxy.newProxyInstance调用了Proxy的类函数,我们跟进去看下。
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h);
final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
/*
* Look up or generate the designated proxy class.
*/
//关键代码在这句,获取代理的类
Class<?> cl = getProxyClass0(loader, intfs);
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}
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);
}
这段代码会从proxyClassCache判断是新创建还是从缓存获取对应的类返回回去。创建的话是从下面这个类去进行的。
private static final class ProxyClassFactory
implements BiFunction<ClassLoader, Class<?>[], Class<?>>
执行该类的apply方法。由于代码比较多就不全贴了,只贴出关键的代码。
/*
* Generate the specified proxy class.
*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
}
在apply的方法里我们会看到上面的代码。等等,ProxyGenerator.generateProxyClass这个熟悉不,还记得上面让注意的吗,是不是这个方法?对的,在这里也是通过这个方法来创建得到一个byte[],我们的.class不就是由.java编译而来的一份二进制文件,不就是一个byte[]么。其实如果你有兴趣可以自己将我们之前生成的HelloImpl.class通过类加载器加载进去,然后实例化并使用它。注意以上代码最后返回给我们的是Class<?>,是什么?这是一个已经被类加载器加载过的Class啦。我们回到newProxyInstance这个函数里可以看到如下代码:
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
return cons.newInstance(new Object[]{h});
到这里各位应该看明白了吧,得到Class然后反射创建对应的实例。到这里是不是可以收工了?to yong to simple,知道动态代理的类是怎么创建并实例化出来的,难度不看下又是怎么可以被正常调用的么?这时候我们之前的HelloImpl.class终于可以上场了,我们知道其实代理创建与我们自己通过函数创建的class是一样的,那我们调用的时候很明显也是调用了相同的代码,如下:
public final void sayHello() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
super.h,super是谁?h又是谁? 我们继承的是Proxy很明显这就是super,那h呢?
/**
* the invocation handler for this proxy instance.
* @serial
*/
protected InvocationHandler h;
好吧,我们创建动态代理的时候不是传了个InvocationHandler 的实现类么,我想不用过多解释了吧。
m3 = Class.forName("com.calm.proxy.HelloInterface").getMethod("sayHello");
m3对应调用的方法,更具体的可以去看下对应的.class文件吧。
好了,到这里我们可以真正的手工了,从写法到对应的生成原理在到调用逻辑,这样的一套是不是可以让你更加印象深刻呢?感谢陪伴。