设计模式 - 动态代理 - 整合

前言

关于动态代理的博客有很多,本文是对于作者自己看过的博客的总结

为什么需要代理?

程序的设计原则:扩展开放,修改关闭;在一个项目中假设已经写好了一个功能(一个功能可以理解为就是一个方法),突然有一个需求,在这个功能上面加新的功能;如果直接在原来的代码上进行添加代码,会使得代码维护起来的成本变高;为什么成本会变高?突然那个功能不要了,是不是还得去删除多余的代码,维护起来十分的麻烦;此时可以引用出来代理的概念;

既然直接在原来的代码上面修改比较麻烦,那么我创建出来一个代理类,代理类上面有一个方法,这个方法里面既可以调用原来的方法(原有的功能不变),又可以在原来方法的上下文添加自己需要的代码(达到扩展了原来的功能的效果),符合开闭原则,是一种比较好的方式;

而代理分为动态代理以及静态代理;

怎么实现代理?

静态代理(实例代码可以查看本文给的其他作者的链接,比较简单,不再赘述)

所谓的静态代理就是我给每一个需要添加新功能的类都添加一个代理,这个时候,达到了扩展功能的需要,但是每个类都加一个代理,实在是没有必要,造成的代码的冗余量太大了,有点儿不好,此时引出来了动态代理(见下面描述);
静态代理需要的东西:

1 一个原始的接口;

2 一个实现了原始接口的类;

3 一个实现了原始接口并且扩展了一个实现了原始接口的类;的类

动态代理

所谓的动态代理,就是我创建一个大家通用的代理,所有需要修改的类,也就是需要添加新功能的类,使用这一个代理;在 Java 中这个动态代理就是:实现了 InvocationHandler 的类

模仿动态代理实现,本文需要四个类:

1 一个原始的接口;

2 一个实现了原始接口的类;

3 (一个实现了InvocationHandle的类)可以在 invoke 方法中添加功能以及调用原来的功能,也就是实现了功能的扩展但是没有修改原来的代码;

4 一个使用代理对象产生的类调用方法的类;(也就是一个测试类);

在产生代理对象实例调用方法的的之前,会自动的创建一个代理对象类,不然实例是没有办法产生的;

上述的需要的四个类具体的代码实现

一个原始的接口;

public interface UserDao {

    void saveUser();
}

一个实现了原始接口的类;

public class UserDaoImpl implements UserDao{

    @Override
    public void saveUser() {
        System.out.println(" ---- 保存用户 ---- ");
    }
}

一个产生代理对象的类;

public class UserHandler implements InvocationHandler

public class UserHandler implements InvocationHandler {

    private UserDao userDao;

    public UserHandler(UserDao userDao){
        this.userDao = userDao;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        saveUserStart();
        Object obj = method.invoke(userDao, args);
        saveUserDone();
        return obj;
    }

    public void saveUserStart(){
        System.out.println("---- 开始插入 ----");
    }

    public void saveUserDone(){
        System.out.println("---- 插入完成 ----");
    }
}

一个使用代理对象产生的类调用方法的类;(也就是一个测试类);

public static void dynamicProxy(){
  // 这个实例可以动态的创建,为了给代理类传递进去参数使用,不一定是创建 userDao 对象 ,创建任意对象都行
  UserDao userDao = new UserDaoImpl();
  
  // 创建出来 InvocatioinHandler 的一个实例,因为我们在上面重写了 UserHandler 的 invoke() 方法,目的是:通过重写其invoke方法扩展被代理对象的方法内容,在重写方法的前后添加自己需要的逻辑代码
  InvocationHandler handler = new UserHandler(userDao);

  // 自动创建出来的代理类的类加载器,和被代理的类使用同样的方式加载进去,也就是同样的类加载器加载进去
  ClassLoader loader = userDao.getClass().getClassLoader();
  
  // 代理对象和目标对象(被代理对象实现相同的接口)
  Class<?>[] interfaces = userDao.getClass().getInterfaces();

  // 创建一个代理类的实例,代理类底层自动创建,这里实例化,方法调用方法
  // 反射创建代理实例
  // 代理对象创建之前,代理类会在底层自动的生成,他里面有 UserHandler invoke 的代码执行逻辑
  UserDao proxy = (UserDao)Proxy.newProxyInstance(loader, interfaces, handler);
  
  // 实际上执行的是自动创建的代理类的 invoke 方法,不是最原始的实现了接口的那个类里面的代码,因为 invoke 被重写了,添加了自己的逻辑代码进去了
  proxy.saveUser();
}

本文参考的动态代理测试代码示例

知乎高赞的一个回答

invoke函数的第一个参数是调用该方法的实例,如果该方法是静态方法,那么可以用null或者用类来代替,第二个参数是变长的,是调用该方法的参数。

上述的动态代理基于一个 User 类而产生的动态代理,有了动态代理的雏形,但是动态没有比较好的体现,这样做是为了理解起来方便一点,下面这个链接是更加抽象层面上的一个动态代理,做到了一个代理多个类使用,比较好的体现动态

趣味讲解的动态代理

关于动态代理中不同方法中不同参数的理解

Proxy.newProxyInstance的三个参数:

1.ClassLoader loader:加载代理对象的类加载器

2.Class<?>[] interfaces:代理类需要实现的接口,返回值类型即为该接口的类型

3.InvocationHandler h:调用处理器,通过重写其invoke方法扩展被代理对象的方法内容

动态代理中产生的代理类长什么样子可以通过下面博客得到

动态代理反编译结果,得到代理类的真实样貌

总结动态代理类实现的逻辑就是:主要功能是对所有的方法进行初始化,到执行某个方法的时候调用我们自己实现的代理类去执行扩展功能和原始类的方法.

posted @   YIMENG-0  阅读(20)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示