java之动态代理

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中创建文件以及编辑文件!
我的核心实现是:创建一个文件夹!
=======执行结束后跟我说一下========

原创不易,欢迎转载,转载时请注明出处,谢谢!
作者:潇~萧下
原文链接:https://www.cnblogs.com/manongxiao/p/11774550.html

原文地址:https://www.cnblogs.com/manongxiao/p/11774550.html