动态代理及实际应用

先回顾一下代理模式

代理模式分动态代理和静态代理,写起来也比较简单,先上代码:

public interface Goal {
    
    
    void sayHello();//定义一个接口,声明好要做的事儿
}

然后实现他,真实的目的

public class RealGoal implements Goal {
    
    

    @Override
    public void sayHello() {
    
    
        System.out.println("hello! think you very much!");
    }
}

这时候我们想打印hello! think you very much!,只需要 new RealGoal().sayHello(); 这样只执行代码就可以,但是既然是代理模式,肯定不能这样亲自动手,要找个代理人执行,所以还要创建个代理人

public class ProxyGoal implements Goal {
    
    

    private Goal goal;

    public ProxyGoal(Goal goal) {
    
    
        this.goal = goal;
    }

    @Override
    public void sayHello() {
    
    
        goal.sayHello();
    }
}

如何具体应用呢?

public class Test {
    
    

    public static void main(String[] args) {
    
    

        Goal realGoal = new RealGoal();

        Goal proxyGoal = new ProxyGoal(realGoal);
        proxyGoal.sayHello();
    }
}

执行结果:
在这里插入图片描述
**刚开始学的时候,不知道你们有没有和我一样,有那种脱裤子放屁多此一举的感觉,既然我们想执行sayHello(),直接用realGoal.sayHello()就完了呗,为什么还要搞一个代理类呢????????**在回答这个问题之前,再来看看动态代理怎么写!

public class GoalHandler implements InvocationHandler {
    
    

    Object object;

    public GoalHandler(Object object) {
    
    
        this.object = object;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
    
        System.out.println("I'm Proxy, I'm invoking...");
        method.invoke(object, args);
        System.out.println("invoke end!");
        return null;
    }
}

执行测试

    public static void main(String[] args) {
    
    

        Goal realGoal = new RealGoal();

        InvocationHandler goalHandler = new GoalHandler(realGoal);

        Goal dynamicProxyGoal = (Goal) Proxy.newProxyInstance(realGoal.getClass().getClassLoader(), realGoal.getClass().getInterfaces(), goalHandler);
        dynamicProxyGoal.sayHello();
    }

动态代理其实也一样,也多此一举,所以一定要结合使用场景,设计模式才有意义。这个代理模式很简单吧,顾名思义,多出来一个代理人,相当于你从基层员工直接变领导了,比如你想发个快递,本来你要亲自跟快递员打交道,但是代理模式让你拥有了一个小弟(proxy),有什么事儿你就叫你小弟去办理,然后你小弟再去找快递;当然这个事儿还可以是买烟,具体什么事儿无所谓,重点是多了一个小弟,而且这个小弟完全在你的掌握之中.

使用场景1:动态代理+反射 实现hook技术

hook技术很简单,所谓hook点有点找后门的意思,只要找到hook点,就可以,现在模拟一个场景,android里面有个ActivityManagerNative,这个对象,他是和AMS进行通信的对象,所有的startActivity方法最后都要经过它里面的binder对象发送给AMS,这部分没看过源码的同学可能不懂,不过不要紧这不是重点;重点是这些类 这些对象正常情况下都是遥不可及的,我们无法直接使用他们;但是通过动态代理+反射就可以,这时候我们就可以走后门了,我们可以监听到startActvity的一些信息,从而做自己的手脚,先来写个小demo
代码结构很简单,system下代表系统级别的类,我们正常开发是无法使用的;我们能使用的只有应用层的Activity如图
在这里插入图片描述
Activity就是模拟我们android里的android.app.Activity 先来看结构,system下

public interface IActivityManager {
    
    

    int startActivity(String intent);
}

public class ActivityManagerNative {
    
    

    public static IActivityManager binder = new ActivityManagerImpl();

    public static IActivityManager gDefault() {
    
    
        return binder;
    }
}
public class Log {
    
    

    public static void i(String tag, String value) {
    
    
        System.out.println(tag+" : "+value);
    }
}
public class ActivityManagerImpl implements IActivityManager {
    
    
    @Override
    public int startActivity(String intent) {
    
    
        System.out.println("我是AMS,将要启动activity对象是: "+intent);
        return 0;
    }
}

再来看application包下的activity

public class Activity {
    
    

    public void statActivity(String i) {
    
    
        ActivityManagerNative.gDefault().startActivity(i);
    }

}

上面这些都是已经存在的类,我们这时候自定义一个activity,并且执行startActvity方法,告诉系统我们要启动的Activity是哪个

public class MainActivity extends Activity {
    
    

    public static void main(String[] args) {
    
    

        MainActivity mainActivity = new MainActivity();
        mainActivity.statActivity("MainActivity");

    }
}

执行结果:在这里插入图片描述

那么问题来了!,我要在不影响正常开发的时候,我要对运行结果进行更改如何做?接着写一个类,里面包含了一个逻辑,检测要启动的activity是否包含NeedLogin,如果包含NeedLogin则直接打印LoginActivity,如果不包含就正常打印,不做改变

import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class HookUtil {
    
    

    private static final String TAG = "HookUtil";

    /**
     * 获取IActivityManager对象  将其替换掉
     * 这里需要动态代理;
     * 动态代理也是个时髦的技术;
     */
    public void hookActivityStart() {
    
    
        try {
    
    
            //获取ActivityManager类
            Class<?> activityManagerClass = Class.forName("com.example.myapplication.system.ActivityManagerNative");
            //获取其IActivityManagerSingleton属性
            Field iActivityManagerSingleton = activityManagerClass.getDeclaredField("binder");
            iActivityManagerSingleton.setAccessible(true);

            Object binder = iActivityManagerSingleton.get(activityManagerClass);

            HookHandler hookHandler = new HookHandler(binder);
            Object proxyInstance = Proxy.newProxyInstance(binder.getClass().getClassLoader(), binder.getClass().getInterfaces(), hookHandler);

            //proxyInstance代理IActivityManager
            iActivityManagerSingleton.set(activityManagerClass, proxyInstance);
        } catch (ClassNotFoundException e) {
    
    
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
    
    
            e.printStackTrace();
        } catch (IllegalAccessException e) {
    
    
            e.printStackTrace();
        }
    }

    class HookHandler implements InvocationHandler {
    
    

        private Object object;

        public HookHandler(Object object) {
    
    
            this.object = object;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
    
            //拦截startActivity()方法
            String name = method.getName();
            Log.i(TAG, "调用" + name + "方法了");
            if ("startActivity".contains(name)) {
    
    
//                Log.i(TAG, "调用startActivity方法了");
                //获取intent参数
                for (int i = 0; i < args.length; i++) {
    
    
                    Object arg = args[i];
                    if (arg instanceof String) {
    
    
                        if(((String) arg).contains("NeedLogin"))//此处判断逻辑,如果里面包含NeedLogin,则跳转LoginActivity,否则就正常执行
                            args[i] = "LoginActivity";
                    }
                }
            }

            return method.invoke(object, args);
        }
    }

}

然后我们写测试的时候这样写

public class MainActivity extends Activity {
    
    

    public static void main(String[] args) {
    
    

        HookUtil hookUtil = new HookUtil();
        hookUtil.hookActivityStart();

        MainActivity mainActivity = new MainActivity();
        mainActivity.statActivity("MainActivity");
        mainActivity.statActivity("MineActivityNeedLogin");

    }
}

执行结果:
在这里插入图片描述

我们可以看,字符串里包含NeedLogin的执行结果集完全变了,这就是典型的动态代理+反射 实现的hook结束、一般插件化、热修复或者某些场景都需要用到。

AOP(面向切面编程)

AOP的全称是Aspect-Oriented Programming,即面向切面编程(也称面向方面编程)。它是面向对象编程(OOP)的一种补充,目前已成为一种比较成熟的编程方式。就是一种编程思想。代码注入是AOP中的重要部分:AOP可用于日志埋点、性能监控、动态权限控制、甚至是代码调试等等。实现AOP的一个方式就有动态代理的模式,未完待续…

猜你喜欢

转载自blog.csdn.net/english111999/article/details/114044025