[Java]静态代理与动态代理(基于JDK1.8)

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

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

参考笔记一,P83。

1、简介

什么是代理模式?“代理模式”指通过为目标对象(原代码)创建代理对象,将附加功能(附加代码)注入目标对象的方法,从而实现附加功能的设计模式,分为静态代理和动态代理。

什么是静态代理?“静态代理”指为目标类手动创建代理类的代理方式。

什么是动态代理?“动态代理”指在不变动原代码的情况下,通过反射动态创建代理对象的代理方式。(注:“反射”是动态代理的底层,不可见)

2、静态代理的两种形式

2.1 面向接口

特点:目标对象与代理对象隶属于同一接口。

看下述代码:
1、公共接口:目标类和代理类的公共接口。

interface IService {
   
    int transfer(int money);
}

2、目标类。

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

3、代理类。

class Proxy implements IService {
   
    private Target target;
    public Proxy(Target target) {
   
        this.target = target;
    }

    @Override
    public int transfer(int score) {
   
        System.out.println("打开事务");// 附加功能
        int x = target.transfer(score);
        System.out.println("关闭事务");
        return x;
    }
}

测试。

class Test {
   
    public static void main(String[] args) {
   
        Proxy proxy = new Proxy(new Target());// 创建代理对象
        int x = proxy.transfer(10);
        if (x > 0)
            System.out.println("转账成功");
        else
            System.out.println("转账失败");
    }
}

测试结果:
在这里插入图片描述

2.2 面向继承

特点:目标对象与代理对象是继承关系,代理对象继承于目标对象。

看下述代码:
1、目标类。

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

3、代理类。

class Proxy extends Target {
   
    @Override
    public int transfer(int money) {
   
        System.out.println("打开事务");// 附加功能
        int x = super.transfer(money);
        System.out.println("关闭事务");
        return x;
    }
}

测试。

class Test {
   
    public static void main(String[] args) {
   
        Proxy proxy = new Proxy();// 创建代理对象
        int x = proxy.transfer(20);
        if (x > 0)
            System.out.println("转账成功");
        else
            System.out.println("转账失败");
    }
}

测试结果:
在这里插入图片描述

3、动态代理的两种形式

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

3.1 JDK动态代理

特点:面向接口,隶属于Java API

看下述代码:
1、公共接口。

/**
 * 目标对象与代理对象的公共接口
 * 注:因为JDK动态代理面向接口,故目标对象和代理对象实现于同一接口
 */
interface IService {
   
    int transfer(int money);
}

2、目标类。

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

测试。

class Test {
   
    public static void main(String[] args) {
   
        Target target = new Target();
        /**
         * 通过 newProxyInstance() 创建代理对象
         *     第一个参数是目标对象的类加载器,指定为哪个目标对象创建代理对象;
         *     第二个参数是目标对象实现的接口,指定目标对象和代理对象的公共接口;
         *     第三个参数是拦截器对象,指定用哪个拦截器来创建代理对象,需要实现 InvocationHandler 接口。
         */
        // 由于代理对象 proxy 是通过反射创建于JVM,并无类存在,故要上转为公共接口 IService
        IService proxyInstance = (IService) Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.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(target, args);
                        System.out.println("关闭事务");
                        return result;
                    }
                });// 创建代理对象
        int x = proxyInstance.transfer(50);
        if (x > 0)
            System.out.println("转账成功");
        else
            System.out.println("转账失败");
    }
}

测试结果:
在这里插入图片描述
可以用Lambda表达式进行简化。

3.2 Cglib动态代理

特点:面向继承,隶属于Spring API

看下述代码:
1、目标类。

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

2、代理类。

/**
 * Cglib动态代理类,需实现接口 MethodInterceptor
 */
class DynamicProxy implements MethodInterceptor {
   
    private Object target;
    public DynamicProxy(Object target) {
   
        this.target = target;
    }
    public Object createProxy() {
   
        Enhancer proxy = new Enhancer();// Enhancer 类是一种类生成器
        proxy.setCallback(this);// 设置拦截器,即自身
        proxy.setSuperclass(target.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("打开事务");// 附加功能
        // invoke() 是反射中 Method 对象执行时调用的方法,故动态代理是通过反射调用目标对象被代理的方法
        Object result = method.invoke(target, 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);
        if (x > 0)
            System.out.println("转账成功");
        else
            System.out.println("转账失败");
    }
}

测试结果:
在这里插入图片描述

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

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

最后

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

本文完结。

相关文章
|
11天前
|
Java
Java访问外网图片地址时,如何添加代理?
【10月更文挑战第14天】Java访问外网图片地址时,如何添加代理?
19 2
|
16天前
|
Java
【编程进阶知识】静态代理、JDK动态代理及Cglib动态代理各自存在的缺点及代码示例
本文介绍了三种Java代理模式:静态代理、JDK动态代理和Cglib动态代理。静态代理针对特定接口或对象,需手动编码实现;JDK动态代理通过反射机制实现,适用于所有接口;Cglib动态代理则基于字节码技术,无需接口支持,但需引入外部库。每种方法各有优缺点,选择时应根据具体需求考虑。
16 1
|
16天前
|
Java
让星星⭐月亮告诉你,jdk1.8 Java函数式编程示例:Lambda函数/方法引用/4种内建函数式接口(功能性-/消费型/供给型/断言型)
本示例展示了Java中函数式接口的使用,包括自定义和内置的函数式接口。通过方法引用,实现对字符串操作如转换大写、数值转换等,并演示了Function、Consumer、Supplier及Predicate四种主要内置函数式接口的应用。
20 1
|
17天前
|
Java
Java基础之 JDK8 HashMap 源码分析(中间写出与JDK7的区别)
这篇文章详细分析了Java中HashMap的源码,包括JDK8与JDK7的区别、构造函数、put和get方法的实现,以及位运算法的应用,并讨论了JDK8中的优化,如链表转红黑树的阈值和扩容机制。
17 1
|
8天前
|
Java
Java代码解释静态代理和动态代理的区别
### 静态代理与动态代理简介 **静态代理**:代理类在编译时已确定,目标对象和代理对象都实现同一接口。代理类包含对目标对象的引用,并在调用方法时添加额外操作。 **动态代理**:利用Java反射机制在运行时生成代理类,更加灵活。通过`Proxy`类和`InvocationHandler`接口实现,无需提前知道接口的具体实现细节。 示例代码展示了两种代理方式的实现,静态代理需要手动创建代理对象,而动态代理通过反射机制自动创建。
|
5月前
|
设计模式 Java 程序员
java动态代理(JDK和cglib)
java动态代理(JDK和cglib)
30 0
|
5月前
|
设计模式 Java 程序员
java动态代理(JDK和cglib)
java动态代理(JDK和cglib)
40 0
|
设计模式 Java 数据安全/隐私保护
剖析代理模式及Java两种动态代理(JDK动态代理和CGLIB动态代理)
本文详述了代理模式以及我们经常接触到的两种具体实现(JDK动态代理和CGLIB动态代理),为读者理解代理模式、JDK动态代理和CGLIB动态代理提供帮助
193 0
剖析代理模式及Java两种动态代理(JDK动态代理和CGLIB动态代理)
|
设计模式 Java 数据库连接
Java 动态代理机制 (一) JDK Proxy详解
JDK Proxy 代理是可以根据我们的 接口 Interface 生成类的字节码,从而可以在 Java 中为所欲为的一种技术,包括对象增强(修改成员变量),函数增强(在函数前后执行别的代码),根据接口名执行不同逻辑 等。在 Mybatis 中有典型应用。它的本质是 由 Proxy 生成一个 代理对象,实现我们的接口。这个对象中有我们的回调函数。当调用 代理对象的接口方法时,这个对象再调用我们的回调函数,我们的回调函数再调用原对象的对应方法。从而实现代理。为了实现代理模式,Proxy 用了另外一种设计模式:命令模式。 不过,如果我们没有接口,直接是个类,那么 Proxy 将不能用,我们可能需
|
Java
Java动态代理模式jdk和cglib(上)
Java动态代理模式jdk和cglib(上)
112 0
Java动态代理模式jdk和cglib(上)