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

简介: 本篇文章主要是对静态代理和动态代理实现思路的简述,以示例为主,少涉及理论。如果文中阐述不全或不对的,多多交流。

【版权声明】未经博主同意,谢绝转载!(请尊重原创,博主保留追究权)

https://developer.aliyun.com/article/1440714?spm=a2c6h.13148508.setting.15.4b004f0efZfm5B 

出自【进步*于辰的博客


参考笔记一,P83;笔记二,P75.4。

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、动态代理的两种形式

PS:静态代理需要手动创建代理类,进而创建代理对象,很冗余。换个思路,反射可以根据 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 类的属性进行一些设置,故进行了封装。


注意:JDK动态代理和Cglib动态代理皆可**拦截所有方法**,包括:`toString()`、`hashcode()`。不能拦截由 **final** 修饰方法,如:`getClass()`。

最后

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

本文完结。

相关文章
|
7天前
|
存储 Ubuntu Java
【Linux】已解决:Ubuntu虚拟机安装Java/JDK
【Linux】已解决:Ubuntu虚拟机安装Java/JDK
13 1
|
10天前
|
开发框架 Java Android开发
Java中的类反射与动态代理详解
Java中的类反射与动态代理详解
|
12天前
|
算法 Java 编译器
Java基础之lambda表达式(JDK1.8新特性)
Java基础之lambda表达式(JDK1.8新特性)
18 1
|
17天前
|
Java 编译器
Java健壮性 Java可移植性 JDK, JRE, JVM三者关系 Java的加载与执行原理 javac编译与JAVA_HOME环境变量介绍 Java中的注释与缩进 main方法的args参数
Java健壮性 Java可移植性 JDK, JRE, JVM三者关系 Java的加载与执行原理 javac编译与JAVA_HOME环境变量介绍 Java中的注释与缩进 main方法的args参数
19 1
|
10天前
|
设计模式 Java C++
Java中的静态代理与动态代理详解
Java中的静态代理与动态代理详解
|
12天前
|
Java API 数据处理
Java JDK 8新特性详解及应用实例
Java JDK 8新特性详解及应用实例
|
13天前
|
设计模式 监控 Java
Java中的动态代理:实现与应用
Java中的动态代理:实现与应用
|
Java 开发者 Spring
通俗易懂详解Java代理及代码实战
一、概述   代理模式是Java常用的设计模式之一,实现代理模式要求代理类和委托类(被代理的类)具有相同的方法(提供相同的服务),代理类对象自身并不实现真正的核心逻辑,而是通过调用委托类对象的相关方法来处理核心逻辑,而代理类对象主要负责为委托类对象过滤消息、预处理消息、转发消息给委托类、事后处理消息等等。
1124 0
|
4天前
|
Java 调度
Java线程的六种状态
Java线程有六种状态: 初始(NEW)、运行(RUNNABLE)、阻塞(BLOCKED)、等待(WAITING)、超时等待(TIMED_WAITING)、终止(TERMINATED)。
13 1
|
5天前
|
存储 安全 Java
Java面试题:请解释Java内存模型(JMM)是什么,它如何保证线程安全?
Java面试题:请解释Java内存模型(JMM)是什么,它如何保证线程安全?
37 13