一个最纯粹的技术分享网站,打造精品技术编程专栏!编程进阶网
本文详细介绍了动态代理设计模式,包括其必要性、概念、实现方式及案例分析。动态代理允许在运行时动态创建代理对象,增强代码复用性和灵活性,减少类膨胀。文章通过对比静态代理,深入解析了动态代理的实现机制,如基于接口和类的动态代理,以及其在Retrofit中的应用。同时,讨论了动态代理的优势和潜在问题,如性能开销和调试难度。最后,提供了丰富的学习资源链接,帮助读者进一步理解和掌握动态代理。
一个真实角色必须对应一个代理角色,如果大量使用会导致类的急剧膨胀;此外,如果事先并不知道真实角色(委托类),该如何使用代理呢?这个问题可以通过Java的动态代理类来解决。
总结起来,静态代理在一些简单的场景下可以使用,但在需要更大的灵活性、扩展性和可维护性时,动态代理更为适合,动态代理在解耦方面提供了巨大的便利。
当涉及到动态代理时,有几个方面值得思考:
动态代理的概括
代理类是在运行时生成的。也就是说 Java 编译完之后并没有实际的 class 文件,而是在运行时动态生成的类字节码,并加载到JVM中。
动态代理定义
给某个对象提供一个代理对象,并由代理对象控制对于原对象的访问,即客户不直接操控原对象,而是通过代理对象间接地操控原对象。
第一个类比:代理对象是中介,原对象是房东的房子,客户就是我们需要找房的人。
静态代理:房东的房子让中介代理出租,中介代理可以直接控制房子出租,用户是不能找房东直接租房子的,而是要间接通过中介租房子。
动态代理:现在中介代理有很多,链家中介,中原地产中介,21世纪中介,贝壳中介等等,用户在所在区域搜索心仪的房子(公寓,单间,隔断房,床位,整租房等等)后才知道找具体的中介租房子。这就需要用到动态代理!
代理模式的角色分四种:
如下所示
定义业务接口和实现:习惯性地首先定义一个接口,然后提供一个或多个实现。
创建调用处理器:实现InvocationHandler接口的类,它定义了代理实例的调用处理程序。
生成代理实例:通过调用Proxy.newProxyInstance(),传入目标类的类加载器、接口数组和调用处理器来创建。
现在有一个需求:用户找中介租房子,假如根据租房的条件,将客户需求分类:比如需要公寓,单间,隔断房,床位,整租房等等。
为其他对象提供一种代理以控制对这个对象的访问。
某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以再两者之间起到中介作用。运行阶段才指定代理哪个对象。
组成元素:
Java 实现动态代理主要涉及以下几个类:
基于接口的动态代理是一种常见的动态代理方式,它通过实现Java的Proxy类和InvocationHandler接口来实现。以下是一个基于接口的动态代理的示例:
首先,定义一个接口(例如MyInterface):
public interface MyInterface {
void doSomething();
}
然后,创建一个实现InvocationHandler接口的代理处理器类(例如MyInvocationHandler):
public class MyInvocationHandler implements InvocationHandler {
private Object target;
public MyInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 在方法调用前执行额外的逻辑
System.out.println("Before method invocation");
// 调用目标对象的方法
Object result = method.invoke(target, args);
// 在方法调用后执行额外的逻辑
System.out.println("After method invocation");
return result;
}
}
接下来,创建原始对象并生成代理对象:
MyInterface originalObject = new MyOriginalObject(); // 创建原始对象
MyInvocationHandler invocationHandler = new MyInvocationHandler(originalObject); // 创建代理处理器
MyInterface proxyObject = (MyInterface) Proxy.newProxyInstance(
MyInterface.class.getClassLoader(),
new Class[]{MyInterface.class},
invocationHandler); // 创建代理对象
最后,通过代理对象调用方法:
proxyObject.doSomething();
在调用proxyObject.doSomething()时,会先执行代理处理器的invoke()方法中的前置逻辑,然后调用原始对象的doSomething()方法,最后执行代理处理器的后置逻辑。
通过基于接口的动态代理,我们可以在不修改原始对象的情况下,为其添加额外的功能或行为,例如日志记录、权限检查、性能监控等。
基于类的动态代理是另一种常见的动态代理方式,它使用第三方库或框架来实现。在Java中,常用的类动态代理库包括CGLIB和Byte Buddy。以下是一个基于CGLIB的类动态代理的示例:
首先,确保在项目中引入CGLIB库的依赖。
然后,定义一个原始类(例如MyClass):
public class MyClass {
public void doSomething() {
System.out.println("Original method");
}
}
接下来,创建一个类动态代理处理器类(例如MyMethodInterceptor),继承自CGLIB的MethodInterceptor接口:
public class MyMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
// 在方法调用前执行额外的逻辑
System.out.println("Before method invocation");
// 调用原始对象的方法
Object result = proxy.invokeSuper(obj, args);
// 在方法调用后执行额外的逻辑
System.out.println("After method invocation");
return result;
}
}
在上述代码中,我们实现了MethodInterceptor接口的intercept()方法,该方法在方法调用前后执行额外的逻辑。通过调用proxy.invokeSuper(obj, args)来调用原始对象的方法。
接着,创建原始对象并生成代理对象:
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(MyClass.class); // 设置原始类
enhancer.setCallback(new MyMethodInterceptor()); // 设置代理处理器
MyClass proxyObject = (MyClass) enhancer.create(); // 创建代理对象
使用Enhancer类来创建代理对象。通过setSuperclass()方法设置原始类,setCallback()方法设置代理处理器,然后调用create()方法生成代理对象。
最后,通过代理对象调用方法:
proxyObject.doSomething(); // 调用代理对象的方法
在调用proxyObject.doSomething()时,会先执行代理处理器的intercept()方法中的前置逻辑,然后调用原始对象的doSomething()方法,最后执行代理处理器的后置逻辑。
通过基于类的动态代理,我们可以在不修改原始类的情况下,为其添加额外的功能或行为,例如日志记录、权限检查、性能监控等。
代码如下所示Demo代码
private void testProxy() {
RealSubject realSubject = new RealSubject();
ProxyHandler proxyHandler = new ProxyHandler(realSubject);
Subject subject = (Subject) Proxy.newProxyInstance(RealSubject.class.getClassLoader(),
RealSubject.class.getInterfaces(), proxyHandler);
subject.request();
}
interface Subject{
void request();
}
class RealSubject implements Subject{
@Override
public void request(){
System.out.println("request");
}
}
/**
* 代理类的调用处理器
*/
class ProxyHandler implements InvocationHandler {
private final Subject subject;
public ProxyHandler(Subject subject){
this.subject = subject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//定义预处理的工作,当然你也可以根据 method 的不同进行不同的预处理工作
System.out.println("====before====");
Object result = method.invoke(subject, args);
System.out.println("====after====");
return result;
}
}
先来了解下反射
那么为什么要用到反射呢?
Retrofit,想必大家都很熟悉,retrofit其实核心内容就是用了动态代理。
想想retrofit是怎么工作的?在interface里面写上需要配置的请求方法,并添加一些注解 然后创建出interface的实例,就可以直接调用方法进行网络请求了。看看代码:
public interface ApiService {
@POST(RetrofitHelper.APP_V1 + "/banner")
Observable<BaseEntity<List<Banner>>> getBanners();
}
//如何调用,如下所示
ApiService service = new Retrofit.Builder().baseUrl("").build().create(ApiService.class);
service.getBanners().enqueue(callback);
只是写了ApiService接口和接口下面的getBanners方法,然后就可以进行网络请求。所以retrofit是代替我们写了网络请求的具体逻辑,也就是完成了代理的这样一个作用。
具体怎么代理的呢?奥秘主要就在这个.create(ApiService.class)方法里面,看看源码:
public <T> T create(final Class<T> service) {
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
new InvocationHandler() {
private final Platform platform = Platform.get();
@Override public Object invoke(Object proxy, Method method, @Nullable Object[] args)
throws Throwable {
ServiceMethod<Object, Object> serviceMethod =
(ServiceMethod<Object, Object>) loadServiceMethod(method);
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
}
});
}
看到这个newProxyInstance方法了吧,这就是创建动态代理类的方法。invoke方法里面就是具体去拆解,接口里面方法的一些参数,然后完成网络请求的整个过程了,也就是代理帮你做的一些事情。
1,通过实现 InvocationHandler 接口创建自己的调用处理器;
// InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发
// 其内部通常包含指向委托类实例的引用,用于真正执行分派转发过来的方法调用
InvocationHandler handler = new InvocationHandlerImpl(..);
2,通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类;
// 通过 Proxy 为包括 Interface 接口在内的一组接口动态创建代理类的类对象
Class clazz = Proxy.getProxyClass(classLoader, new Class[] { Interface.class, ... });
3,通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;
// 通过反射从生成的类对象获得构造函数对象
Constructor constructor = clazz.getConstructor(new Class[] { InvocationHandler.class });
4,通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。
// 通过构造函数对象创建动态代理类实例
Interface Proxy = (Interface)constructor.newInstance(new Object[] { handler });
为了简化对象创建过程,Proxy类中的newProxyInstance方法封装了2~4,只需两步即可完成代理对象的创建。
// InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发
InvocationHandler handler = new InvocationHandlerImpl(..);
// 通过 Proxy 直接创建动态代理类实例
Interface proxy = (Interface)Proxy.newProxyInstance( classLoader,
new Class[] { Interface.class },
handler );
需要注意的是:
动态代理的思想基于以下几个关键概念:
尽管动态代理在许多情况下非常有用,但也存在一些问题需要考虑:
01.为何要动态代理
一个真实角色必须对应一个代理角色,如果大量使用会导致类的急剧膨胀;此外,如果事先并不知道真实角色(委托类),该如何使用代理呢?这个问题可以通过Java的动态代理类来解决。
02.动态代理的概念
动态代理是指:代理类是在运行期间生成的,也就是说 Java 编译完之后并没有实际的 class 文件,而是在运行时动态生成的类字节码,并加载到JVM中。
动态代理定义:给某个对象提供一个代理对象,并由代理对象控制对于原对象的访问,即客户不直接操控原对象,而是通过代理对象间接地操控原对象。
03.动态代理的实现
代理模式的角色分四种:
04.动态代理案例
组成元素:
动态代理有两种实现方式:
05.动态代理思想和原理
动态代理实现机制,主要是通过关键类Proxy
和InvocationHandler
来实现的。
InvocationHandler
处理器对象,主要作用是通过实现方法,调用从代理类到委托类的分派转发。Proxy
创建代理对象,传入指定 ClassLoader 对象和一组 interface 来创建动态代理类,里面实现是通过反射去动态创建对象。模块 | 描述 | 备注 |
---|---|---|
GitHub | 多个YC系列开源项目,包含Android组件库,以及多个案例 | |
博客汇总 | 汇聚Java,Android,C/C++,网络协议,算法,编程总结等 | |
设计模式 | 六大设计原则,23种设计模式,设计模式案例,面向对象思想 | |
Java进阶 | 数据设计和原理,面向对象核心思想,IO,异常,线程和并发,JVM | |
网络协议 | 网络实际案例,网络原理和分层,Https,网络请求,故障排查 | |
计算机原理 | 计算机组成结构,框架,存储器,CPU设计,内存设计,指令编程原理,异常处理机制,IO操作和原理 | |
学习C编程 | C语言入门级别系统全面的学习教程,学习三到四个综合案例 | |
C++编程 | C++语言入门级别系统全面的教学教程,并发编程,核心原理 | |
算法实践 | 专栏,数组,链表,栈,队列,树,哈希,递归,查找,排序等 | |
Android | 基础入门,开源库解读,性能优化,Framework,方案设计 |
23种设计模式
23种设计模式 & 描述 & 核心作用 | 包括 |
---|---|
创建型模式 提供创建对象用例。能够将软件模块中对象的创建和对象的使用分离 | 工厂模式(Factory Pattern) 抽象工厂模式(Abstract Factory Pattern) 单例模式(Singleton Pattern) 建造者模式(Builder Pattern) 原型模式(Prototype Pattern) |
结构型模式 关注类和对象的组合。描述如何将类或者对象结合在一起形成更大的结构 | 适配器模式(Adapter Pattern) 桥接模式(Bridge Pattern) 过滤器模式(Filter、Criteria Pattern) 组合模式(Composite Pattern) 装饰器模式(Decorator Pattern) 外观模式(Facade Pattern) 享元模式(Flyweight Pattern) 代理模式(Proxy Pattern) |
行为型模式 特别关注对象之间的通信。主要解决的就是“类或对象之间的交互”问题 | 责任链模式(Chain of Responsibility Pattern) 命令模式(Command Pattern) 解释器模式(Interpreter Pattern) 迭代器模式(Iterator Pattern) 中介者模式(Mediator Pattern) 备忘录模式(Memento Pattern) 观察者模式(Observer Pattern) 状态模式(State Pattern) 空对象模式(Null Object Pattern) 策略模式(Strategy Pattern) 模板模式(Template Pattern) 访问者模式(Visitor Pattern) |
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。