• Java高级-代理(proxy)


    什么是代理

    • 代理是一种设计模式,当我们要访问目标类时,不是直接访问目标类,而是先调用其代理类,通过代理类来调用目标类完成操作。即从直接调用变为间接调用,这样做我们可以在代理类在调用目标类之气和之后去添加一些预处理和后处理的操作,来拓展不属于目标类的功能,比如说,我们可以在方法调用之前和调用结束之前,记录日志、在方法执行之前继续额外的参数校验,进行事务管理,如手动提交,权限校验等。
    • 静态代理

      • 在程序运行之前,我们就给目标类编写其代理类的代码,然后编译了其代理类,如此在程序运行之前就以及生成了它代理类的字节码文件。即我们事先编写然后编译,在程序运行的时候直接去读取这些字节码文件进行运行
    • 动态代理

    为什么需要代理

    • 代理可以转移一部分对象的职责

    代理长什么样子

    • 代理具有被代理对象中的所有方法,在这些方法执行之前或结束之前可以进行自定义的逻辑

    代理与被代理类之间通过接口来进行联系。

    示例如下

    • 被代理类
      1. package com.example.Demo;
      2. public class SuperStart implements Start {
      3. private String name;
      4. public SuperStart(String name) {
      5. this.name = name;
      6. }
      7. public String sing(String name) {
      8. System.out.println(this.name + "正在唱:" + name);
      9. return "谢谢谢谢";
      10. }
      11. public void dance() {
      12. System.out.println(this.name + "正在跳舞");
      13. }
      14. }
    • 代理类

      • 在代理类中,创建一个生成代理对象的方法,其中将被代理对象作为参数传入,即指定产生谁的代理,其实是通过最后将被代理对象作为参数传入invoke()方法中而实现的,代理对象通过Proxy.newProxyInstance()方法创建,该方法中的三个参数的作用都在代码注解中有,自己体会。

      1. package com.example.Demo;
      2. import java.lang.reflect.InvocationHandler;
      3. import java.lang.reflect.Method;
      4. public class Proxy {
      5. public static Start createProxy(SuperStart superStart) // 指定该方法产生谁的代理
      6. {
      7. // 创建一个代理对象
      8. /* ClassLoader loader,
      9. Class[] interfaces,
      10. InvocationHandler h
      11. 参数1 ClassLoader:用于指定一个类加载器,一般是当前类的类加载器,用于加载代理类
      12. 参数2 Class[] :指定代理类实行的接口列表,代理类与被代理类实现相同的接口以便能够代理被代理对象中的方法,这些接口用于指定代理长什么样子,也就是有哪些方法,被代理对象所属类,应该实现该接口,创建的代理实例应该转换为该接口类型
      13. 参数3 InvocationHandler :用于指定生成的代理对象需要干什么事情,一般是直接创建一个接口的匿名内部类对象
      14. */
      15. Start startProxy = (Start) java.lang.reflect.Proxy.newProxyInstance(Proxy.class.getClassLoader(),
      16. new Class[]{Start.class}, new InvocationHandler() {
      17. @Override // 回调方法
      18. // 该方法指定代理对象所要干的事情
      19. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      20. // 其中proxy指的是startProxy
      21. // method指的是表示被调用的方法对象
      22. // args表示被调用方法的参数数组
      23. if (method.getName().equals("sing")) {
      24. System.out.println("需要唱歌,需要话筒");
      25. } else if (method.getName().equals("dance")) {
      26. System.out.println("需要跳舞,需要准备场地");
      27. }
      28. return method.invoke(superStart, args);//通过反射机制,调用superStart对象的method方法,并传递args作为方法的参数。这样就实际执行了被代理对象的方法。
      29. }
      30. });
      31. return startProxy; // 返回生成的代理对象
      32. }
      33. }
    • 接口类

      1. package com.example.Demo;
      2. public interface Start {
      3. String sing(String name);
      4. void dance();
      5. }
    • 测试类

      1. package com.example.Demo;
      2. public class Test {
      3. public static void main(String[] args) {
      4. SuperStart superStart = new SuperStart("法老");
      5. Start startProxy = Proxy.createProxy(superStart); // 生成superStart对象的代理对象
      6. System.out.println(startProxy.sing("会魔法的老人"));
      7. startProxy.dance();
      8. }
      9. }
    • 运行测试类结果如下

    使用代理

    • 场景
      • 某系统有一个用户管理类,包含用户登录,删除用户,查询用户等功能,相同需要统计每个功能的执行耗时情况,以便后期观察程序性能。
    • 需求
      • 现在,某个初级程序员已经开发好了该模块,请观察该模块的代码,找出存在的问题,并对其进行改造。
    • 原代码
        1. package com.example.Proxy;
        2. public class UserServiceImpl implements UserService {
        3. @Override
        4. public void login(String loginName, String password) throws Exception {
        5. long starTime = System.currentTimeMillis();
        6. if ("admin" == loginName && "123456" == password) {
        7. System.out.println("登陆成功");
        8. } else {
        9. System.out.println("登陆失败");
        10. }
        11. Thread.sleep(1000);
        12. long endTime = System.currentTimeMillis();
        13. System.out.println("login方法耗时:" + (endTime - starTime) / 1000 + "s");
        14. }
        15. @Override
        16. public void deleteUser() throws Exception {
        17. long starTime = System.currentTimeMillis();
        18. System.out.println("成功删除1个用户");
        19. Thread.sleep(1500);
        20. long endTime = System.currentTimeMillis();
        21. System.out.println("deleteUser方法耗时:" + (endTime - starTime) / 1000 + "s");
        22. }
        23. }
        1. package com.example.Proxy;
        2. public interface UserService {
        3. // 登录功能
        4. void login(String loginName, String password) throws Exception;
        5. // 删除用户
        6. void deleteUser() throws Exception;
        7. }
      • 存在的问题

        • 在接口实现类中存在许多与业务不相关且重复的代码逻辑,这部分代码可以交给代理=对象处理

        • 代理如下

          1. package com.example.Proxy;
          2. import java.lang.reflect.InvocationHandler;
          3. import java.lang.reflect.Method;
          4. public class Proxy {
          5. public static UserService createProxy(UserService userService) {
          6. UserService UserServiceProxy = (UserService) java.lang.reflect.Proxy.newProxyInstance(Proxy.class.getClassLoader(), new Class[]{UserService.class}, new InvocationHandler() {
          7. @Override
          8. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
          9. if (method.getName().equals("login") || method.getName().equals("deleteUser")) {
          10. long starTime = System.currentTimeMillis();
          11. Object rs = method.invoke(userService, args);
          12. Thread.sleep(1000);
          13. long endTime = System.currentTimeMillis();
          14. System.out.println(method.getName() + "方法耗时:" + (endTime - starTime) / 1000 + "s");
          15. return rs;
          16. } else {
          17. Object rs = method.invoke(userService, args);
          18. return rs;
          19. }
          20. }
          21. });
          22. return UserServiceProxy;
          23. }
          24. }

  • 相关阅读:
    如何使用django 结合websocket 进行实时目标检测呢?以yolov5 为例
    【OpenGL】(1) 专栏介绍:OpenGL 库 | 3D 计算机图形应用 | GPGPU 计算 | 3D 建模和 3D动画 | 渲染技术介绍
    【MATLAB源码-第48期】基于matlab的16QAM信号盲解调仿真。
    【Mysql】模糊查询
    C#/.NET/.NET Core优秀项目和框架2024年5月简报
    说几句得罪人的大实话
    linux安装oracle client解决cx_Oracle.DatabaseError: DPI-1047
    postgresql-类型转换函数
    JVM(二)
    鸿蒙开发之MPChart图表开发
  • 原文地址:https://blog.csdn.net/weixin_64939936/article/details/133183917