静态代理
简介
代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗的来讲代理模式就是我们生活 中常见的中介。比如你按照小卡片上的电话打过去寻求服务,一般不是由本人,可能是一个成年雄性接听电话,然 而真正做事情的可能是另一个小姐姐。
定义:为其他对象提供一种代理以控制对这个对象的访问。
目的:
- 通过引入代理对象的方式来间接访问目标对象,防止直接访问目标对象给系统带来的不必要复杂性;
- 通过代理对象对访问进行控制
代理模式一般含有三个角色:
抽象角色:声明真实角色和代理角色的公共接口方法。
真实角色:需要实现抽象角色接口,定义了真实角色所要实现的业务逻辑,以便供代理角色调用。也就是真正的业 务逻辑在此。
代理角色:需要实现抽象角色接口,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。将统一的流程控制都放到代理角色中处理!
实现
代理模式在生活中的例子,比较像跑腿。我本来需要去超市买菜,但是呢被窝里真舒服。于是找paotui服务,类似于代理,先到达超市,在超市购买我需要的东西,还能送货上门。
抽象角色
public interface IShop {
void buy();
}
真实角色
public class Supermarket implements IShop {
@Override
public void buy() {
System.out.println("Supermarket buy");
}
}
代理角色
public class MeituanPaotui implements IShop {
private IShop mark;
MeituanPaotui(IShop mark){
this.mark = mark;
}
@Override
public void buy() {
System.out.println("go to Supermarket");
mark.buy();
System.out.println("provide home delivery service");
}
}
客户端调用
public static void main(String []args) {
IShop supermarket = new Supermarket();
IShop meituan = new MeituanPaotui(supermarket);
meituan.buy();
}
问题
静态代理在使用时,需要定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同父类。一般来说,被代理对象和代理对象是一对一的关系,当然一个代理对象对应多个被代理对象也是可以的。
静态代理,一对一则会出现时静态代理对象量多、代码量大,从而导致代码复杂,可维护性差的问题,一对多则代理对象会出现扩展能力差的问题。
动态代理
简单使用
在运行时再创建代理类和其实例,因此显然效率更低。要完成这个场景,需要在运行期动态创建一个Class。JDK提供了 Proxy 来完成这件事情。基本使用如下:
//抽象角色
interface Api {
void test(String a);
}
//真实角色
class ApiImpl implements Api{
@Override
public void test(String a) {
System.out.println("真实实现:" + a);
}
}
ApiImpl imp = new ApiImpl();
Api api = (Api) Proxy.newProxyInstance(getClass().getClassLoader(),
new Class[]{Api.class},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Log.e(TAG,"invoke ...");
return method.invoke(imp,args);
}
});
api.test("ok");
原理分析
上面我们利用Proxy类的newProxyInstance方法创建了一个动态代理对象,查看该方法的源码,发现它只是封装了创建动态代理类的步骤(红色标准部分):
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h);
final Class<?>[] intfs = interfaces.clone();
/*
* Look up or generate the designated proxy class.
*/
Class<?> cl = getProxyClass0(loader, intfs);
/*
* Invoke its constructor with the designated invocation handler.
*/
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 (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);
}
}
在上面代码中,重要的几行如下:
Class<?> cl = getProxyClass0(loader, intfs);产生代理类。
final Constructor<?> cons = cl.getConstructor(constructorParams);获取构造器。
return cons.newInstance(new Object[]{h}); 返回对应的代理类。
我们将这个生成的临时代理类,以文件形式输出出来。输出的方法如下:
String name = Api.class.getName()+"$Proxy0";
byte[] classFile = ProxyGenerator.generateProxyClass(name, new Class[]{Api.class});
String path = "lib/" + name+".class";
try(FileOutputStream fos = new FileOutputStream(path)) {
fos.write(classFile);
fos.flush();
System.out.println("代理类class文件写入成功");
} catch (Exception e) {
System.out.println("写文件错误");
}
这里注意的是,鄙人这边是android开发,直接用studio来写上述代码,结果发现ProxyGenerator找不到,最后发现是android的sdk里面没有对应的rt.jar,需要使用java环境并且是JDK1.8才能找到ProxyGenerator,这边环境的是IntelliJ IDEA。
输出的文件如下:
public final class Api$Proxy0 extends Proxy implements Api {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
public Api$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 test(String var1) throws {
try {
super.h.invoke(this, m3, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
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("Api").getMethod("test", Class.forName("java.lang.String"));
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
如上动态代理类,执行test方法时会调用super.h.invoke(this, m3, new Object[]{var1})。也就是回调了InvocationHandler 的 public Object invoke(Object proxy, Method method, Object[] args) 的方法。
其中的m3是通过Class.forName("Api").getMethod("test", Class.forName("java.lang.String"))进行反射的Api的test方法。
剩下还有m0,m1,m2都是Object里的equals、toString、hashCode方法。
总结
静态代理,一对一则会出现时静态代理对象量多、代码量大,从而导致代码复杂,可维护性差的问题,一对多则代理对象会出现扩展能力差的问题。
动态代理,运行时生成代理类,效率会比静态代理低,但是减少了代码的复杂以及程序开发时间。