• 设计模式之代理模式(3)


    观看这篇文章前,请先阅读设计模式之代理模式(1)

    静态代理会发生类爆炸?那jdk的使用的动态代理到底是怎么做到的呢?我来大概模拟一下jdk的动态代理。

    这是我的目录结构:(可先跳过代码,到最下面听下我的BB,在对照代码来看!)

    我先来介绍一下这些兄弟:

    Tank:

    package cn.asto.proxy;
    
    import java.util.Random;
    
    public class Tank implements Moveable{
    
        public void move(){
            System.out.println("Tank is moving...");
            try {
                Thread.sleep(new Random(47).nextInt(10000));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        
        }
    }

    Moveable:

    package cn.asto.proxy;
    
    public interface Moveable {
    
        public void move();
    }

    InvocationHandler(一个接口):

    package cn.asto.proxy;
    
    import java.lang.reflect.Method;
    
    public interface InvocationHandler {
    
        public void invoke(Object o,Method m);
    }

    TimeHandler(实现了InvocationHandler的接口)

    package cn.asto.proxy;
    
    
    import java.lang.reflect.Method;
    
    public class TimeHandler implements InvocationHandler{
    
        private Object target;
        public TimeHandler(Object target){
            this.target = target;
        }
    
        @Override
        public void invoke(Object o, Method m)  {
            
            long startTime = System.currentTimeMillis();
            System.out.println("startTime:"+startTime);
               
    
            try {
                m.invoke(target,null);
            } catch (Exception e) {
                
                e.printStackTrace();
            } 
            long endTime = System.currentTimeMillis();
            System.out.println("Time:"+(endTime-startTime));
            
        }
    
    }

    Proxy(总代理):

    package cn.asto.proxy;
    
    import java.io.FileWriter;
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Method;
    import java.net.URL;
    import java.net.URLClassLoader;
    
    import javax.tools.JavaCompiler;
    import javax.tools.JavaCompiler.CompilationTask;
    import javax.tools.StandardJavaFileManager;
    import javax.tools.ToolProvider;
    public class Proxy {
    
        public static Object getProxyInstance(Class inface,InvocationHandler h) throws Exception{
            String rt ="
    	";
            String methodStr = "";
    //        for(Method m:inface.getMethods()){
    //            methodStr+="    @Override" + rt+
    //                        "    public void " + m.getName() + "(){" + rt +
    //                        "            long startTime = System.currentTimeMillis();"+ rt +
    //                        "            System.out.println("startTime:"+startTime);"+ rt +
    //                        "            t." + m.getName() + "()" + ";"+ rt +
    //                        "            long endTime = System.currentTimeMillis();"+ rt +
    //                        "            System.out.println("Time:"+(endTime-startTime));"+ rt +
    //
    //                        "     }" + rt;
    //
    //        }
            for(Method m : inface.getMethods()) {
                methodStr += "@Override" + rt + 
                             "public void " + m.getName() + "() {" + rt +
                             "    try {" + rt +
                             "    Method md = " + inface.getName() + ".class.getMethod("" + m.getName() + "");" + rt +
                             "    h.invoke(this, md);" + rt +
                             "    }catch(Exception e) {e.printStackTrace();}" + rt +
                            
                             "}";
            }
    
            
            
            String str = 
            
            "package cn.asto.proxy;"+ rt +
            "import java.lang.reflect.Method;" + rt +
            "public class TankTimeProxy implements    " +inface.getName()+ "{"+ rt +
    
            "   cn.asto.proxy.InvocationHandler h;" + rt +
                
                
            "    public TankTimeProxy(InvocationHandler h) {"+ rt +
            "        super();"+ rt +
            "        this.h = h;"+ rt +
            "}"+ rt +
    
                methodStr+
    
            "}";
            String fileName =System.getProperty("user.dir")+"/src/cn/asto/proxy/TankTimeProxy.java";
            FileWriter fw = new FileWriter(fileName);
            fw.write(str);
            fw.flush();
            fw.close();
            //compile
            JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
            StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
            Iterable units = fileMgr.getJavaFileObjects(fileName);
            CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units);
            t.call();
            fileMgr.close();
            
            //load into memory and create an instance
            URL[] urls = new URL[]{(new URL("file:/"+System.getProperty("user.dir")+"/src/"))};
            URLClassLoader ul = new URLClassLoader(urls);
            Class c = ul.loadClass("cn.asto.proxy.TankTimeProxy");
            System.out.println(c);
            Constructor ctr = c.getConstructor(InvocationHandler.class);
            Object m = (Object)ctr.newInstance(h);
            return m;
        }
    }

    Test1:

    package cn.asto.compiler.test;
    
    import cn.asto.proxy.InvocationHandler;
    import cn.asto.proxy.Moveable;
    import cn.asto.proxy.Proxy;
    import cn.asto.proxy.Tank;
    import cn.asto.proxy.TimeHandler;
    
    public class Test1 {
    
        public static void main(String args[]) throws Exception{
            Tank t = new Tank();
            InvocationHandler h = new TimeHandler(t);
            Moveable m = (Moveable)Proxy.getProxyInstance(Moveable.class, h);
            m.move();
        }
    }

    大概思路就是将代理添加的逻辑代码和基类抽象出来,我们都知道所有的代理类(以及基类实现一个接口),并且为了提高逻辑代码的重用性,我们将这两部分抽象成InvocationHandler和XXX.class.(xxx表示代理和基类实现的接口,这里是指Moveable),

    写一个TimeHandler类,传入一个代理或者基类,实现InvocationHandler接口,将实现类和xxx.class作为Proxy的构造参数传入,在Proxy大概做那么几件事情,动态生成代理类文件(.java),编译,将InvocationHandler的实现类传入到动态代理文件的构造函数中,构造代理类。(这里有一个回调的过程),让动态代理类去回调InvocationHandler的实现类中的具体逻辑代码(invoke方法),而invoke调用的就是传给InvocationHandler实现类的构造参数类的需要实现逻辑代码添加的方法的基础上,加上逻辑代码。

     (总代理生成动态代理类,回调handler,handler回调move)

    现在让我们来做一个实验,验证动态代理类可以实现代理任意的对象和任意的逻辑方法体(InvocationHandler)

    新建一个User接口:

    package cn.asto.proxy.user;
    
    public interface UserMgr {
    
        public void addUser();
    }

    建一个User类:

    package cn.asto.proxy.user;
    
    public class User implements UserMgr{
    
        @Override
        public void addUser() {
            System.out.println("添加用户");
            
        }
    
        
        
    }

    在建一个LoginHandler

    package cn.asto.proxy.user;
    
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    
    import cn.asto.proxy.InvocationHandler;
    
    public class LoginHandler implements InvocationHandler{
    
        private Object target;
        
        public LoginHandler(Object target) {
            super();
            this.target = target;
        }
    
        @Override
        public void invoke(Object o, Method m) {
            System.out.println("登录成功");
            try {
                m.invoke(target, null);
            } catch (IllegalArgumentException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    
    }

    在Client中进行调用:

    package cn.asto.proxy.user;
    
    import cn.asto.proxy.Proxy;
    
    public class Client {
    
        public static void main(String args[]){
            User u = new User();
            LoginHandler l = new LoginHandler(u);
            try {
                UserMgr ur = (UserMgr)Proxy.getProxyInstance(UserMgr.class, l);
                ur.addUser();
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        
    }

    打印输出:


    登录成功
    添加用户

    测试完成!

    好让我来看看jdk的代理程序:

    package cn.asto.jdk;
    
    import java.lang.reflect.Proxy;
    
    import cn.asto.proxy.user.UserMgr;
    
    
    
    public class Client {
        public static void main(String args[]){
            User u = new User();
            LoginHandler l = new LoginHandler(u);
            ClassLoader c = ClassLoader.getSystemClassLoader();
            try {
                UserMgr ur = (UserMgr)Proxy.newProxyInstance(c,new Class[]{UserMgr.class}, l);
                ur.addUser();
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
    package cn.asto.jdk;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    
    public class LoginHandler implements InvocationHandler {
    
    
        private Object target;
        
        public LoginHandler(Object target) {
            super();
            this.target = target;
        }
    
        
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args)
                throws Throwable {
            System.out.println("登录成功");
            try {
                method.invoke(target, null);
            } catch (IllegalArgumentException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            return null;
        }
    }
    package cn.asto.jdk;
    
    
    
    import cn.asto.proxy.user.UserMgr;
    
    public class User implements UserMgr{
    
        @Override
        public void addUser() {
            System.out.println("添加用户");
            
        }
    
        
        
    }
    package cn.asto.proxy.user;
    
    public interface UserMgr {
    
        public void addUser();
    }

    不用看了,基本和我实现得差不多。只不过换成了jdk的InvocationHandler和jdk的Proxy.newProxyInstance

    总结:代理可以完成代码块的插拔叠加!什么意思呢?spring中aop不就是这样子吗?    拦截器? 登录校验?权限控制?

  • 相关阅读:
    异步加载JS
    解决Vue刷新一瞬间出现样式未加载完或者出现Vue代码问题
    什么是NaN?它的类型是什么?如何可靠的测试一个值是否等于NaN?
    JS快速获取图片宽高的方法
    为什么操作dom会消耗性能
    localstorage的浏览器支持情况
    mongodb Windows系统下安装卡死问题
    正则表达式
    CSS Hack
    css中cursor(光标类型)
  • 原文地址:https://www.cnblogs.com/think-in-java/p/4739014.html
Copyright © 2020-2023  润新知