Java静态代理和动态代理详解

 更新时间:2024年11月01日 08:37:09   作者:louisgeek  
这篇文章主要介绍了Java静态代理和动态代理,本文通过代码示例给大家讲解的非常详细,对大家的学习或工作有一定的帮助,需要的朋友可以参考下

Java 静态代理和动态代理

  • 代理模式主要有静态代理和动态代理两种实现方式

静态代理

  • 指程序在运行之前所需要的代理类就已经被创建好了
  • 通常情况下代理类、目标类都需要实现同一个接口
  • 代理类内部维护了目标类的引用,在不改变目标类的情况下通过代理类增加功能来扩展目标类的逻辑功能,真正的业务逻辑还是由目标类来实现,代理类只是调用目标类的相关方法,符合开闭原则
  • 一般需要为每个目标类都创建代理类和接口,可能导致类的数量大大增加
  • 通常情况下接口一旦修改,代理类和目标类都需要修改,代码耦合度较高

示例

接口

public interface ILogin {
    void doLogin();
}

目标类

public class UserLogin implements ILogin {
  @Override
  public void doLogin(){
      System.out.print("用户登录逻辑");
  }   
}

代理类

public class UserLoginProxy implements ILogin {
  private UserLogin mUserLogin;
  public UserLoginProxy() {
    mUserLogin = new UserLogin();
  }
  @Override
  public void doLogin(){
    //...
    System.out.print("登录前逻辑");
    //真正的业务逻辑还是由目标类来实现
    mUserLogin.doLogin();
    System.out.print("登录后逻辑");     
    //...
  }
}

使用

	public static void main(String[] args){
     //
     ILogin iLogin = new UserLoginProxy();
     iLogin.doLogin();
	}

动态代理

  • 动态代理指在程序运行时通过反射机制等技术动态创建出代理对象
  • 基于接口的动态代理,通常通过 java.lang.reflect.Proxy 类和 java.lang.reflect.InvocationHandler 接口实现动态代理
  • 基于类的动态代理,比如使用 CGLIB(Code Generation Library,一个强大的 Java 字节码生成库)
  • InvocationHandler 调用处理器,是一个接口,有个 invoke 方法
  • 通过使用 Proxy#newProxyInstance 来创建指定接口的代理实现类,当调用代理对象任何方法时都会调用 InvocationHandler#invoke 方法,可以在这个方法中拿到传入的参数,注解等
  • 只能实现基于接口的动态代理,因为 Java 是单继承的,它在动态生成 $ProxyX 代理类的时候已经继承 Proxy 类了
  • 可以减少类的数量,降低工作量,灵活性高,可以在运行时动态地为不同的委托类创建代理对象
  • 减少对业务接口的依赖,降低耦合,便于后期维护,可以用于实现 AOP 面向切面编程,比如在某个逻辑功能前后添加日志记录、监控性能和权限检查控制等操作
  • 由于因为使用了反射,所以会在运行时会消耗一定的性能
private InvocationHandler mInvocationHandler = new InvocationHandler() {
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            //target 是目标类对象
            Object result = method.invoke(target,args);
            //
            return result;
        }
    };
//方式1
//创建代理类的 Class 对象,参数 (ClassLoader 类加载器,Class<?>)
Class<?> proxyClass = Proxy.getProxyClass(Toast.class.getClassLoader(), Toast.class);
//通过反射实例化代理对象
Toast toastProxy = (Toast) proxyClass.getConstructor(InvocationHandler.class).newInstance(mInvocationHandler);
//方式2,是方法1的简化版,newProxyInstance 内部代码逻辑和方法1基本一致
//直接创建代理对象,参数 (ClassLoader 类加载器,Class<?>[],InvocationHandler)
Toast toastProxy2 = (Toast) Proxy.newProxyInstance(Toast.class.getClassLoader(),new Class<?>[]{Toast.class}, mInvocationHandler);

示例

接口

public interface ICalculator {
    int add(int a, int b);
    int minus(int a, int b);
}

目标类

public class Calculator implements ICalculator {
    @Override
    public int add(int a, int b) {
        return a + b;
    }
    @Override
    public int minus(int a, int b) {
        return a - b;
    }
}

动态代理的处理类

  public class CalculatorInvocationHandler implements InvocationHandler {
      private Object target;
      public CalculatorInvocationHandler(Object target) {
          this.target = target;
      }
      @Override
      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
          // 在方法调用前可以添加一些逻辑
          Object result = method.invoke(target, args);
          // 在方法调用后也可以添加一些逻辑
          return result;
      }
  }

使用

//目标类
ICalculator calculator = new Calculator();
//代理类
ICalculator proxyCalculator = (ICalculator) Proxy.newProxyInstance(
        calculator.getClass().getClassLoader(),
        calculator.getClass().getInterfaces(),
        new CalculatorInvocationHandler(calculator)
);

到此这篇关于Java静态代理和动态代理详解的文章就介绍到这了,更多相关Java静态和动态代理内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Curator实现zookeeper的节点监听详解

    Curator实现zookeeper的节点监听详解

    这篇文章主要介绍了Curator实现zookeeper的节点监听详解,Curtor框架中一共有三个实现监听的方式,一种是NodeCache监听指定节点,一种是pathChildrenCache监听子节点,一种是TreeCache可以监控所有节点 相当于以上两种的合集,需要的朋友可以参考下
    2023-12-12
  • SpringBoot启动流程SpringApplication准备阶段源码分析

    SpringBoot启动流程SpringApplication准备阶段源码分析

    这篇文章主要为大家介绍了SpringBoot启动流程SpringApplication准备阶段源码分析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-04-04
  • Java开发过程中关于异常处理的详解

    Java开发过程中关于异常处理的详解

    异常是程序中的一些错误,但不是所有错误都是异常,且错误有时候是可以避免的。比如说,你的代码少一个分号,那运行出来结果是提示是错误 java.lang.Error;如果你用System.out.println(11/0),那么你是因为你用0做了除数,会抛出 java.lang.ArithmeticException 的异常
    2021-10-10
  • SpringBoot中日志切面实现小结

    SpringBoot中日志切面实现小结

    本文介绍了SpringBoot中日志切面实现小结,通过定义一个自定义注解和创建一个日志切面类,为方法添加日志记录功能,感兴趣的可以了解一下
    2024-11-11
  • java搭建一个Socket服务器响应多用户访问

    java搭建一个Socket服务器响应多用户访问

    本篇文章主要介绍了java搭建一个Socket服务器响应多用户访问,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-02-02
  • 详解如何通过Java实现压缩PDF文档

    详解如何通过Java实现压缩PDF文档

    PDF文档是我们日常办公中使用最频繁的文档格式。但因为大多数PDF文档都包含很多页面图像或大量图片,这就导致PDF文档过大,处理起来较为麻烦。本文将介绍如何通过Java应用程序压缩PDF文档,需要的可以了解一下
    2022-12-12
  • Java实现短信验证码的示例代码

    Java实现短信验证码的示例代码

    Java是一种流行的编程语言,验证码是一种常用的网络安全技术。Java发展至今,网上也出现了各种各样的验证码,下面是用Java实现短信验证码的总结,感兴趣的可以了解一下
    2023-03-03
  • Java实现反转义的示例代码

    Java实现反转义的示例代码

    本文主要介绍了Java实现反转义的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2025-02-02
  • java线程之线程的生命周期的使用

    java线程之线程的生命周期的使用

    本篇文章介绍了,java线程之线程的生命周期的使用。需要的朋友参考下
    2013-05-05
  • SpringBoot创建多模块项目的全过程记录

    SpringBoot创建多模块项目的全过程记录

    这篇文章主要给大家介绍了关于SpringBoot创建多模块项目的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-01-01

最新评论