前言
说道代理大家应该都很熟悉,在日常生活中也有很多例子,比如当我们无法对真实目标无法直接访问时,需要一个代理替代我们去做这些事情,比如国内如果要访问google网站,一般就需要翻墙了,这就是一种代理模式。
Java中分为静态代理和动态代理模式,静态代理比较简单,在编译期就直接定义好代理类,有代理对象去访问真正对象,本文主要就讲讲动态代理,其实之前一篇Hook文章有大概说了下动态代理,有兴趣的可以看下这篇Hook文章
Activity不用注册?那就来Hook吧,今天详细说明下动态代理中的细节地方。
先来个栗子
抽象对象接口,面对接口编程,抽象出定义方法,定义两个方法
/**
* @FileName: com.example.hik.lib.proxy
* @Anthor: taolin
*/
public interface ISubject {
void setInfo(int age,String name);
void sayHello();
}
具体对象,实现接口方法:
/**
* @FileName: com.example.hik.lib.proxy
* @Anthor: taolin
*/
public class RealSubject implements ISubject{
private int age;
private String name;
@Override
public void setInfo(int age, String name) {
this.age = age;
this.name = name;
}
@Override
public void sayHello() {
System.out.println("i'm "+name+" age "+age);
}
}
也很明了,实现方法,实现自己逻辑,一个作为赋值操作,一个把赋值给打印出来。
接着来,定义一个类,实现InvocationHandler
/**
* @FileName: com.example.hik.lib.proxy
* @Anthor: taolin
*/
public class HookHandler implements InvocationHandler {
//真实对象,这里代表是realSubject
private Object mObject;
public HookHandler(Object mObject) {
this.mObject = mObject;
}
@Override
public Object invoke(Object mO, Method mMethod, Object[] mObjects) throws Throwable {
if (mMethod.getName().startsWith("setInfo")){
for (int i = 0; i < mObjects.length; i++) {
if (mObjects[i] instanceof Integer){
int age = (int) mObjects[i];
System.out.println("get age :"+age);
age = 20;
mObjects[i] = age;
}else if (mObjects[i] instanceof String){
String name = (String) mObjects[i];
System.out.println("get name :"+name);
name = "lisi";
mObjects[i] = name;
}
}
}
return mMethod.invoke(mObject,mObjects);
}
}
主要看invoke这个方法,三个参数
- 第一个Object是表示生成的代理对象,内部通过拼接字节码的方式来创建代理类
- 第二个Method,很明显就是真实对象自身被调用的方法
- 同理第三个就是方法中的参数
具体实现逻辑,当代理对象拦截到setInfo()方法时,我们拿到其中的参数,进行修改,再赋值回去,最终通过mMethod.invoke(mObject,mObjects)调用原有方法。
主代码
public class MainClass {
public static void main(String[] args) {
//生成真实对象
ISubject subject = new RealSubject();
//生成一个方法委托类对象
HookHandler hookHandler = new HookHandler(subject);
//生成代理对象,传递进去实现HookHandler对象
ISubject proxy = (ISubject) Proxy.newProxyInstance(subject.getClass().getClassLoader(), subject.getClass().getInterfaces(), hookHandler);
//这里实质就是代理对象调用的方法
proxy.setInfo(10,"zhangsan");
proxy.sayHello();
}
}
我们主要看看其中的一个方法Proxy.newProxyInstance(),三个参数
- 第一个,指定一个动态加载代理类的类加载器,这里传入classLoader即可
- 指明被代理类实现的接口,之后我们通过拼接字节码生成的类才能知道调用哪些方法,因为代理对象和真实对象实现同一个接口,所以这里传入subject.getClass().getInterfaces()即可
- 这是一个方法委托类,我们通过代理调用被代理类的方法时,就可以将方法名和方法参数都委托给这个委托类。这样我们就可以在invoke方法中,拦截到真实调用的方法以及参数
我们Run一下,看看输出
get age :10
get name :zhangsan
i'm lisi age 20
Process finished with exit code 0
可以看到,我们在invoke方法中,拦截到了setInfo方法中参数,改变之后,再赋值回去,所以,我们在调用proxy.sayHello()的时候,输出的是改变之后的值
总结
- 定义一个委托类,实现InvocationHandler,实现invoke方法,在其中实现自己想要的逻辑
- 调用Proxy.newProxyInstance()方法,将委托类对象以及代理对象实现接口和类加载器传递进去,生成一个代理proxy对象
- 调用代理对象方法,实质是代理对象去调用真实对象的方法,我们可以操作的就是在委托类中拦截方法,实现想要的逻辑