• Java设计模式——代理模式


    静态代理

    Java静态代理是设计模式中的一种,它通过创建一个代理类来代替原始类,从而提供额外的功能或控制原始类的访问。

    如何使用Java静态代理

    要创建一个静态代理,你需要有一个接口,一个实现该接口的目标类,以及一个代理类,该代理类也实现了该接口。代理类持有一个对目标对象的引用,并在其方法调用中调用目标对象的相应方法。

    1. // 定义一个接口
    2. interface Calculator {
    3. int add(int a, int b);
    4. }
    5. // 目标类
    6. class CalculatorImpl implements Calculator {
    7. @Override
    8. public int add(int a, int b) {
    9. return a + b;
    10. }
    11. }
    12. // 代理类
    13. class CalculatorProxy implements Calculator {
    14. private Calculator calculator;
    15. public CalculatorProxy(Calculator calculator) {
    16. this.calculator = calculator;
    17. }
    18. @Override
    19. public int add(int a, int b) {
    20. System.out.println("Before calculation");
    21. int result = calculator.add(a, b);
    22. System.out.println("After calculation");
    23. return result;
    24. }
    25. }
    26. // 测试类
    27. public class ProxyExample {
    28. public static void main(String[] args) {
    29. Calculator target = new CalculatorImpl();
    30. Calculator proxy = new CalculatorProxy(target);
    31. int result = proxy.add(5, 3);
    32. System.out.println("Result: " + result);
    33. }
    34. }

    在这个例子中,Calculator是一个接口,CalculatorImpl是目标类,CalculatorProxy是代理类。当我们调用代理类的add方法时,它会首先打印"Before calculation",然后调用目标对象的add方法,最后打印"After calculation"。 

    为什么使用静态代理

    • 解耦:可以在不修改目标对象的情况下,通过代理对象添加额外的功能或控制访问。

    • 保持干净:代理类可以为目标对象提供一个干净的接口,隐藏了目标对象的实现细节。

    • 权限控制:可以在代理类中添加额外的权限检查,以控制对目标对象的访问。

    缺点

    • 代码冗余:每次添加一个新的功能都需要创建一个新的代理类。

    • 静态:因为代理类是静态的,所以不能在运行时改变它们的行为。

    总的来说,静态代理是一种简单且直接的方式来实现代理模式,它在很多情况下都是非常有用的,特别是当你需要对目标对象进行额外控制或添加额外功能时

    动态代理:

     Java动态代理是Java语言提供的一个强大的特性,它允许你在运行时创建代理类和代理实例。这使得你可以在运行时为一个接口创建一个代理实例,而不需要手动编写实现该接口的类。动态代理通常在需要添加额外逻辑(例如日志记录、事务管理等)到现有代码中而不修改现有代码的情况下使用。

    • 是在内存中生成代理对象的一种技术
    • 无需手写代理类,也不会存在代码编译的过程。运用在内存中生产代理类的技术在JVM的运行区造一个代理对象,只需对需要修改的部分进行编辑。

    编写流程:

    1. 准备一个目标类对象
    2. 使用jdk的API动态的生成代理对象
    3. 调用代理对象执行相应的方法

     

    如何使用Java动态代理

    要使用Java动态代理,你需要先创建一个接口,然后实现一个InvocationHandler接口,最后使用Proxy.newProxyInstance()方法创建代理实例

    1.ClassLoader loader:

    固定写法,指定目标类对象的类的加载器即可,用于加载目标类及其接口的字节码文件,通常使用目标类的字节码文件调用getClassLoader()方法可得到。

    2.Class[] interfaces:

    固定写法,指定目标类的所以接口的字节码对象的数组,通常使用目标类的字节码文件调用getinterfaces()方法可得到。

    3.InvocationHander h:

    这个参数是一个接口,主要关注它里面的一个方法,它会在代理类调用方法时执行,也就是说,在代理类对象中调用的任何方法都会执行invoke()方法。所以在此方法中进行代码的扩展。

    invoke()方法中参数的含义:

    1. proxy:就是代理类对象的一个引用也就是Proxy.newProxyInstance()方法的返回值;此引用几乎不会用到。
    2.  method:对应的就是触发invoke执行的方法的Method对象。假如我们调用了Xxx方法,该方法触发了invoke执行,那么method就是Xxx方法对应的反射对象Method。
    3.   args:代理对象调用方法时传入的实际参数

     

    1. import java.lang.reflect.InvocationHandler;
    2. import java.lang.reflect.Method;
    3. import java.lang.reflect.Proxy;
    4. public interface Hello {
    5. void sayHello();
    6. }
    7. public class HelloImpl implements Hello {
    8. @Override
    9. public void sayHello() {
    10. System.out.println("Hello World!");
    11. }
    12. }
    13. public class DynamicProxyExample {
    14. public static void main(String[] args) {
    15. Hello hello = new HelloImpl();
    16. Hello proxy = (Hello) Proxy.newProxyInstance(
    17. hello.getClass().getClassLoader(),
    18. hello.getClass().getInterfaces(),
    19. new MyInvocationHandler(hello)
    20. );
    21. proxy.sayHello();
    22. }
    23. }
    24. public class MyInvocationHandler implements InvocationHandler {
    25. private Object target;
    26. public MyInvocationHandler(Object target) {
    27. this.target = target;
    28. }
    29. @Override
    30. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    31. System.out.println("Before method execution.");
    32. Object result = method.invoke(target, args);
    33. System.out.println("After method execution.");
    34. return result;
    35. }
    36. }

    在上面的例子中,我们首先定义了一个Hello接口和一个实现该接口的HelloImpl类。然后,我们创建了一个MyInvocationHandler类,实现了InvocationHandler接口,该类在方法调用前后打印日志。最后,在DynamicProxyExample类中,我们使用Proxy.newProxyInstance()方法创建了一个代理实例,并传入了一个MyInvocationHandler实例

     

    为什么使用动态代理

    • 解耦:你可以将通用逻辑(如日志记录、事务管理等)与特定的业务逻辑分开。

    • 代码复用:可以为多个对象提供相同的额外功能,而无需在每个类中重复相同的代码。

    • 运行时改变行为:你可以在运行时改变对象的行为,而无需修改源代码。

    注意事项

    • 接口代理:动态代理只能为接口创建代理,不能为类创建代理。

    • 性能开销:动态代理可能会带来一些性能开销,因为它涉及反射调用。

    总的来说,Java动态代理是一个非常强大的特性,可以在很多场景中帮助你更加灵活地管理和组织你的

  • 相关阅读:
    内部对象(Date、JSON、AJAX)
    设计模式---代理模式(结构型)
    使用libswresample库实现音频重采样
    16:00面试,16:08就出来了,问的问题有点变态。。。
    前端开发神器之 VsCode AI 辅助插件 DevChat
    【15-项目中服务的远程调用之OpenFeign&订单模块与商品模块集成使用OpenFeign的案例】
    pandas DataFrame内存优化技巧:让数据处理更高效
    MySQL中为什么要使用索引合并(Index Merge)
    keras-gpu安装
    流式数据处理与高吞吐消息传递:深入探索Kafka技术的奥秘
  • 原文地址:https://blog.csdn.net/m0_55049655/article/details/137891391