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

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 = "\r\n";
        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 = "\r\n";
        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 = "\r\n";
        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  https://files.cnblogs.com/files/tenWood/Proxy.rar

 

 
 
 
 

 

posted on 2017-04-26 01:30  有点懒惰的大青年  阅读(594)  评论(0编辑  收藏  举报