热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

JAVA动态代理(JDK版本)

java,动态

1、摘要

在代理设计模式里,代理类扮演桥接使用方和实现方的角色。使用者通过代理类获得实现类的访问权限,并通过代理类定制执行业务逻辑前、后的处理流程。

2、背景

有时候你不想直接调用实现类的方法或者将实现类的方法"包装"到业务流程中。你很自然地想到采用"新增包含实现类引用的代理类"的方式。而静态代理和动态代理都可实现"代理模式"的需求。

3、原理阐述

静态代理和动态代理模式类图

图1image

图2image

图1、2分别是静态和动态代理的类图。基于对业务控制粒度控制的需求,ProxyActor是有必要的。如果直接在使用时实例化Abstractor,你的业务逻辑变得很分散,降低了代码可读性同时增大了维护成本。

当Abstractor类的方法持续增加,RealActor必须相应地增加实现方法,如果被代理的功能模块很多,代理代码量不可控。那我们自然会思考:有没有办法可以在Abstractor增加接口时自适应地兼容新增接口呢?

针对"自适应"的需求,无非是代理类要感知Abstractor新增接口。Java的反射机制为我们提供这种感知路径,我们可以通过反射获取接口申明的所有方法集合,从而实现"Abstractor增加接口时自适应地兼容新增接口"的需求。所以动态代理应运而生。

实现动态代理时我们需要解决的几个问题:

1、如何获取接口申明的方法信息 -> Class.getInterfaces() 2、如何抽象代理对象对实现类方法的调用过程 -> invoke() 3、如何在调用代理类方法时实现对委托对象方法的调用 -> newProxyInstance()

3.1 如何获取接口申明的方法信息

java的反射机制很好地解决了该问题。我们假设AbstractActor为委托类,则利用AbstractActor.class.getInterfaces()即可获取到方法信息。

3.2 如何抽象代理对象对实现类方法的调用过程

既然代理类调用委托类方法时需要动态感知接口方法以及入参,java.lang.reflect.InvocationHandler#invoke(Object proxy, Method method, Object[] args)实现这层抽象逻辑。我们可以看出,invoke方法的语意为用哪些入参调用特定代理类的特定方法

3.3 如何在调用代理类方法时实现对委托对象方法的调用

这是JDK版本动态代理最为核心的内容,这必须了解JDK动态代理的底层实现。我们先看下委托类AbstractActor:

/** * Created by fujianbo on 2018/5/20. * * @author fujianbo * @date 2018/05/20 */ public interface AbstractActor { /** * 处理方法 */ void process(); }

代理对象是真正调用委托类方法的地方,生成的动态代理类要完成以下工作:

* 获取委托类方法信息并生成包含同名方法的代理类 * 利用java.lang.reflect.InvocationHandler#invoke实现调用委托类方法

而java.lang.reflect.Proxy#newProxyInstance的职责就是完成这两个任务。

3.3.1 获取委托类方法信息并生成包含同名方法的代理类

java.lang.reflect.Proxy#newProxyInstance源码里,通过调用以类加载器和接口列表为入参的getProxyClass0()方法,生成以下类似的代理类(仅供理解,并非真实动态代理类源码):

public final class $Proxy1 extends Proxy implements AbstractActor { private InvocationHandler handler; private $Proxy1(){} public $Proxy1(InvocationHandler v){ this.handler = handler; } public void process() { Method method = Subject.class.getMethod("process", new Class[]{int.class}); return (Integer)handler.invoke(this, method, new Object[]); } }

3.3.2 实现调用委托类方法

代理类的处理逻辑交给InvocationHandler处理,使用方实现java.lang.reflect.InvocationHandler#invoke方法,从而控制委托对象方法的调用过程。

newProxyInstance方法最后通过带参数的构造函数(入参为外部定义的InvocationHandler)创建代理对象,实现调用代理对象的任意方法都会执行InvocationHandler#invoke方法。

总结

静态代理

  • 优点:代理类只需关心自身业务逻辑。
  • 缺点

    • 代理(ProxyActor)和委托类(BaseActor)实现相同的接口,仅仅为了调用代理类时,代码重复;若接口新增方法,代理和委托类都要修改。
    • 委托类只服务同一类型的代理类(如ProxyActor -> BaseActor),如果有多个代理类需要为每个代理类生成委托类,代码会无意义地膨胀。

动态代理

  • 优点:相对于静态代理,接口所有申明的方法由invoke逻辑集中管理,多种代理的场景下无需再写委托类。
  • 缺点:不能直接代理非接口的类,需借助CGLIB的Enhancer实现代理非接口的类(通过Enhancer的callback成员变量,实现对非接口类方法的调用)。

推荐阅读
  • 实体映射最强工具类:MapStruct真香 ... [详细]
  • JavaScript 基础语法指南
    本文详细介绍了 JavaScript 的基础语法,包括变量、数据类型、运算符、语句和函数等内容,旨在为初学者提供全面的入门指导。 ... [详细]
  • 本文详细探讨了JDBC(Java数据库连接)的内部机制,重点分析其作为服务提供者接口(SPI)框架的应用。通过类图和代码示例,展示了JDBC如何注册驱动程序、建立数据库连接以及执行SQL查询的过程。 ... [详细]
  • 使用GDI的一些AIP函数我们可以轻易的绘制出简 ... [详细]
  • 本文探讨了在Java多线程环境下,如何确保具有相同key值的线程能够互斥执行并按顺序输出结果。通过优化代码结构和使用线程安全的数据结构,我们解决了线程同步问题,并实现了预期的并发行为。 ... [详细]
  • 本文介绍如何使用 Android 的 Canvas 和 View 组件创建一个简单的绘图板应用程序,支持触摸绘画和保存图片功能。 ... [详细]
  • 深入解析Spring启动过程
    本文详细介绍了Spring框架的启动流程,帮助开发者理解其内部机制。通过具体示例和代码片段,解释了Bean定义、工厂类、读取器以及条件评估等关键概念,使读者能够更全面地掌握Spring的初始化过程。 ... [详细]
  • 在尝试使用C# Windows Forms客户端通过SignalR连接到ASP.NET服务器时,遇到了内部服务器错误(500)。本文将详细探讨问题的原因及解决方案。 ... [详细]
  • 解决JAX-WS动态客户端工厂弃用问题并迁移到XFire
    在处理Java项目中的JAR包冲突时,我们遇到了JaxWsDynamicClientFactory被弃用的问题,并成功将其迁移到org.codehaus.xfire.client。本文详细介绍了这一过程及解决方案。 ... [详细]
  • 本文详细介绍如何在VSCode中配置自定义代码片段,使其具备与IDEA相似的代码生成快捷键功能。通过具体的Java和HTML代码片段示例,展示配置步骤及效果。 ... [详细]
  • 深入解析 Spring Security 用户认证机制
    本文将详细介绍 Spring Security 中用户登录认证的核心流程,重点分析 AbstractAuthenticationProcessingFilter 和 AuthenticationManager 的工作原理。通过理解这些组件的实现,读者可以更好地掌握 Spring Security 的认证机制。 ... [详细]
  • 本文探讨了在Java中实现系统托盘最小化的两种方法:使用SWT库和JDK6自带的功能。通过这两种方式,开发者可以创建跨平台的应用程序,使窗口能够最小化到系统托盘,并提供丰富的交互功能。 ... [详细]
  • Calendar.DAY_OF_WEEK用来获取今天是本周第几天。在获取本月第一天是本周第几天的时候,我们可以先将几天设为本月第一天。实现如下:cld.set(Calendar.DATE,1); ... [详细]
  • 本文介绍了Android开发中Intent的基本概念及其在不同Activity之间的数据传递方式,详细展示了如何通过Intent实现Activity间的跳转和数据传输。 ... [详细]
  • Java 中的月减()方法 ... [详细]
author-avatar
mobiledu2502864045
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有