前言
说道代理大家应该都很熟悉, 在日常生活中也有很多例子, 比如当我们无法对真实目标无法直接访问时, 需要一个代理替代我们去做这些事情, 比如国内如果要访问 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 对象
调用代理对象方法, 实质是代理对象去调用真实对象的方法, 我们可以操作的就是在委托类中拦截方法, 实现想要的逻辑
来源: https://juejin.im/post/5c934fe65188252d83554c64