关于动态代理实现的步骤及操作
知识重温背景:在Android APP插件化框架的实现中,需要HOOK AMS类的实例(该实例为单例实现)并做相关处理,需要使用到动态代理
一.静态代理
- 静态代理较为简单:个人感觉和装饰模式类似,新建一个类,构造函数中传入一个被代理类的实例,在代理类中声明方法传入参数,并调用传入的被代理的实例中的方法,即实现了一个简单的静态代理。
缺点:1.JAVA中的代理是基于接口声明的,需要有一个接口声明被代理实例中要实现的方法,原因是代理类Proxy生成动态实例时是根据约定的接口实现的
2如果接口中增加一个方法,则所有的静态代理类中都要添加该方法
3.需要为每一个API都指定一个代理方法
实现代码如下
1.定义的接口类:## IAction.java ##
public interface IAction {
String fly(String flag);
void run();
void eat();
void sleep();
}
2.生成具体实现类
## Pigeon.java ##
public class Pigeon implements IAction{
@Override
public String fly(String flag) {
System.out.println("信鸽飞行..."+flag);
return "成功";
}
@Override
public void run() {
System.out.println("信鸽跑路...");
}
@Override
public void eat() {
System.out.println("信鸽进食...");
}
@Override
public void sleep() {
System.out.println("信鸽休息...");
}
}
3.静态代理类实现(简单的包装了一层,在调用被代理的Pigeon实例的方法之前,添加业务新需要的操作)
## PigeonStaticProxy.java ##
public class PigeonStaticProxy {
private Pigeon pigeon;
public PigeonStaticProxy(Pigeon pigeon) {
this.pigeon = pigeon;
}
public void fly(String flag){
System.out.println("绑上信件...");//【代理类】中添加的【被代理类】中没有的操作
String result = pigeon.fly(flag);//【被代理类】中的fly() 方法
System.out.println("信件已投递..."+result);//【代理类】中添加的【被代理类】中没有的操作
}
}
4.Test
## Main.calss ##
public class Main {
public static void main(String[] args) {
Pigeon pigeon = new Pigeon();
PigeonStaticProxy pigeonProxy = new PigeonStaticProxy(pigeon);
pigeonProxy.fly("单程");// 静态代理实现
// 动态代理实现
}
}
二.动态代理
简单的说动态代理是由系统代替手动生成类,是在使用的时候再生成一个代理类。进而调用实际被代理类的方法,JAVA中的动态代理需要使用到 Proxy类 和 InvocationHandler实现。
具体实现和静态代理一样
1.首先需要有统一的接口
2.有一个具体实现类的实例
3.实现InvocationHandler 该实现中添加需要在【被代理】对象操作之外额外的业务操作## PigeonInvocationHandler.java ## public class PigeonInvocationHandler implements InvocationHandler { private IAction action; public PigeonInvocationHandler(IAction action) { this.action = action; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("动态代理:绑上信件"); Object result = method.invoke(action, args); System.out.println("动态代理:信件已投送,返回结果:"+(result==null?"null":result.toString())); return result; } }
4.Test-测试
## Main.java ## public class Main { public static void main(String[] args) { Pigeon pigeon = new Pigeon(); // 动态代理实现 PigeonInvocationHandler pigeonInvocationHandler = new PigeonInvocationHandler(pigeon); IAction actionDemoPro = (IAction) Proxy.newProxyInstance(pigeon.getClass().getClassLoader(), pigeon.getClass().getInterfaces(), pigeonInvocationHandler); actionDemoPro.fly("往返"); actionDemoPro.run();//同时接口中的所有方法均可使用 createProxyClassFile();//生成实际代理类的二进制代码 } //用来生成代理类类的class文件,可以反编译此.class文件,查看实际生成的代理类,其本质上是和上面静态代理类.java文件编译后生成的.class一样的,动态生成的代理类中间又经过了一层InvocationHandler执行业务定制的内容,TIP:如果ProxyGenerator该类不识别,可以remove掉默认的JRE:lib,然后重新Add public static void createProxyClassFile() { String name = "ProxySubject"; byte[] data = ProxyGenerator.generateProxyClass( name, new Class[] { IAction.class } ); try { FileOutputStream out = new FileOutputStream( name + ".class" ); out.write( data ); out.close(); } catch( Exception e ) { e.printStackTrace(); } } }
三动态代理内部实现
1.Proxy的代码实现中,Proxy类的主要静态变量如下
#// 映射表:用于维护类装载器对象到其对应的代理类缓存
private static Map loaderToCache = new WeakHashMap();// 标记:用于标记一个动态代理类正在被创建中
private static Object pendingGenerationMarker = new Object();// 同步表:记录已经被创建的动态代理类类型,主要被方法 isProxyClass 进行相关的判断
private static Map proxyClasses = Collections.synchronizedMap(new WeakHashMap());// 关联的调用处理器引用
protected InvocationHandler h;2.Proxy的构造方法
#// 由于 Proxy 内部从不直接调用构造函数,所以 private 类型意味着禁止任何调用 private Proxy() {} // 由于 Proxy 内部从不直接调用构造函数,所以 protected 意味着只有子类可以调用 protected Proxy(InvocationHandler h) {this.h = h;}
3.Proxy的静态方法
#public static Object newProxyInstance(ClassLoader loader, Class
引用http://www.cnblogs.com/flyoung2008/archive/2013/08/11/3251148.html 的总结
- 动态代理实现经过以下四个步骤:
- 1、实现InvocationHandler接口创建自己的调用处理器 IvocationHandler handler = new InvocationHandlerImpl(…);
- 2、通过为Proxy类指定ClassLoader对象和一组interface创建动态代理类,(ps:因为此处是根据定义的Interface动态生成的实例,所以JAVA的动态代理,是基于设计的接口实现的,是其局限之一)
Class clazz = Proxy.getProxyClass(classLoader,new Class[]{…}); - 3、通过反射机制获取动态代理类的构造函数,其参数类型是调用处理器接口类型
Constructor constructor = clazz.getConstructor(new Class[]{InvocationHandler.class}); - 4、通过构造函数创建代理类实例,此时需将调用处理器对象作为参数被传入
Interface Proxy = (Interface)constructor.newInstance(new Object[] (handler));
为了简化对象创建过程,Proxy类中的newInstance方法封装了2~4,只需两步即可完成代理对象的创建。
生成的ProxySubject继承Proxy类实现Subject接口,实现的Subject的方法实际调用处理器的invoke方法,而invoke方法利用反射调用的是被代理对象的的方法(Object result=method.invoke(proxied,args))