在 Java 中代理模式一般分为两种,静态代理和动态代理.
代理模式指给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用.
代理模式一般会有三个角色:
抽象角色:指代理角色和 真实角色对外提供的公共方法,一般为一个接口
真实角色:需要实现抽象角色接口,定义了真实角色所要实现的 业务逻辑,以便代理角色调用.也就是真正的业务逻辑.
代理角色:需要实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作.将统一的流程都控制到代理角色中处理.
而访问者不再访问真实角色,而是去访问代理角色。
静态代理
静态代理在使用时,需要定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同父类。一般来说,被代理对象和代理对象是一对一的关系,当然一个代理对象对应多个被代理对象也是可以的。
静态代理,一对一则会出现时静态代理对象量多、代码量大,从而导致代码复杂,可维护性差的问题,一对多则代理对象会出现扩展能力差的问题。
动态代理
指在使用时再创建代理类的实例
优点
只需要一个动态代理类,就可以解决创建多个静态代理类的问题,避免重复多余的代码
缺点
相比于静态代理,效率低.动态代理需要先通过 Java 反射机制,创建 Proxy 类来间接调用目标方法.
应用场景局限, 因为 Java 的单继承特征(每个代理类都继承了 Proxy 类),即只能针对接口创建代理类,不能针对类创建代理类.
在动态机制中,有两个重要的类和接口,一个是 InvocationHandler 接口,一个 Proxy 类,这两个是实现动态代理必须用到的.
InvocationHandler接口是给动态代理类实现的,负责处理被代理对象的操作的,而Proxy是用来创建动态代理类实例对象的,因为只有得到了这个对象我们才能调用那些需要代理的方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| public class DynamicProxy implements InvocationHandler {
//持有的真实对象 private Object factory;
public Object getFactory() { return factory; }
public void setFactory(Object factory) { this.factory = factory; }
//获取到代理类实例 public Object getProxyInstance() { return Proxy.newProxyInstance(factory.getClass().getClassLoader() , factory.getClass().getInterfaces(), this); }
//当方法被调用时,会调用 InvocationHandler 的 invoke 方法 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { doSthAfter(); Object result = method.invoke(factory, args); doSthBefore(); return result; }
private void doSthAfter() { System.out.println("调用之前进行操作"); }
private void doSthBefore() { System.out.println("调用之后进行操作"); } }
|
抽象代理接口
1 2 3
| public class AbstractPart{ void methodA() }
|
真正实现业务逻辑的类
1 2 3 4 5 6
| public class RealPart implements AbstractPart { @Override public void methodA() { System.out.println("真实实现!"); } }
|
使用动态代理
1 2 3 4
| DynamicProxy dynamicProxy = new DynamicProxy(); dynamicProxy.setFactory(new RealPart()); AbstractPart proxyInstance = (AbstractPart) dynamicProxy.getProxyInstance(); proxyInstance.methodA();
|
动态代理实现原理
当我们创建一个动态代理时,最重要的一行代码是:
1 2
| Proxy.newProxyInstance(factory.getClass().getClassLoader() , factory.getClass().getInterfaces(), this);
|
在newProxyInstance方法中,会先获取到动态创建的代理对象,然后获取构造器,创建实例对象.
1 2 3
| Class<?> cl = getProxyClass0(loader, intfs); final Constructor<?> cons = cl.getConstructor(constructorParams); return cons.newInstance(new Object[]{h});
|
在这个方法内部使用了proxyClassCache.get
方法来获取代理类,通过这个方法我们可以知道 JDK 内部使用了某种缓存机制缓存了我们的代理类 class 对象,get 方法接受的参数是被代理类的类加载器和类实现的接口.
在 JDK 内部,会生成一个派生自 Proxy 的类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
| public final class $Proxy0 extends Proxy implements ICook { private static Method m1; private static Method m2; private static Method m3; 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})).booleanValue(); } 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 methodA() 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)).intValue(); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } }
static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")}); m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); m3 = Class.forName("com.caimuhao.examples.javaadvance.proxy.dynamic").getMethod("methodA", new Class[0]); m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } }
|
在这个类中,有我们代理对象的所有方法,当我们调用创建动态代理返回的对象的方法时,其实调用的是 h.invoke
方法,这个 h 对象就是我们在调用 Proxy.newProxyInstance
实例时,传入的 this 参数,也就是动态代理 中的 invoke
方法.这样我们就可以代理到其它类,在其它类方法执行前后加入一些我们自己的业务逻辑.