JAVA动态代理(JDK版本)

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

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成员变量,实现对非接口类方法的调用)。
目录
相关文章
|
2月前
|
安全 Java 编译器
JDK 10中的局部变量类型推断:Java编程的简化与革新
JDK 10引入的局部变量类型推断通过`var`关键字简化了代码编写,提高了可读性。编译器根据初始化表达式自动推断变量类型,减少了冗长的类型声明。虽然带来了诸多优点,但也有一些限制,如只能用于局部变量声明,并需立即初始化。这一特性使Java更接近动态类型语言,增强了灵活性和易用性。
116 53
|
2月前
|
Oracle 安全 Java
深入理解Java生态:JDK与JVM的区分与协作
Java作为一种广泛使用的编程语言,其生态中有两个核心组件:JDK(Java Development Kit)和JVM(Java Virtual Machine)。本文将深入探讨这两个组件的区别、联系以及它们在Java开发和运行中的作用。
93 1
|
2月前
|
安全 Java 开发者
AOP中的JDK动态代理与CGLIB动态代理:深度解析与实战模拟
【11月更文挑战第21天】面向切面编程(AOP,Aspect-Oriented Programming)是一种编程范式,它通过将横切关注点(cross-cutting concerns)与业务逻辑分离,以提高代码的可维护性和可重用性。在Java开发中,AOP的实现离不开动态代理技术,其中JDK动态代理和CGLIB动态代理是两种常用的方式。本文将从背景、历史、功能点、业务场景、底层逻辑等多个维度,深度解析这两种代理方式的区别,并通过Java示例进行模拟和比较。
78 5
|
2月前
|
IDE Java 编译器
开发 Java 程序一定要安装 JDK 吗
开发Java程序通常需要安装JDK(Java Development Kit),因为它包含了编译、运行和调试Java程序所需的各种工具和环境。不过,某些集成开发环境(IDE)可能内置了JDK,或可使用在线Java编辑器,无需单独安装。
85 1
|
2月前
|
Java Linux Windows
如何查看已安装的 Java 版本
要查看已安装的 Java 版本,打开命令提示符或终端,输入 `java -version`,回车后即可显示当前系统中 Java 的版本信息。
882 1
|
2月前
|
Ubuntu Java Linux
如何检查 Java 版本是否兼容
要检查Java版本是否兼容,可在命令行输入“java -version”查看当前安装的Java版本,然后对比目标应用所需的Java版本,确保其满足要求。
83 1
|
3月前
|
Java Spring 数据库连接
[Java]代理模式
本文介绍了代理模式及其分类,包括静态代理和动态代理。静态代理分为面向接口和面向继承两种形式,分别通过手动创建代理类实现;动态代理则利用反射技术,在运行时动态创建代理对象,分为JDK动态代理和Cglib动态代理。文中通过具体代码示例详细讲解了各种代理模式的实现方式和应用场景。
46 0
[Java]代理模式
|
2月前
|
Java
JAVA 静态代理 & 动态代理
【11月更文挑战第14天】静态代理是一种简单的代理模式实现,其中代理类和被代理类的关系在编译时已确定。代理类实现与被代理类相同的接口,并持有被代理类的实例,通过调用其方法实现功能增强。优点包括代码结构清晰,易于理解和实现;缺点是对于多个被代理类,需为每个类编写相应的代理类,导致代码量大增,维护成本高。动态代理则在运行时动态生成代理类,更加灵活,减少了代码冗余,但可能引入性能损耗和兼容性问题。
|
3月前
|
Java 关系型数据库 MySQL
【编程基础知识】Eclipse连接MySQL 8.0时的JDK版本和驱动问题全解析
本文详细解析了在使用Eclipse连接MySQL 8.0时常见的JDK版本不兼容、驱动类错误和时区设置问题,并提供了清晰的解决方案。通过正确配置JDK版本、选择合适的驱动类和设置时区,确保Java应用能够顺利连接MySQL 8.0。
296 1
|
3月前
|
Java
【编程进阶知识】静态代理、JDK动态代理及Cglib动态代理各自存在的缺点及代码示例
本文介绍了三种Java代理模式:静态代理、JDK动态代理和Cglib动态代理。静态代理针对特定接口或对象,需手动编码实现;JDK动态代理通过反射机制实现,适用于所有接口;Cglib动态代理则基于字节码技术,无需接口支持,但需引入外部库。每种方法各有优缺点,选择时应根据具体需求考虑。
28 1