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静态和动态代理内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Spring Boot中使用JDBC Templet的方法教程

    Spring Boot中使用JDBC Templet的方法教程

    这篇文章主要给大家介绍了关于在Spring Boot中使用JDBC Templet的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧。
    2018-03-03
  • SpringBoot中的事务处理问题

    SpringBoot中的事务处理问题

    这篇文章主要介绍了SpringBoot中的事务处理问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-01-01
  • springboot+vue3无感知刷新token实战教程

    springboot+vue3无感知刷新token实战教程

    本文介绍了基于Spring Boot和Vue3的无感知刷新Token的实现,包括后端token构造和刷新逻辑,以及前端的请求处理和缓存机制
    2025-03-03
  • kafka 消息队列中点对点与发布订阅的区别说明

    kafka 消息队列中点对点与发布订阅的区别说明

    这篇文章主要介绍了kafka 消息队列中点对点与发布订阅的区别说明,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-05-05
  • Java中的getBytes()方法使用详解

    Java中的getBytes()方法使用详解

    这篇文章主要介绍了Java中getBytes()方法使用的相关资料,getBytes()方法有多个重载形式,可以根据需要指定字符集来进行转换,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2025-05-05
  • 使用记事本编写java程序全过程图解

    使用记事本编写java程序全过程图解

    这篇文章主要介绍了如何使用记事本编写java程序,需要的朋友可以参考下
    2014-03-03
  • Springboot实现密码的加密解密

    Springboot实现密码的加密解密

    这篇文章主要为大家详细介绍了Springboot实现密码的加密解密,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-11-11
  • MyBatis+MyBatisPlus中遇到的一些坑及解决

    MyBatis+MyBatisPlus中遇到的一些坑及解决

    这篇文章主要介绍了MyBatis+MyBatisPlus中遇到的一些坑及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-03-03
  • SpringSecurity动态加载用户角色权限实现登录及鉴权功能

    SpringSecurity动态加载用户角色权限实现登录及鉴权功能

    这篇文章主要介绍了SpringSecurity动态加载用户角色权限实现登录及鉴权功能,很多朋友感觉这个功能很难,今天小编通过实例代码给大家讲解,需要的朋友可以参考下
    2019-11-11
  • Java如何获取文件夹下所有压缩包下指定文件

    Java如何获取文件夹下所有压缩包下指定文件

    在Java中,通过遍历文件夹并对压缩包进行解析,可以实现提取指定文件的功能,如文档、PDF等,该过程中可增加过滤条件来适应不同需求,例如文件类型或文件名过滤,该方法适用于处理大量数据时的文件管理和数据提取
    2024-09-09

最新评论