美文网首页
设计模式之代理模式二

设计模式之代理模式二

作者: 小白小白啦 | 来源:发表于2018-08-06 10:45 被阅读13次

    设计模式之代理模式一

    现在只是对小汽车进行代理,如果要实现对火车,自行车的代理是不是需要创建火车代理类,自行车代理类等,太麻烦了。可以通过动态代理进行实现。

    JDK动态代理

    jdk动态代理
    jdk动态代理

    创建一个TimeHandler的时间处理类

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    
    /**
     * Create by fengguofei
     * Date: 2018/8/5
     * Time: 15:16
     */
    public class TimeHandler implements InvocationHandler {
    
        private Object target;
    
        public TimeHandler(Object target) {
            super();
            this.target = target;
        }
    
        /**
         * @param proxy 被代理对象
         * @param method 被代理对象的方法
         * @param args 方法的参数
         * @return 方法的返回值
         * @throws Throwable
         */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            long starttime = System.currentTimeMillis();
            System.out.println("汽车开始行驶。。。");
            method.invoke(target);
            long endtime = System.currentTimeMillis();
            System.out.println("汽车结束行驶。。。 汽车行驶时间:" + (endtime - starttime) + "毫秒!");;
            return null;
        }
    }
    

    创建Test测试类

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Proxy;
    
    /**
     * Create by fengguofei
     * Date: 2018/8/5
     * Time: 15:21
     */
    public class Test {
    
        /**
         * jdk动态代理测试
         * @param args
         */
        public static void main(String[] args) {
            Car car = new Car();
            InvocationHandler h = new TimeHandler(car);
            Class<?> cls = car.getClass();
    
            /**
             * loader 类加载器
             * interfaces 实现接口
             * h InvocationHandler
             */
            Moveable m = (Moveable) Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(), h);
            m.move();
        }
    }
    

    输出

    汽车开始行驶。。。
    汽车行驶中。。。
    汽车结束行驶。。。 汽车行驶时间:607毫秒!
    

    所谓Dynamic Proxy是这样一种class:
    它是在运行时生成的clas
    该class需要实现一组interface
    使用动态代理类时,必须实现InvocationHandler接口

    动态代理类实现步骤:
    1 创建一个实现接口InvocationHandler的类,它必须实现invoke方法
    2 创建被代理的类以及接口
    3 调用Proxy的静态方法,创建一个代理类 newProxyInstance(ClassLoader loader,Class[] interfaces, InvocationHandler h)
    4 通过代理调用方法

    cglib动态代理

    JDK动态代理 CGLIB动态代理
    只能代理实现了接口的类 针对类来实现代理的
    没有实现接口的类不能实现JDK的动态代理 对指定目标类产生一个子类,通过方法拦截技术拦截所有父类方法的调用

    创建Train类

    public class Train {
        public void move(){
            System.out.println("火车行驶中。。。");
        }
    }
    

    创建CglibProxy类

    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    
    import java.lang.reflect.Method;
    
    /**
     * Create by fengguofei
     * Date: 2018/8/5
     * Time: 18:06
     */
    public class CglibProxy implements MethodInterceptor {
    
        private Enhancer enhancer = new Enhancer();
    
        public Object getProxy(Class clazz){
            //设置创建子类的类
            enhancer.setSuperclass(clazz);
            enhancer.setCallback(this);
    
            return enhancer.create();
        }
    
        /**
         * 拦截所有目标类方法的调用
         * @param o 目标类的实例
         * @param method 目标方法的反射对象
         * @param objects 方法的参数
         * @param methodProxy 代理类的实例
         * @return
         * @throws Throwable
         */
        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
    
            System.out.println("日志开始。。。");
            //代理类调用父类的方法
            methodProxy.invokeSuper(o, objects);
            System.out.println("日志结束。。。");
            return null;
        }
    }
    

    创建测试类

    public class Client {
    
        public static void main(String[] args) {
    
            CglibProxy proxy = new CglibProxy();
            Train t = (Train)proxy.getProxy(Train.class);
    
            t.move();
        }
    }
    

    输出

    日志开始。。。
    火车行驶中。。。
    日志结束。。。
    

    JDK动态代理模拟

    动态代理实现思路:
    实现功能:通过Proxy的newProxyInstance返回代理对象
    1 声明一段源码(动态产生代理)
    2 编译源码(JDK Compiler API),产生新的类(代理类)
    3 将这个类load到内存当中,产生一个新的对象(代理对象)
    4 return 代理对象

    创建Proxy类

    package pattern.proxy.simulateProxy;
    
    import org.apache.commons.io.FileUtils;
    
    import javax.tools.JavaCompiler;
    import javax.tools.StandardJavaFileManager;
    import javax.tools.ToolProvider;
    import java.io.File;
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Method;
    import java.util.ArrayList;
    import java.util.List;
    import javax.tools.JavaCompiler.CompilationTask;
    
    /**
     * Create by fengguofei
     * Date: 2018/8/5
     * Time: 19:55
     */
    public class Proxy {
    
        public static Object newProxyInstance(Class infce, InvocationHandler h) throws Exception {
            String rt = "\r\n";
            String methodStr = "";
            for(Method m:infce.getMethods()){
                methodStr += "    @Override" + rt +
                "   public void " + m.getName() +"() {" + rt +
                " try{" + rt +
                " Method md = " + infce.getName() + ".class.getMethod(\"" + m.getName() + "\");" + rt +
                " h.invoke(this, md);" + rt +
                " }catch(Exception e){e.printStackTrace();}" + rt +
                "   }";
            }
            String str =
            "package pattern.proxy.simulateProxy;" + rt +
            "import  pattern.proxy.simulateProxy.InvocationHandler;" + rt +
            "import  java.lang.reflect.Method;" + rt +
            "public class $Proxy0 implements " + infce.getName() + " {" + rt +
                    "    public $Proxy0(InvocationHandler h) {" + rt +
                    "        super();" + rt +
                    "        this.h = h;" + rt +
                    "    }" + rt +
                    " private InvocationHandler h;" + rt +
                   methodStr + rt +
                    "}";
            //产生代理类的java文件
            String filename = System.getProperty("user.dir") + "\\bin\\pattern\\proxy\\simulateProxy\\$Proxy0.java";
            System.out.println(filename);
            File file = new File(filename);
            FileUtils.writeStringToFile(file, str);
    
            //编译
            //拿到编译器
            JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
            //文件管理者
            StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
            //获取文件
            Iterable units = fileMgr.getJavaFileObjects(filename);
    
            List<String> options = new ArrayList<String>();
            options.add("-encoding");
            options.add("UTF-8");
            //编译任务
            CompilationTask t = compiler.getTask(null, fileMgr, null, options, null, units);
            //进行编译
            t.call();
            fileMgr.close();
            //load到内存中
    //        ClassLoader cl = ClassLoader.getSystemClassLoader();
    //        System.out.println(cl.toString());//sun.misc.Launcher$AppClassLoader@18b4aac2
            DiskClassLoader diskLoader = new DiskClassLoader(System.getProperty("user.dir") + "\\bin\\pattern\\proxy\\simulateProxy");
            Class c = diskLoader.loadClass("pattern.proxy.simulateProxy.$Proxy0");
            System.out.println(c.getName());
            Constructor ctr = c.getConstructor(InvocationHandler.class);
            return ctr.newInstance(h);
        }
    }
    

    主要代码是Proxy的创建,建议看一下慕课上面的课程,很清晰,主要是字符串的拼接,编译,加载到内存,然后是创建代理类实例。但是在编写代码的时候一模一样就是不能运行,一直报错,无法把编译后的$Proxy0.class加载到内存中,可能是jdk版本或者其他问题。后来参考一看你就懂,超详细java中的ClassLoader详解创建了DiskClassLoader类加载器,就可以加载到内存了。其他的代码就参考github,不在粘贴了。
    全部演示代码在github

    相关文章

      网友评论

          本文标题:设计模式之代理模式二

          本文链接:https://www.haomeiwen.com/subject/teebvftx.html