设计模式-动态代理

今天复习了一下马士兵老师讲的动态代理,理解稍微深了一些。这边写个博客记录一下。

源码链接:https://github.com/wenrongyao/proxy.git

类别:动态代理分为静态代理和动态代理两种。

实现方式:继承和聚合

1、静态代理

这边比较简单,有兴趣的自己可以把代码拉下来看看。

2、动态代理

功能:可以实现任何类的任何方法的任何代理。(所谓代理简单来说就是在不改变原来的方法的前提下增加逻辑)

工程结构

其中最重要的类就是Proxy。代码上方都有注释,需要了解的知识有,JavaCompiler, java的反射机制。

Proxy类

package com.wry.dynamicproxy;

import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;

/**
 * @author rongyao wen
 * @date 2018/7/29
 */
public class Proxy {
    public static Object newInstance(Class inface, InvocationHandler ih) throws Exception{
        String rt = "\r\t";
        String methodStr= "";
        Method methods[] = inface.getMethods();
        /**
         * 循环生成被代理的方法
         */
        for(Method m: methods){
            methodStr =
                    "    @Override"+rt +
                            "    public void "+m.getName()+"() {"+rt +
                            "       try{"+rt+
                            "           Method md = "+inface.getName()+".class.getMethod(\""+ m.getName() + "\");"+rt +
                            "           ih.invoke(md);"+rt +
                            "       }catch(Exception e){e.printStackTrace();}"+rt+
                            "    }";
        }
        /**
         * src是根据传入的接口,动态的生成的代理类的源码。
         */
        String src =
                "package com.wry.dynamicproxy;" + rt +
                "import java.lang.reflect.Method; "+rt+
                "public class $Proxy implements "+inface.getName()+" {" +rt +
                "    com.wry.dynamicproxy.InvocationHandler ih;"+rt +

                "    public $Proxy(InvocationHandler ih) {"+rt +
                "        this.ih = ih;"+rt +
                "    }"+rt +
                methodStr +
                "}";

        /**
         *  这一部分将源码写入到硬盘上,具体如下。
         */
        String fileName = System.getProperty("user.dir") + "/com/wry/dynamicproxy/$Proxy.java";
        File file = new File(fileName);
        FileWriter fw = new FileWriter(file);
        fw.write(src);
        fw.flush();
        fw.close();

        /**
         *  这边利用java1.6以后提供的编译接口,编译java文件(相当于运行javac命令)生成class文件
         *  可以直径利用cglib直接生成class文件,就可以不用生成java文件)
         */
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
        Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjects(fileName);
        compiler.getTask(null, fileManager, null, null, null, compilationUnits).call();
        fileManager.close();

        /**
         *  这一部分将生成的字节码文件加载到内存,利用java的反射机制,生成代理类。
         */
        URL urls[] = new URL[]{new URL("file:/" + System.getProperty("user.dir")+"/")};
        URLClassLoader urlClassLoader = new URLClassLoader(urls);
        Class c = urlClassLoader.loadClass("com.wry.dynamicproxy.$Proxy");
        Object obj = c.getConstructor(InvocationHandler.class).newInstance(ih);
        return obj;
    }

}

下面展示一份由上述代码生成的java文件

package com.wry.dynamicproxy;
	import java.lang.reflect.Method; 
	public class $Proxy implements com.wry.dynamicproxy.Moveable {
	    com.wry.dynamicproxy.InvocationHandler ih;
	    public $Proxy(InvocationHandler ih) {
	        this.ih = ih;
	    }
	    @Override
	    public void moveable() {
	       try{
	           Method md = com.wry.dynamicproxy.Moveable.class.getMethod("moveable");
	           ih.invoke(md);
	       }catch(Exception e){e.printStackTrace();}
	    }}

上述就可以在方法加逻辑实现了。那么具体的逻辑实现加什么,这就看你自己需要什么了。实现方式

首先逻辑有个接口叫InvocationHandler,这是一个接口,这个接口在代理在代理类中被赋上了真正的实现。

package com.wry.dynamicproxy;

import java.lang.reflect.Method;

/**
 * @author rongyao wen
 * @date 2018/7/29
 */
public interface InvocationHandler {
    public void invoke(Method m);
}

逻辑实现类,这边以LogInvocationHandler

package com.wry.dynamicproxy;

import java.lang.reflect.Method;

/**
 * @author rongyao wen
 * @date 2018/7/29
 */
public class LogInvocationHandler implements InvocationHandler {
    Object target;

    public LogInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public void invoke(Method md) {
        System.out.println("日志记录开始");
        try {
            md.invoke(target);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("日志记录结束");
    }
}

其中target对象是被代理的那个类,invoke中是具体代理逻辑的实现。

被代理类

package com.wry.dynamicproxy;

/**
 * @author rongyao wen
 * @date 2018/7/29
 */
public class Tank implements Moveable {
    @Override
    public void moveable() {
        System.out.println("Tank start....");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Tank stop....");
    }
}

测试函数:

package com.wry.dynamicproxy;


/**
 * @author rongyao wen
 * @date 2018/7/29
 */
public class Client {
    public static void main(String args[]) throws Exception {
        Tank t = new Tank();
        InvocationHandler ih = new LogInvocationHandler(t);
        Moveable m = (Moveable) Proxy.newInstance(Moveable.class, ih);
        m.moveable();

    }
}

结果:在没有修改tank的方法的前提下,加入了日志的处理。

整体调用流程如下

猜你喜欢

转载自blog.csdn.net/wrongyao/article/details/81276456