[Java]代理模式

简介: 本文介绍了代理模式及其分类,包括静态代理和动态代理。静态代理分为面向接口和面向继承两种形式,分别通过手动创建代理类实现;动态代理则利用反射技术,在运行时动态创建代理对象,分为JDK动态代理和Cglib动态代理。文中通过具体代码示例详细讲解了各种代理模式的实现方式和应用场景。

【版权声明】未经博主同意,谢绝转载!(请尊重原创,博主保留追究权)
https://developer.aliyun.com/article/1631770
出自【进步*于辰的博客

参考笔记一,P83。

代理模式分为静态代理和动态代理,比较简单,直接上示例,一目了然。为降低大家阅读成本,所有示例的打印结果都相同:

打开事务
转账金额:100
关闭事务
1

大家可自行cp测试。

静态代理

1:面向接口、目标类与代理类实现于同一接口。

// 目标类和代理类的公共接口
interface IService {
   
    int transfer(int account);
}

// 目标类
class Target implements IService {
   
    public int transfer(int account) {
   
        System.out.println("转账金额:" + account);
        return 1;
    }
}

// 代理类
class Proxy implements IService {
   
    private Target tar;
    public Proxy(Target tar) {
   
        this.tar = tar;
    }

    public int transfer(int account) {
   
        System.out.println("打开事务");
        int x = tar.transfer(account);
        System.out.println("关闭事务");
        return x;
    }
}

class Test {
   
    public static void main(String[] args) {
   
        Proxy proxy = new Proxy(new Target());
        int x = proxy.transfer(100);
        System.out.println(x);
    }
}

2:面向继承、代理类继承于目标类。

class Target {
   
    public int transfer(int account) {
   
        System.out.println("转账金额:" + account);
        return 1;
    }
}

class Proxy extends Target {
   
    public int transfer(int account) {
   
        System.out.println("打开事务");
        int x = super.transfer(account);
        System.out.println("关闭事务");
        return x;
    }
}

class Test {
   
    public static void main(String[] args) {
   
        Proxy proxy = new Proxy();
        int x = proxy.transfer(100);
        System.out.println(x);
    }
}

动态代理

静态代理需要手动创建代理类,冗余。换个思路,反射可以根据Class信息创建实例,故可以通过反射为目标对象创建代理对象,则无需创建代理类,这就是“动态代理”。

JDK动态代理

面向接口,是JDK自带的。

interface IService {
   
    int transfer(int account);
}

class Target implements IService {
   
    public int transfer(int account) {
   
        System.out.println("转账金额:" + account);
        return 1;
    }
}

class Test {
   
    public static void main(String[] args) {
   
        Target tar = new Target();
        /**
         * 通过 Proxy.newProxyInstance() 创建代理对象。
         *     第一个参数是目标对象的类加载器,指定为哪个目标对象创建代理对象;
         *     第二个参数是目标对象实现的接口,指定目标对象和代理对象的公共接口;
         *     第三个参数是拦截器对象,指定用哪个拦截器来创建代理对象,需要实现 InvocationHandler 接口。
         */
        // 由于 proxyInstance  是通过反射创建,存储于JVM,匿名(文末说明),故上转为 IService
        IService proxyInstance = (IService) Proxy.newProxyInstance(tar.getClass().getClassLoader(),
                tar.getClass().getInterfaces(),
                new InvocationHandler() {
   
                    /**
                     * 代理(调用 transfer())时执行的方法
                     * @param proxy  代理对象,即 proxyInstance,暂不知如何使用
                     * @param method 目标对象的 Method 的 class 对象
                     * @param args   目标对象的 Method 的形参数组
                     */
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
   
                        System.out.println("打开事务");
                        // invoke() 是反射中 Method 对象执行时调用的方法,故动态代理是通过反射调用目标对象被代理的方法
                        Object result = method.invoke(tar, args);
                        System.out.println("关闭事务");
                        return result;
                    }
                });
        int x = proxyInstance.transfer(100);
        System.out.println(x);
    }
}

可以使用Lambda表达式省略new InvocationHandler()

Cglib动态代理

面向继承,基于Spring。

class Target {
   
    public int transfer(int account) {
   
        System.out.println("转账金额:" + account);
        return 1;
    }
}

// 动态代理类(拦截器)
class DynamicProxy implements MethodInterceptor {
   
    private Object tar;
    public DynamicProxy(Object tar) {
   
        this.tar = tar;
    }

    public Object createProxy() {
   
        Enhancer proxy = new Enhancer();// Enhancer 类是一种类生成器
        proxy.setCallback(this);// 设置拦截器,即自身
        proxy.setSuperclass(tar.getClass());// 设置父类,指定为哪个目标对象创建代理对象
        return proxy.create();// 创建代理对象
    }

    /**
     * 代理(调用 transfer())时执行的方法
     * @param proxy       代理对象,即 proxyInstance,暂不知如何使用
     * @param method      目标对象的 Method 的 class对象
     * @param args        目标对象的 Method 的参数数组
     * @param methodProxy 代理方法,即 Target.transfer(),暂不知如何使用
     */
    @Override
    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
   
        System.out.println("打开事务");
        Object result = method.invoke(tar, args);
        System.out.println("关闭事务");
        return result;
    }
}

class Test {
   
    public static void main(String[] args) {
   
        Target target = new Target();
        Target proxyInstance = (Target) new DynamicProxy(target).createProxy();
        int x = proxyInstance.transfer(100);
        System.out.println(x);
    }
}

同样可以使用Lambda表达式优化,不过代理对象的创建(proxy.create())需要对Enhancer 类的属性进行一些设置,故进行了封装。

注意:两种动态代理都可以拦截所有方法,如:toString()hashcode()。但不能拦截由final修饰的方法,如:getClass()

扩展

JDK动态代理的核心是“面向接口”,实际上并不需要目标对象(公共接口的实现),也就是这样:

interface IService {
   
    int transfer(int account);
}

class Test {
   
    public static void main(String[] argx) {
   
        Class z1 = IService.class;
        IService proxyInstance = (IService) Proxy.newProxyInstance(z1.getClassLoader(),
                new Class[] {
    z1 },
                (proxy, method, args) -> {
   
                    System.out.println("打开事务");
                    System.out.println("关闭事务");
                    return 1;
                });
        int x = proxyInstance.transfer(100);
        System.out.println(x);
    }
}

没有目标对象,还是代理模式吗?又有什么意义?

Mybatis为Mapper接口创建代理对象使用的就是这种方式,不需要为Mapper接口创建实现类,我寻得一篇文章 → 一文搞懂Java动态代理:为什么Mybatis Mapper不需要实现类?(转发),那位博主讲述得狠详细,大家自行转站学习,我在此就不赘述了。

最后

本文中的例子是为了阐述静态代理和动态代理的实现思想、方便大家理解而简单举出的,不一定有实用性,仅是抛砖引玉。

本文完结。

目录
相关文章
|
2月前
|
Oracle 安全 Java
深入理解Java生态:JDK与JVM的区分与协作
Java作为一种广泛使用的编程语言,其生态中有两个核心组件:JDK(Java Development Kit)和JVM(Java Virtual Machine)。本文将深入探讨这两个组件的区别、联系以及它们在Java开发和运行中的作用。
90 1
|
2月前
|
安全 Java 开发者
AOP中的JDK动态代理与CGLIB动态代理:深度解析与实战模拟
【11月更文挑战第21天】面向切面编程(AOP,Aspect-Oriented Programming)是一种编程范式,它通过将横切关注点(cross-cutting concerns)与业务逻辑分离,以提高代码的可维护性和可重用性。在Java开发中,AOP的实现离不开动态代理技术,其中JDK动态代理和CGLIB动态代理是两种常用的方式。本文将从背景、历史、功能点、业务场景、底层逻辑等多个维度,深度解析这两种代理方式的区别,并通过Java示例进行模拟和比较。
75 5
|
2月前
|
IDE Java 编译器
开发 Java 程序一定要安装 JDK 吗
开发Java程序通常需要安装JDK(Java Development Kit),因为它包含了编译、运行和调试Java程序所需的各种工具和环境。不过,某些集成开发环境(IDE)可能内置了JDK,或可使用在线Java编辑器,无需单独安装。
84 1
|
2月前
|
Java
JAVA 静态代理 & 动态代理
【11月更文挑战第14天】静态代理是一种简单的代理模式实现,其中代理类和被代理类的关系在编译时已确定。代理类实现与被代理类相同的接口,并持有被代理类的实例,通过调用其方法实现功能增强。优点包括代码结构清晰,易于理解和实现;缺点是对于多个被代理类,需为每个类编写相应的代理类,导致代码量大增,维护成本高。动态代理则在运行时动态生成代理类,更加灵活,减少了代码冗余,但可能引入性能损耗和兼容性问题。
|
3月前
|
Java
Java代码解释静态代理和动态代理的区别
### 静态代理与动态代理简介 **静态代理**:代理类在编译时已确定,目标对象和代理对象都实现同一接口。代理类包含对目标对象的引用,并在调用方法时添加额外操作。 **动态代理**:利用Java反射机制在运行时生成代理类,更加灵活。通过`Proxy`类和`InvocationHandler`接口实现,无需提前知道接口的具体实现细节。 示例代码展示了两种代理方式的实现,静态代理需要手动创建代理对象,而动态代理通过反射机制自动创建。
|
7月前
|
设计模式 Java 程序员
java动态代理(JDK和cglib)
java动态代理(JDK和cglib)
34 0
|
7月前
|
设计模式 Java 程序员
java动态代理(JDK和cglib)
java动态代理(JDK和cglib)
42 0
|
设计模式 Java 数据安全/隐私保护
剖析代理模式及Java两种动态代理(JDK动态代理和CGLIB动态代理)
本文详述了代理模式以及我们经常接触到的两种具体实现(JDK动态代理和CGLIB动态代理),为读者理解代理模式、JDK动态代理和CGLIB动态代理提供帮助
204 0
剖析代理模式及Java两种动态代理(JDK动态代理和CGLIB动态代理)
|
设计模式 Java 数据库连接
Java 动态代理机制 (一) JDK Proxy详解
JDK Proxy 代理是可以根据我们的 接口 Interface 生成类的字节码,从而可以在 Java 中为所欲为的一种技术,包括对象增强(修改成员变量),函数增强(在函数前后执行别的代码),根据接口名执行不同逻辑 等。在 Mybatis 中有典型应用。它的本质是 由 Proxy 生成一个 代理对象,实现我们的接口。这个对象中有我们的回调函数。当调用 代理对象的接口方法时,这个对象再调用我们的回调函数,我们的回调函数再调用原对象的对应方法。从而实现代理。为了实现代理模式,Proxy 用了另外一种设计模式:命令模式。 不过,如果我们没有接口,直接是个类,那么 Proxy 将不能用,我们可能需
|
Java
Java动态代理模式jdk和cglib(上)
Java动态代理模式jdk和cglib(上)
118 0
Java动态代理模式jdk和cglib(上)