• 马士兵设计模式-动态代理


    1.class Tank实现Moveable接口,实现了move()方法,现在在move方法的前后记录这个方法的执行时间:

    com.cy.proxy.Moveable:

    package com.cy.proxy;
    
    public interface Moveable {
        public void move();
    }
    View Code

    com.cy.proxy.Tank:

    package com.cy.proxy;
    
    import java.util.Random;
    
    public class Tank implements Moveable{
    
        @Override
        public void move() {
            long start = System.currentTimeMillis();
            System.out.println("Tank Moving...");
            try {
                Thread.sleep(new Random().nextInt(10000));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            long end = System.currentTimeMillis();
            System.out.println("time: " + (end-start));
        }
    
    }
    View Code

    2.但是现在假如不知道Tank类的源码,不能再其中的move()方法中直接添加代码的情况下,怎么知道move方法的执行时间?

    1)Tank2继承Tank,重写其中的move()方法,在调用super.move()方法的前后添加逻辑,来记录时间:

    package com.cy.proxy;
    
    public class Tank2 extends Tank{
    
        @Override
        public void move() {
            long start = System.currentTimeMillis();
            super.move();
            long end = System.currentTimeMillis();
            System.out.println("time: " + (end-start));
        }
        
    }

    2)聚合,实现接口的方式在方法的前后记录时间,Tank3也实现Moveable接口,保存Tank类,每次调用Tank的move方法,在其前后加入逻辑:

    package com.cy.proxy;
    
    public class Tank3 implements Moveable{
        private Tank t;
        
        public Tank3(Tank t) {
            super();
            this.t = t;
        }
    
    
        @Override
        public void move() {
            long start = System.currentTimeMillis();
            t.move();
            long end = System.currentTimeMillis();
            System.out.println("time: " + (end-start));
        }
        
    }

    1、2两种方式都实现了对class Tank中move()方法的代理,哪种方法好呢?---聚合的方式好些,继承的方式不灵活;

    为什么?

    比如还有个记录Tank move方法的日志代理:TankLogProxy:

    package com.cy.proxy;
    
    public class TankLogProxy implements Moveable{
        private Moveable t;
        
        public TankLogProxy(Moveable t) {
            super();
            this.t = t;
        }
    
    
        @Override
        public void move() {
            System.out.println("Tank Start...");
            t.move();
            System.out.println("Tank Stop.");
        }
        
    }
    现在考虑功能上的叠加:先记录日志,在记录时间:
    如果用继承方式的话,Tank2的代码就要改动了;或者弄个Tank4 extends Tank2;要实现的代理功能更多的话,类就会无限制的增加下去;而且各个功能顺序可能不一样,比如先记录时间,再记录日志;
    聚合的方式更容易实现:
    现在TankTimeProxy和TankLogProxy都是一个Moveable类型的对象,两个之间可以互相代理;
    TankTimeProxy:
    package com.cy.proxy;
    
    public class TankTimeProxy implements Moveable{
        private Moveable t;
        
        public TankTimeProxy(Moveable t) {
            super();
            this.t = t;
        }
    
    
        @Override
        public void move() {
            long start = System.currentTimeMillis();
            t.move();
            long end = System.currentTimeMillis();
            System.out.println("time: " + (end-start));
        }
        
    }
    View Code

    测试代码:

    package com.cy.proxy;
    
    public class Client {
    
        public static void main(String[] args) {
            //日志代理,记录tank.move()执行时间
            TankTimeProxy ttp = new TankTimeProxy(new Tank());
            ttp.move();    
        }
    
    }
    
    Tank Moving...
    time: 967
    View Code
    package com.cy.proxy;
    
    public class Client {
    
        public static void main(String[] args) {
            
            //在时间外面加一层日志
            TankTimeProxy ttp = new TankTimeProxy(new Tank());
            TankLogProxy tlp = new TankLogProxy(ttp);
            tlp.move();
            
        }
    
    }
    
    Tank Start...
    Tank Moving...
    time: 3631
    Tank Stop.
    View Code

    TankTimeProxy、TankLogProxy、Tank都实现了Moveable接口,TankTimeProxy和TankLogProxy之间可以互相代理;

    -----------------------------------------------------------------------------------------------------------------------------------------
    以上的部分都还是静态代理,还不够强大;
    比如Moveable中还有个方法:void stop();
    class Tank势必会实现 stop()方法;
    现在TankTimeProxy中也要记录stop()方法执行的时间,那么记录时间的代码:
    long start = System.currentTimeMillis();
    long end = System.currentTimeMillis();
    也要在stop方法中重写一遍;
     
    就像这样:
    package com.cy.proxy;
    
    public interface Moveable {
        public void move();
        public void stop();
    }
    View Code
    package com.cy.proxy;
    
    import java.util.Random;
    
    public class Tank implements Moveable{
    
        @Override
        public void move() {
            System.out.println("Tank Moving...");
            try {
                Thread.sleep(new Random().nextInt(10000));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        @Override
        public void stop() {
            System.out.println("Tank Stopping...");
        }
    
    }
    View Code
    package com.cy.proxy;
    
    public class TankTimeProxy implements Moveable{
        private Moveable t;
        
        public TankTimeProxy(Moveable t) {
            super();
            this.t = t;
        }
    
    
        @Override
        public void move() {
            long start = System.currentTimeMillis();
            System.out.println("starttime: " + start);
            t.move();
            long end = System.currentTimeMillis();
            System.out.println("time: " + (end-start));
        }
    
    
        @Override
        public void stop() {
            long start = System.currentTimeMillis();
            System.out.println("starttime: " + start);
            t.stop();
            long end = System.currentTimeMillis();
            System.out.println("time: " + (end-start));
            
        }
        
    }
    View Code
    会发现TankTimeProxy中move()和stop()中两段代码几乎一样;于是发现将他们封装起来:
    比如beforeMethod(); afterMethod();
    貌似这些代码可以重用了。
     
    现在考虑这个问题:
    TankTimeProxy不叫TankTimeProxy了,而是叫TimeProxy,可以把任何一个对象当做被代理对象,可以计算这个对象里面的任意一个方法的运行时间,怎么做?
    原始的这种方法肯定不行了,因为假如一个系统里面有100个类,要知道这100个类里面的方法运行了多长时间,那么就要为这100个类写代理对象,显然太不行了。
     
    思路:
    写个通用的时间代理类,可以对任意的对象进行代理,对象里面的任意方法都可以重写,前面加上start,后面加上end来计算时间;
     
    1)我们假设被代理的对象都实现了某个接口,真正代理的时候是根据这个接口来生成代理对象的;
    怎么样生成这个代理呢?
     
    2)假设com.cy.proxy.Proxy能够产生一个代理类:
    Proxy.java:
    package com.cy.proxy;
    
    public class Proxy {
        
        //用来产生新的代理类
        public static Object newProxyInstance(){
            String src = 
            "package com.cy.proxy;" +
            "public class TankTimeProxy implements Moveable{" +
                "private Moveable t;" +
                
                "public TankTimeProxy(Moveable t) {" +
                    "super();" +
                    "this.t = t;" +
                "}" +
     
                "@Override" +
                "public void move() {" +
                    "long start = System.currentTimeMillis();" +
                    "System.out.println("starttime: " + start);" +
                    "t.move();" +
                    "long end = System.currentTimeMillis();"+
                    "System.out.println("time: " + (end-start));" +
                "}" +
            "}";
            
            return null;
        }
        
        
    }
    View Code
    String src声明了一段源码,假设这段源码能够编译完,产生一个新的类,再把这个类load到内存,再由这个类产生新的对象,这个对象就是实现了TimeProxy逻辑的对象;
    问题就变成了,如果我们能把这段代码动态的编译,那么原来的TankTimeProxy这个类就不需要了;这个类的名字不重要;
    调用Proxy.newProxyInstance()方法就能返回一个具体的代理对象;
    如何动态的编译这段String src的源码呢?有这些:
    JDK6 Complier API, CGLib, ASM;
     
    3)现在解决编译这段String src源代码的问题:
    下面测试程序是来编译string src这段源码的:
    com.cy.compiler.test.Test1.java:
    package com.cy.compiler.test;
    
    import java.io.File;
    import java.io.FileWriter;
    import java.lang.reflect.Constructor;
    import java.net.URL;
    import java.net.URLClassLoader;
    
    import javax.tools.JavaCompiler;
    import javax.tools.JavaCompiler.CompilationTask;
    import javax.tools.JavaFileObject;
    import javax.tools.StandardJavaFileManager;
    import javax.tools.ToolProvider;
    
    import com.cy.proxy.Moveable;
    import com.cy.proxy.Tank;
    
    public class Test1 {
        public static void main(String[] args) throws Exception{
            String rt = "
    ";
            String src = 
            "package com.cy.proxy;" + rt +
            "public class TankTimeProxy implements Moveable{" + rt +
            "    private Moveable t;" + rt +
                
            "    public TankTimeProxy(Moveable t) {" + rt +
            "        super();" + rt +
            "        this.t = t;" + rt +
            "     }" + rt +
     
            "    @Override" + rt +
            "    public void move() {" + rt +
            "        long start = System.currentTimeMillis();" + rt +
            "        System.out.println("starttime: " + start);" + rt +
            "         t.move();" + rt +
            "        long end = System.currentTimeMillis();"+ rt +
            "        System.out.println("time: " + (end-start));" + rt +
            "    }" + rt +
            "}";
            
            
            //先把源代码写到下面:
            String fileName = System.getProperty("user.dir")+"/src/com/cy/proxy/TankTimeProxy.java";
            //user.dir:  当前项目的根路径
            //E:/jdbcWorkspace/Proxy/src/com/cy/proxy/TankTimeProxy.java
            File f = new File(fileName);
            FileWriter fw = new FileWriter(f);
            fw.write(src);
            fw.flush();
            fw.close();
            
            //在程序中编译这段代码:
            //JavaCompiler java的编译器
            //ToolProvider.getSystemJavaCompiler() 拿到系统当前默认的java编译器,其实就是javac
            JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
            //System.out.println(compiler.getClass().getName());    //com.sun.tools.javac.api.JavacTool
            StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);    //通过fileMgr管理要编译的文件
            Iterable<? extends JavaFileObject> units = fileMgr.getJavaFileObjects(fileName);        //拿到编译的内容
            CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units);            //编译的任务
            t.call();                                                                                //编译
            fileMgr.close();
            
            //生成了这个TankTimeProxy.class之后,将其load到内存,并且生成一个对象
            URL[] urls = new URL[] {new URL("file:/" + System.getProperty("user.dir") + "/src")};
            URLClassLoader ul = new URLClassLoader(urls);
            Class c = ul.loadClass("com.cy.proxy.TankTimeProxy");    //class com.cy.proxy.TankTimeProxy
            //c.newInstance() 调用的是类TankTimeProxy参数为空的构造方法,但是类中没有此构造方法
            //站在java虚拟机的角度,每一个类,每一个类里面的方法都是一个对象;
            Constructor ctr = c.getConstructor(Moveable.class);
            Moveable m = (Moveable)ctr.newInstance(new Tank());
            m.move();
        }
    }
    
    
    打印结果如下:
    starttime: 1493223076248
    Tank Moving...
    time: 861
    View Code

    Proxy中就可以通过Proxy.newProxyInstance()来返回一个代理对象了;

    4)现在确实能产生一个动态的代理了,但是现在产生的代理只能够代理实现了Moveable接口的这样的一种代理;如果是实现了别的接口的就不行了,现在考虑能生成实现任意接口的代理类:

    要做的事情:
    1.在Proxy.newProxyInstance()方法中添加参数,Proxy.newProxyInstance(Class infce),将要实现的接口类型传进去,src源码中动态implements这个接口;
    2.要知道infce中有多少个方法,src中就要动态实现这些方法,在这些方法中添加逻辑;
     
    下面是代码实现:(没有模拟方法的参数和返回值;)
    a.测试程序,利用反射,获取接口中的方法:
    package com.cy.compiler.test;
    
    import java.lang.reflect.Method;
    
    public class Test2 {
    
        public static void main(String[] args) {
            //通过反射可以获取接口中有多少个方法
            Method[] methods = com.cy.proxy.Moveable.class.getMethods();
            for(Method m : methods){
                System.out.println(m);    //public abstract void com.cy.proxy.Moveable.move()
            }
        }
    
    }
    View Code

    b.代码实现,代理实现任意接口的类:

    Proxy.java: newProxyInstance(Class infce):

    package com.cy.proxy;
    
    import java.io.File;
    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.JavaFileObject;
    import javax.tools.StandardJavaFileManager;
    import javax.tools.ToolProvider;
    import javax.tools.JavaCompiler.CompilationTask;
    
    public class Proxy {
        
        //用来产生新的代理类
        public static Object newProxyInstance(Class infce) throws Exception{
            String rt = "
    ";
            String methodStr = "";
            
            Method[] methods = infce.getMethods();
            for(Method m : methods){
                methodStr +=
                "    @Override" + rt +
                "    public void " + m.getName() + "() {" + rt +
                "        long start = System.currentTimeMillis();" + rt +
                "         System.out.println("starttime: " + start);" + rt +
                "         t." + m.getName() + "();" + rt +
                "        long end = System.currentTimeMillis();"+ rt +
                "        System.out.println("time: " + (end-start));" + rt +
                "    }" + rt;
            }
            
            String src = 
            "package com.cy.proxy;" + rt +
            "public class TankTimeProxy implements "+infce.getName()+"{" + rt +
            "    private Moveable t;" + rt +
                
            "    public TankTimeProxy(Moveable t) {" + rt +
            "        super();" + rt +
            "        this.t = t;" + rt +
            "     }" + rt +
     
                 methodStr +
                 
            "}";
            
            //先把源代码写到下面:
            String fileName = "d:/src/com/cy/proxy/TankTimeProxy.java";
            File f = new File(fileName);
            FileWriter fw = new FileWriter(f);
            fw.write(src);
            fw.flush();
            fw.close();
            
            //在程序中编译这段代码:
            //JavaCompiler java的编译器
            //ToolProvider.getSystemJavaCompiler() 拿到系统当前默认的java编译器,其实就是javac
            JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
            //System.out.println(compiler.getClass().getName());    //com.sun.tools.javac.api.JavacTool
            StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);    //通过fileMgr管理要编译的文件
            Iterable<? extends JavaFileObject> units = fileMgr.getJavaFileObjects(fileName);        //拿到编译的内容
            CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units);            //编译的任务
            t.call();                                                                                //编译
            fileMgr.close();
            
            //生成了这个TankTimeProxy.class之后,将其load到内存,并且生成一个对象
            URL[] urls = new URL[] {new URL("file:/" + "d:/src/")};
            URLClassLoader ul = new URLClassLoader(urls);
            Class c = ul.loadClass("com.cy.proxy.TankTimeProxy");    //class com.cy.proxy.TankTimeProxy
            //c.newInstance() 调用的是类TankTimeProxy参数为空的构造方法,但是类中没有此构造方法
            //站在java虚拟机的角度,每一个类,每一个类里面的方法都是一个对象;
            Constructor ctr = c.getConstructor(infce);
            Object m = ctr.newInstance(new Tank());
            
            return m;
        }
        
    }
    View Code

    //上面的代码中修改了生成源码文件、编译class文件的存放位置;

    //利用反射,动态生成实现接口方法的字符串;为了简单起见,没有模拟方法的参数和返回值;

    c.测试代码:

    package com.cy.proxy;
    
    public class Client {
    
        public static void main(String[] args) throws Exception{
            
            Moveable m = (Moveable) Proxy.newProxyInstance(Moveable.class);
            m.move();
            
        }
    
    }
    
    starttime: 1493228074841
    Tank Moving...
    time: 5836
    View Code

     5)我们现在的Proxy能够对实现任意接口的类进行代理了,但是上面的Proxy中的生成逻辑的代码是固定的,只能是记录时间的代码,如果要生成一个记录日志的代理/权限的代理,就不行了;

    现在怎么让这段内容:
    long start = System.currentTimeMillis();
    long end = System.currentTimeMillis();
    ...
    也能让用户灵活的指定呢?
    现在最好是这段代码能够调用别人指定的处理方式;现在需要一个能够调用别人指定的处理方法的东西;
     
    InvocationHandler.java:
    package com.cy.proxy;
    
    import java.lang.reflect.Method;
    
    public interface InvocationHandler {
        public void invoke(Object o, Method m);    //假设返回值void
    }
    View Code
    InvocationHandler:定义了一个接口,这个接口可以对方法进行处理;处理的方式由子类去实现;
     
     TimeHandler.java:
    package com.cy.proxy;
    
    import java.lang.reflect.Method;
    
    public class TimeHandler implements InvocationHandler{
    
        @Override
        public void invoke(Object o, Method m) {
            long start = System.currentTimeMillis();
            System.out.println("starttime: " + start);
            
            try {
                m.invoke(o, new Object[]{});    //假设方法中没有参数
            } catch (Exception e) {
                e.printStackTrace();
            }    
            
            long end = System.currentTimeMillis();
            System.out.println("time: " + (end-start));
        }
    
    }
    
    我们可以对一个任意的方法进行自定义的处理;现在只要在调用给定的方法前后加上记录时间的逻辑就行了;
    Method m:  想要调用的那个方法;在调用这个方法的之前或者之后做一些事情;
    方法的调用:对一个方法的调用,我们必须要知道这个方法的当前对象是谁,也就是this是谁;
    Object o:  对那个对象调用的m方法;即对o这个对象调用m方法;
    View Code

    现在考虑Proxy中动态的代码该怎么生成了:

    Proxy.java:

    package com.cy.proxy;
    
    import java.io.File;
    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.JavaFileObject;
    import javax.tools.StandardJavaFileManager;
    import javax.tools.ToolProvider;
    import javax.tools.JavaCompiler.CompilationTask;
    
    public class Proxy {
        
        /**
         * 用来产生新的代理类
         * infce                    产生哪个接口的动态代理
         * InvocationHandler        要实现什么样的代理,对接口中的方法前后进行什么样的处理,记录时间or...
         */
        public static Object newProxyInstance(Class infce, InvocationHandler h) throws Exception{
            String rt = "
    ";
            String methodStr = "";
            
            Method[] methods = infce.getMethods();
            
            for(Method m : methods){
                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 +
                "    }" + rt;
            }
            
            String src = 
            "package com.cy.proxy;" + rt +
            "import java.lang.reflect.Method;" + rt +
            "public class TankTimeProxy implements "+infce.getName()+"{" + rt +
            "    public TankTimeProxy(InvocationHandler h) {" + rt +
            "        this.h = h;" + rt +
            "     }" + rt +
            "    com.cy.proxy.InvocationHandler h;" + rt +
            
                 methodStr +
                 
            "}";
            
            //先把源代码写到下面:
            String fileName = "d:/src/com/cy/proxy/TankTimeProxy.java";
            File f = new File(fileName);
            FileWriter fw = new FileWriter(f);
            fw.write(src);
            fw.flush();
            fw.close();
            
            //在程序中编译这段代码:
            //JavaCompiler java的编译器
            //ToolProvider.getSystemJavaCompiler() 拿到系统当前默认的java编译器,其实就是javac
            JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
            //System.out.println(compiler.getClass().getName());    //com.sun.tools.javac.api.JavacTool
            StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);    //通过fileMgr管理要编译的文件
            Iterable<? extends JavaFileObject> units = fileMgr.getJavaFileObjects(fileName);        //拿到编译的内容
            CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units);            //编译的任务
            t.call();                                                                                //编译
            fileMgr.close();
            
            //生成了这个TankTimeProxy.class之后,将其load到内存,并且生成一个对象
            URL[] urls = new URL[] {new URL("file:/" + "d:/src/")};
            URLClassLoader ul = new URLClassLoader(urls);
            Class c = ul.loadClass("com.cy.proxy.TankTimeProxy");    //class com.cy.proxy.TankTimeProxy
            //c.newInstance() 调用的是类TankTimeProxy参数为空的构造方法,但是类中没有此构造方法
            //站在java虚拟机的角度,每一个类,每一个类里面的方法都是一个对象;
            Constructor ctr = c.getConstructor(InvocationHandler.class);
            Object m = ctr.newInstance(h);
            
            return m;
        }
        
    }

    TimeHandler.java:

    package com.cy.proxy;
    
    import java.lang.reflect.Method;
    
    public class TimeHandler implements InvocationHandler{
        private Object target;                //被代理对象
        
        public TimeHandler(Object target){
            this.target = target;
        }
        
        /**
         * Object o这里没用到;
         * Proxy中的h.invoke(this, md)其实this没用到,this的指向是代理对象 可以print看下:    com.cy.proxy.TankTimeProxy
         */
        @Override
        public void invoke(Object o, Method m) {
            long start = System.currentTimeMillis();
            System.out.println("starttime: " + start);
            System.out.println(o.getClass().getName());     //com.cy.proxy.TankTimeProxy
            
            try {
                m.invoke(target);                         //对被代理对象调用m方法
            } catch (Exception e) {
                e.printStackTrace();
            }    
            
            long end = System.currentTimeMillis();
            System.out.println("time: " + (end-start));
        }
    
    }

    Client.java测试代码:

    package com.cy.proxy;
    
    public class Client {
    
        public static void main(String[] args) throws Exception{
            Tank t = new Tank();                         //被代理对象
            InvocationHandler h = new TimeHandler(t);    //代理逻辑,这里是记录时间的逻辑
            
            //Proxy.newProxyInstance(Moveable.class, h)返回一个代理对象,我们不知道名字
            Moveable m = (Moveable) Proxy.newProxyInstance(Moveable.class, h);
            m.move();
            
        }
    }

    看下D:src下面生成的代理对象:                                                             

    package com.cy.proxy;
    import java.lang.reflect.Method;
    public class TankTimeProxy implements com.cy.proxy.Moveable{
        public TankTimeProxy(InvocationHandler h) {
            this.h = h;
         }
        com.cy.proxy.InvocationHandler h;
        @Override
        public void move() {
            try{
              Method md = com.cy.proxy.Moveable.class.getMethod("move");
               h.invoke(this, md);
            }catch(Exception e){e.printStackTrace();}
        }
    }

    5.JDK中的动态代理,和上面模拟的代码几乎一样:

    Proxy:

    ClassLoader: 当你产生这个动态代理的时候,用哪个classLoader把它load进来;上面模拟是用的URLClassLoader;
                       真正调的时候,可以用当前这个类的ClassLoader;如果用传过来的ClassLoader,得保证生成代理的类生
                       成在特定的目录里面,才load进来;
     
     
     
    java.lang.reflect.InvocationHandler:

     

    proxy和method和上面代码模拟的一样;args:是method的参数;

    6)使用JDK里面的Proxy、InvocationHandler写的动态代理的例子:                                            

    需求:在userDao的save、delete方法的前后加上日志记录:

    UserDao.java:

    package com.cy.aop;
    
    public interface UserDao {
        public int save(String user);
        public boolean delete(String user);
    }
    View Code

    UserDaoImpl.java:

    package com.cy.aop;
    
    public class UserDaoImpl implements UserDao {
    
        @Override
        public int save(String user) {
            System.out.println("add User: " + user);
            return 1;
        }
    
        @Override
        public boolean delete(String user) {
            System.out.println("delete User: " + user);
            return true;
        }
        
    }

    UserDaoLogHanler.java,在方法的前后加上记录日志的逻辑:

    package com.cy.aop;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    
    public class UserDaoLogHanler implements InvocationHandler {
        
        //target是被代理对象
        private Object target;
        
        public UserDaoLogHanler(Object target){
            this.target = target;
        }
        
        /**
         * 调用被代理对象实现接口UserDao的每个方法,save,delete都会调用invoke方法;
         * 
         * 1.先加自己的业务逻辑
         * 2.再调用被代理对象的对应方法;
         * 3.再加自己的业务逻辑也行。。
         */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println(method.getName()+"----log start...");
            
            /**
             * obj是target调用method的返回值   也就是public int save(String)方法的返回值int
             * method: 是要调用的被代理对象的方法;
             * args:   是method方法的参数
             */
            Object obj = method.invoke(target, args);    
            
            System.out.println(method.getName()+"----log end.");
            
            return obj;        //执行proxy.save,obj是1;执行proxy.delete,obj是true
        }
    
    }

    UserService.java 测试代码:

    package com.cy.aop;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Proxy;
    
    public class UserService {
        /**
         * 1.构建被代理对象;userDao;
         * 2.把被代理对象交给handler;
         * 3.使用newProxyInstance产生代理对象;其中的参数:
         *       1)classloader: 必须和被代理对象使用同一个classloader;
         *       2)被代理对象使用的接口;jdk生成的代理对象会使用同样的接口;
         *       3)代理对象调用方法的时候是由handler来处理;
         */
        public static void main(String[] args) {
            UserDao userDao = new UserDaoImpl();
            InvocationHandler h = new UserDaoLogHanler(userDao);
            UserDao proxy = (UserDao) Proxy.newProxyInstance(userDao.getClass().getClassLoader(), userDao.getClass().getInterfaces(), h);
            proxy.save("小明");
            proxy.delete("小明");
        }
        
        /**
         * JDK中要给一个类实现动态代理:
         * 1.这个类必须实现一个接口,没有实现接口的类,JDK是产生不了动态代理的;
         */
        
    }

    console:

    整个源码会上传到文件:Proxy.zip  http://files.cnblogs.com/files/tenWood/Proxy.rar

     
     
     
     
  • 相关阅读:
    模块化工具require 学习笔记
    学习Jade模板引擎
    通过border来实现各种三角符号
    使用vscode 编译 sass
    Javascript 运行机制
    Vue调试工具 vue-devtools
    MVVM框架
    通信类
    面向对象
    原型和原型链
  • 原文地址:https://www.cnblogs.com/tenWood/p/6766288.html
Copyright © 2020-2023  润新知