面试官提问:什么是动态代理?(上)

简介: 代理这个词最早出现在代理商这个行业,所谓代理商,简而言之,其实就是帮助企业或者老板打理生意,自己本身不做生产任何商品。

一、介绍

何谓代理?

代理这个词最早出现在代理商这个行业,所谓代理商,简而言之,其实就是帮助企业或者老板打理生意,自己本身不做生产任何商品

举个例子,我们去火车站买票的时候,人少老板一个人还忙的过来,但是人一多的话,就会非常拥挤,于是就有了各种代售点,我们可以从代售点买车票,从而加快老板的卖票速度。

代售点的出现,可以说,很直观的帮助老板提升了用户购票体验

站在软件设计的角度,其实效果也是一样的,采用代理模式的编程,能显著的增强原有的功能和简化方法调用方式。

67.jpg

在介绍动态代理之前,我们先来聊解静态代理。

二、静态代理

下面,我们以两数相加为例,实现过程如下!

  • 接口类
public interface Calculator {
    /**
     * 计算两个数之和
     * @param num1
     * @param num2
     * @return
     */
    Integer add(Integer num1, Integer num2);
}
  • 目标对象
public class CalculatorImpl implements Calculator {
    @Override
    public Integer add(Integer num1, Integer num2) {
        Integer result = num1 + num2;
        return result;
    }
}
  • 代理对象
public class CalculatorProxyImpl implements Calculator {
    private Calculator calculator;
    @Override
    public Integer add(Integer num1, Integer num2) {
        //方法调用前,可以添加其他功能....
        Integer result = calculator.add(num1, num2);
        //方法调用后,可以添加其他功能....
        return result;
    }
    public CalculatorProxyImpl(Calculator calculator) {
        this.calculator = calculator;
    }
}
  • 测试类
public class CalculatorProxyClient {
    public static void main(String[] args) {
        //目标对象
        Calculator target = new CalculatorImpl();
        //代理对象
        Calculator proxy = new CalculatorProxyImpl(target);
        Integer result = proxy.add(1,2);
        System.out.println("相加结果:" + result);
    }
}
  • 输出结果
相加结果:3

通过这种代理方式,最大的优点就是:可以在不修改目标对象的前提下,扩展目标对象的功能

但也有缺点:需要代理对象和目标对象实现一样的接口,因此,当目标对象扩展新的功能时,代理对象也要跟着一起扩展,不易维护!

三、动态代理

动态代理,其实本质也是为了解决上面当目标对象扩展新功能时,代理对象也需要跟着一起扩展的痛点问题而生。

那它是怎么解决的呢?

以 JDK 为例,当需要给某个目标对象添加代理处理的时候,JDK 会在内存中动态的构建代理对象,从而实现对目标对象的代理功能。

下面,我们还是以两数相加为例,介绍具体的玩法!

3.1、JDK 中生成代理对象的玩法

  • 创建接口
public interface JdkCalculator {
    /**
     * 计算两个数之和
     * @param num1
     * @param num2
     * @return
     */
    Integer add(Integer num1, Integer num2);
}
  • 目标对象
public class JdkCalculatorImpl implements JdkCalculator {
    @Override
    public Integer add(Integer num1, Integer num2) {
        Integer result = num1 + num2;
        return result;
    }
}
  • 动态代理对象
public class JdkProxyFactory {
    /**
     * 维护一个目标对象
     */
    private Object target;
    public JdkProxyFactory(Object target) {
        this.target = target;
    }
    public Object getProxyInstance(){
        Object proxyClassObj = Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new InvocationHandler(){
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("方法调用前,可以添加其他功能....");
                        // 执行目标对象方法
                        Object returnValue = method.invoke(target, args);
                        System.out.println("方法调用后,可以添加其他功能....");
                        return returnValue;
                    }
                });
        return proxyClassObj;
    }
}
  • 测试类
public class TestJdkProxy {
    public static void main(String[] args) {
        //目标对象
        JdkCalculator target = new JdkCalculatorImpl();
        System.out.println(target.getClass());
        //代理对象
        JdkCalculator proxyClassObj = (JdkCalculator) new JdkProxyFactory(target).getProxyInstance();
        System.out.println(proxyClassObj.getClass());
        //执行代理方法
        Integer result = proxyClassObj.add(1,2);
        System.out.println("相加结果:" + result);
    }
}
  • 输出结果
class com.example.java.proxy.jdk1.JdkCalculatorImpl
class com.sun.proxy.$Proxy0
方法调用前,可以添加其他功能....
方法调用后,可以添加其他功能....
相加结果:3

采用 JDK 技术动态创建interface实例的步骤如下:

1. 首先定义一个 InvocationHandler 实例,它负责实现接口的方法调用
2. 通过 Proxy.newProxyInstance() 创建 interface 实例,它需要 3 个参数:
   (1)使用的 ClassLoader,通常就是接口类的 ClassLoader
   (2)需要实现的接口数组,至少需要传入一个接口进去;
   (3)用来处理接口方法调用的 InvocationHandler 实例。
3. 将返回的 Object 强制转型为接口

动态代理实际上是 JVM 在运行期动态创建class字节码并加载的过程,它并没有什么黑魔法技术,把上面的动态代理改写为静态实现类大概长这样:

public class JdkCalculatorDynamicProxy implements JdkCalculator {
    private InvocationHandler handler;
    public JdkCalculatorDynamicProxy(InvocationHandler handler) {
        this.handler = handler;
    }
    public void add(Integer num1, Integer num2) {
        handler.invoke(
           this,
           JdkCalculator.class.getMethod("add", Integer.class, Integer.class),
           new Object[] { num1, num2 });
    }
}

本质就是 JVM 帮我们自动编写了一个上述类(不需要源码,可以直接生成字节码)。

相关文章
|
1月前
|
设计模式 Java 索引
动态代理总结,面试你要知道的都在这里,无废话!
动态代理总结,面试你要知道的都在这里,无废话!
|
5月前
|
监控 网络协议 安全
TCP和UDP面试题提问
TCP是一种面向连接、可靠的协议,提供确认和重传机制,确保数据完整性和可靠性,适合网页浏览、邮件收发等。UDP则是无连接、轻量级协议,不保证数据可靠性,但适合实时应用如语音视频通话和在线游戏,追求低延迟。
SpringJDK动态代理实现,2024Java面试真题精选干货整理
SpringJDK动态代理实现,2024Java面试真题精选干货整理
|
6月前
|
Java 数据安全/隐私保护
【面试问题】JDK 动态代理与 CGLIB 区别?
【1月更文挑战第27天】【面试问题】JDK 动态代理与 CGLIB 区别?
|
6月前
|
Java
【面试问题】动态代理是什么?
【1月更文挑战第27天】【面试问题】动态代理是什么?
|
监控 Java Spring
【面试题精讲】JDK动态代理
【面试题精讲】JDK动态代理
|
Cloud Native Go
面试中的问题提问:如何通过提问展示你的主动性
面试中的问题提问:如何通过提问展示你的主动性
79 0
|
网络协议 前端开发 数据处理
终于有大佬把TCP/IP协议讲清楚了!面试再也不怂面试官提问了
不难看出,TCP/IP 与 OSI 在分层模块上稍有区别。OSI 参考模型注重“通信协议必要的功能是什么”,而 TCP/IP 则更强调“在计算机上实现协议应该开发哪种程序”。
|
负载均衡 Java
面试官,谈谈动态代理与RPC?我...
面试官,谈谈动态代理与RPC?我...
147 0
|
Java
Java 最常见的面试题:怎么实现动态代理?
Java 最常见的面试题:怎么实现动态代理?
143 0