介绍
代理模式在java中分动态代理和静态代理两种,是一种较为常见的设计模式,顾名思义,它主要的形式是通过一个代理的方式去访问和操作被代理的对象,如下UML图。那么为什么使用这种模式呢,这样既增加了接口和代理类,又使得调用的链拉的更长,仿佛增加了逻辑的复杂度,直接操作对象岂不是更好?
优点
- 职责清晰:真实的角色就是实现业务逻辑,不关心其他非本职责的事务。隐藏具体实现细节,是迪米特法则的一种体现;
- 高扩展性:具体实现随时都会发生变化,只要接口在,代理类就可以在不做任何修改的情况下使用。我觉得这也正好顺应了开闭原则。
动态代理和静态代理
静态代理:代理者的代码是程序员自己或通过自动生成工具的代码再对其进行编译。在代码运行之前,代理类的class编译文件已经存在。
动态代理:通过反射机制动态生成代理者对象,在code阶段不需要知道代理谁,将在执行阶段决定。通过提供的InvocationHandler
接口的invoke
方法来决定被代理的真实方法。使我们的代码逻辑更简单。
静态代理的缺点只能为给定的实现类做代理,如果接口不同那么需要重新定义不同代理类,较为复杂,但静态代理更符合面向对象原则。
使用场景
- 想要隐藏某个类时,可以对这个类提供代理类;
- 一个类对不同的调用者提供不同的调用权限或方式;
- 代理过程中添加额外操作。
应用实例
- AndoridManagerService;(静态代理)
- Retrofit对OkHttp3的封装;(动态代理)
- 一些插件化框架对ActivityManager的代理和Intent的替换。Hook技术实现Android插件化
动态代理的实现
- 定义接口
public interface IGame {
void play();
String getLevel(int exp);
}
- 实现类
public class GameImpl implements IGame {
public void play() {
System.out.println("开始游戏!");
}
@Override
public String getLevel(int exp) {
return String.valueOf(exp / 10);
}
}
- 生成proxy
public static void main(String[] args) {
IGame game = new GameImpl();
// 动态生成代理类,
IGame proxy = (IGame) Proxy.newProxyInstance(
game.getClass().getClassLoader(),
game.getClass().getInterfaces(),
new ReadyInvocationHandler(game));
// 调用时机
proxy.play();
System.out.println(proxy.getLevel(50));
}
- 代理中调用过程
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class ReadyInvocationHandler implements InvocationHandler {
//IGame接口的实现类
private Object game = null;
public ReadyInvocationHandler(Object realSubject) {
this.game = realSubject;
}
public Object invoke(Object proxy, Method m, Object[] args) {
Object result = null;
try {
if ("play".contains(method.getName())) {
System.out.println(proxy.getClass().getSimpleName());
System.out.println("游戏开始前,开启辅助插件");
// 真正的调用play的地方
result = method.invoke(game, args);
}else if ("getLevel".equals(method.getName())){
System.out.println("开始计算等级");
// 做点小改动啥的
int exp = (int) args[0];
args[0] = exp + 20;
result = method.invoke(game, args);
}
} catch (Exception ex) {
System.exit(1);
}
return result;
}
}
- 输出结果
$Proxy0
游戏开始前,开启辅助插件
开始游戏!
开始计算等级
7
动态代理的实现原理
和静态代理的不同点是无需我们手动或依靠工具来生成代理类的代码,动态代理由JDK为我们自动实现代理对象,这又是什么原理呢?
比如现在想为RealSubject这个类创建一个动态代理对象,JDK主要会做以下工作:
1. 获取 RealSubject上的所有接口列表;
2. 确定要生成的代理类的类名,默认为:com.sun.proxy.$ProxyXXXX;
3. 根据需要实现的接口信息,在代码中动态创建 该Proxy类的字节码;
4 . 将对应的字节码转换为对应的class 对象;
5. 创建InvocationHandler 实例handler,用来处理Proxy所有方法调用;
6. Proxy 的class对象 以创建的handler对象为参数,实例化一个proxy对象