欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

java之动态代理

程序员文章站 2022-06-09 20:40:47
...

Java之动态代理

基本说明

  • 我们的目标是:在不改变目标对象方法的情况下对方法进行增强!

  • 动态代理类的字节码在程序运行时由Java反射机制动态生成,而无需手动编写它的源代码。

    • 简化了编程工作
    • 提高了软件系统的可扩展性
  • Java 反射机制可以生成任意类型的动态代理类。java.lang.reflect 包中的Proxy类和InvocationHandler 接口提供了生成动态代理类的能力。

例子

  • 目标:现在有一个linux命令的接口LinuxService,接口中有个方法是创建目录的方法mkdir,想在创建目录的时候能指定一个目标路径。但是不改变LinuxService中的方法。
  • 方案:使用动态代理,在使用LinuxService接口中的mkdir时,代理对象指定目录,但在调用方看来,使用的就是LinuxService类的实例中的mkdir方法。

代码

接口LinuxService

public interface LinuxService {
    /**
     * make directory, 创建目录
     */
    void mkdir();
    /**
     * 如果文件不存在,则会建立空文件;如果文件已经存在,则会修改文件的时间戳
     */
    void touch();
    /**
     * 编辑文件
     */
    void vim();
    /**
     * 连接文件并打印输出到标准输出  查看文件内容
     */
    void cat();
}

实现类LinuxServiceImpl

public class LinuxServiceImpl implements LinuxService {
    private static String centreMessage = "我的核心实现是:";
    @Override
    public void mkdir() {
        System.out.println(centreMessage+"创建一个文件夹!");
    }
    @Override
    public void touch() {
        System.out.println(centreMessage+"创建一个用来编辑的文件!");
    }
    @Override
    public void vim() {
        System.out.println(centreMessage+"打开上面的文件并编辑!");
    }
    @Override
    public void cat() {
        System.out.println(centreMessage+"查看编辑过后的文件!");
    }
}

想要代理接口的代理对象

public class LinuxServiceProxy implements InvocationHandler {
   /**
    * 要代理的核心对象  B是A的代理,则target=A
    * B = LinuxServiceProxy     A = LinuxService
    */
    private Object target;
    public LinuxServiceProxy(Object target){
        this.target = target;
    }
    /**
     * jdk动态代理
     * @param proxy  指被代理的对象。
     * @param method  要调用的方法
     * @param args  方法调用时所需要的参数
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("=======代理对象开始工作========");
        System.out.println("进入/Users/mac/Documents目录下后,创建一个新目录test,之后在/test中创建文件以及编辑文件!");
        //执行真正的核心的方法 即执行LinuxService中接口中真正关心的方法。
        Object result=method.invoke(target, args);
        System.out.println("=======执行结束后跟我说一下========");
        return result;
    }
}

执行类

public class TestLinuxProxy {
    public static void main(String[] args) {
        /**
         * 有一个linux方法的接口,它定义了要实现这些方法的规范,LinuxServiceImpl是接口的具体实现。
         * 我要用它方法的具体实现,但我想先指定一个目录。
         */
        LinuxService linuxService = new LinuxServiceImpl();
        /**
         * 想指定一个目录,但是LinuxService并没有提供指定目录的功能
         * 我去找它的代理。
         */
        LinuxServiceProxy linuxServiceProxy = new LinuxServiceProxy(linuxService);
        /**
         * Proxy类是专门完成代理的操作类,通过newProxyInstance方法为接口动态的生成实现类。
         * 尽管我想指定一个目录, 但我核心是想调用LinuxService中的方法,因为我最终要调用的对象应该是	LinuxService下面的方法。
         * 底层的实现我不在乎,在乎的是我调用的是LinuxService接口中的方法
         */
        /**
         * 三个参数的说明:
         * ClassLoader loader:类加载器
         * Class<?>[] interfaces:得到全部的接口
         * InvocationHandler h:得到InvocationHandler接口的子类实例
         */
        LinuxService asr = (LinuxService) Proxy
                .newProxyInstance(linuxService.getClass().getClassLoader(),
                                  linuxService.getClass().getInterfaces(),
                                  linuxServiceProxy);
        /**
         * 调用生成目录的方法,用返回的代理。
         */
        asr.mkdir();
    }
}

结果

=======代理对象开始工作========
进入/Users/mac/Documents目录下后,创建一个新目录test,之后在/test中创建文件以及编辑文件!
我的核心实现是:创建一个文件夹!
=======执行结束后跟我说一下========
相关标签: java