此篇文章说是通过代理模式来实现简单的AOP其实只是顺带的,主要目的还是讲一下代理模式,在Android中使用的代理模式主要分为静态代理和动态代理,静态代理编译期间就已确认代理类,而动态代理在运行期间才能确认代理类。
1、代理模式简介
- 定义:给一个对象提供一个代理对象,通过代理对象来控制目标对象的访问,代理对象就像是中介,起到客户到目标的沟通,即客户不直接操控目标对象,而是通过中介(代理对象)间接地操控目标对象。
通过类图可以发现,代理模式的代理对象Proxy
和目标对象Subject
实现同一个接口,客户调用的是Proxy
对象,Proxy
可以控制Subject
的访问,真正的功能实现是在Subject
完成的。
代理模式在Java中的实现分为两种,一种是静态代理,一种是动态代理。
- 静态代理其实就是以上UML类图的标准实现,其在编译期间就会确定真正的代理类,即.class文件编译完成后就确定下来了。
- 动态代理是标准静态代理的一种扩展和变形,最主要一点是,动态代理在编译完成后是没有一个代理类的具体类的,但这不代表它没有代理类,其代理类会在运行期间自动生成,并通过反射的方式进行方法调用。
2、静态代理
静态代理可以说是很简单了,拿点江湖事来举例,一个武林盟主想去揍一个人,他一般不会自己去,他可以找武当派的人,或者少林寺的人,最后打人的是各个门派的弟子,武林盟主其实除了下命令之后啥都没干,干活的都是小弟。
/**
* Author:xishuang
* Date:2018.03.07
* Des:少林绝学《易筋经》
*/
public interface IShaolin {
void playYiGinChing();
}
/**
* Author:xishuang
* Date:2018.03.07
* Des:武当派绝学太极拳
*/
public interface IWuDang {
void playTaijiquan();
}
public class ShaoLinMan implements IShaolin {
@Override
public void playYiGinChing() {
System.out.println("在下少林->易筋经");
}
}
public class WuDangMan implements IWuDang {
@Override
public void playTaijiquan() {
System.out.println("在下武当->太极拳");
}
}
/**
* Author:xishuang
* Date:2018.02.06
* Des:静态代理需要的代理类,编译器就已经确定
*/
public class MengZhuProxy implements IWuDang, IShaolin {
private IWuDang mWuDangMan;
private IShaolin mShaolinMan;
public MengZhuProxy() {
mWuDangMan = new WuDangMan();
mShaolinMan = new ShaoLinMan();
}
@Override
public void playYiGinChing() {
System.out.println("给自己加戏");
mShaolinMan.playYiGinChing();
}
@Override
public void playTaijiquan() {
System.out.println("给自己加戏");
mWuDangMan.playTaijiquan();
}
}
就像代码里头展示的,定义好ISubject
接口,这个接口可以是多个,这里定义了两个IShaolin
和IWuDang
,再定义类图中的目标对象类ISubject
,这里的各自接口实现类是ShaoLinMan
和WuDangMan
,而MengZhuProxy
就是代理类盟主了,盟主不需要自己会少林绝学,他只需要叫少林寺的弟子去干活,盟主也不必会武当太极拳,他能控制武当派弟子就好。
/**
* 静态代理
*/
private void staticProxy() {
MengZhuProxy proxy = new MengZhuProxy();
proxy.playTaijiquan();
proxy.playYiGinChing();
}
运行效果就是这样,没啥大惊小怪的,基本上就是按照UML类图照葫芦画瓢就完成了。
3、动态代理
相比于静态代理, 动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类的函数,这也就是AOP的一种实现方式,但是只能针对接口方法进行处理,具体原因后面会顺势提到。
通过其UML类图可以发现其比标准的类图要复杂,主要引入了Proxy
类,这是Java自带的,我们通过调用Proxy
类的newProxyInstance
方法来获取一个代理类实例,此外还引入了InvocationHandler
接口及其实现类,称之为调节处理器类。
初看起来有点懵逼,让我们理一下,会发现动态代理包含了两层的静态代理关系:
- 第一层是
Proxy
和InvocationHandler
,在这层关系中,Proxy
是代理类,而InvocationHandler
是真正的实现类,即目标对象类。 - 第二层是
InvocationHandler
和ISubject
,这里的话,InvocationHandler
中持有ISubject
的实现类对象,所以InvocationHandler
是代理类,而ISubject
是最终的实现类。
会发现,饶了一圈,最后的目标对象还是ISubject
,和静态代理的最主要区别在于SubjectProxy
这个类是在程序运行中动态生成的。
续上面的武林盟主栗子,我们只需要增加一个InvocationHandler
类,这个类会接收到所有对接口方法的调用,在其中我们可以对接口方法进行拦截和实现自己的功能。
/**
* Author:xishuang
* Date:2018.02.07
* Des:具体的代理逻辑实现
*/
public class MengZhuInvocationHandler implements InvocationHandler {
/**
* InvocationHandler持有的被代理对象
*/
private IWuDang mWuDangMan;
private IShaolin mShaolinMan;
MengZhuInvocationHandler(IWuDang wuDang, IShaolin shaolin) {
mWuDangMan = wuDang;
mShaolinMan = shaolin;
}
/**
* 进行具体的代理操作
*
* @param proxy 所代理的类
* @param method 正在调用得方法
* @param args 方法的参数
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("playTaijiquan")) {
System.out.println("给自己加戏-动态代理");
mWuDangMan.playTaijiquan();
} else if (method.getName().equals("playYiGinChing")) {
System.out.println("给自己加戏-动态代理");
mShaolinMan.playYiGinChing();
}
return proxy;
}
}
最关键在于invoke()
方法,它会拦截所有实现了接口的方法,根据不同的方法名来达到我们的目的,在InvocationHandler
类完成之后,就可以进行调用,调用的代码和运行结果如下:
/**
* 动态代理
*/
private static void daynamicProxy() {
// 创建一个与代理对象相关联的InvocationHandler
InvocationHandler handler = new MengZhuInvocationHandler(new WuDangMan(), new ShaoLinMan());
// 创建一个代理对象studentProxy来代理student,代理对象的每个执行方法都会替换执行Invocation中的invoke方法
IWuDang studentProxy = (IWuDang) Proxy.newProxyInstance(getClassLoader(), new Class[]{IWuDang.class, IShaolin.class}, handler);
studentProxy.playTaijiquan();
((IShaolin) studentProxy).playYiGinChing();
}
对比静态代理的使用过程,动态代理的使用显得要复杂一些,其中最关键的是
Proxy.newProxyInstance()
方法,用于返回动态生成的代理对象,需要传入三个参数,类加载器ClassLoader
,需要实现的接口数组,以及InvocationHandler
拦截对象,这些参数都是生成动态代理对象所需要的。
4、动态代理调用分析
单单从代码调用关系上来看是比较难看出UML类图中的那种关系的,最主要原因是没有直观的看到动态生成的SubjectProxy
类,所以我们就顺着Proxy.newProxyInstance()
这条线来看一下背后的调用轨迹。
/**
* 返回指定接口的动态生成类的实例
*/
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,
InvocationHandler invocationHandler)
throws IllegalArgumentException {
...
return getProxyClass(loader, interfaces)
.getConstructor(InvocationHandler.class)
.newInstance(invocationHandler);
...
}
首先通过类加载器和接口数组来生成类文件,然后通过反射的方式来实例化对象,并把InvocationHandler
在构造方法中传入以便后续调用,继续看getProxyClass()
方法。
public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces)
throws IllegalArgumentException {
...
for (Class<?> c : interfaces) {
if (!c.isInterface()) {
// 传入的要实现类必须是接口类,否则会抛出异常
throw new IllegalArgumentException(c + " is not an interface");
}
...
}
...
Class<?> result;
synchronized (loader.proxyCache) {
result = loader.proxyCache.get(interfaceList);
if (result == null) {
String name = baseName + nextClassNameIndex++;
result = generateProxy(name, interfaces, loader, methodsArray, exceptionsArray);
loader.proxyCache.put(interfaceList, result);
}
}
return result;
}
这里只抽取关键代码,可以发现源码中限制了我们传入的需要实现的类必须是接口类,非接口类会抛出异常,最后通过本地native方法generateProxy()
生成并返回类对象。
这一个过程是比较明了的,把动态生成的类文件打印出来就知道代理类中的具体调用了,这里我们使用Java中提供的ProxyGenerator
类的静态方法generateProxyClass()
,来打印出动态生成的代理类class字节码。
private static void generyClass() {
byte[] classFile = ProxyGenerator.generateProxyClass("GenerateProxy", new Class[]{IWuDang.class, IShaolin.class});
String path = "D:/项目/proxyTest/GenerateProxy.class";
try (FileOutputStream fos = new FileOutputStream(path)) {
fos.write(classFile);
fos.flush();
System.out.println("文件写入成功");
} catch (Exception e) {
System.out.println("文件写入失败");
}
}
需要提到的一点是,在Android Studio中调用不了ProxyGenerator
这个类,避免太麻烦,所以我这边是直接把项目迁到IntelliJ IDEA中,最后生成代理class文件。
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class GenerateProxy extends Proxy implements IWuDang, IShaolin {
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m4;
private static Method m0;
public GenerateProxy(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 playTaijiquan() 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 void playYiGinChing() throws {
try {
super.h.invoke(this, m4, (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("IWuDang").getMethod("playTaijiquan");
m2 = Class.forName("java.lang.Object").getMethod("toString");
m4 = Class.forName("IShaolin").getMethod("playYiGinChing");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
SubjectProxy
继承了Proxy
并实现我们定义的接口,其中每个方法调用时没有做具体的事情,而是直接调用super.h.invoke()
,super.h
就是InvocationHandler
对象,所以SubjectProxy
把任务抛给了InvocationHandler
进行处理,而InvocationHandler
就是通过我们可以控制的,绕来绕去把控制权又交到了我们的手中。
前面提到过动态代理只能代理接口方法,原因也在这里,动态生成的类直接实现我们定义的接口,从而实现其中的接口方法,在方法调用时再把功能交还到我们的手中。动态代理可以统一对接口的所有实现类进行操作,而不用修改每个实现类,通过Proxy
->InvocationHandler
->ISubject
的顺序把控制权一步步的交接,最后真正的实现类和静态代理是一样的,只是因为要动态生成代理类从而导致中间过程饶了一点。