Bootstrap

aop源码解析辅助-jdk动态代理

相信读者对jdk动态代理多少有点了解 但可能有些读者并不明白动态代理的实现 这样给人的感觉就是懵懵懂懂 说也说不清 但也好像是知道这么回事 那么这篇博客的目的就是解决读者的这个困局的
既然是叫动态代理 那么说明有很多事情是瞒着程序员做的 相应的就有静态代理 这些对使用的程序员来说是透明的 像spring框架一样 里面做了太多太多的东西 但对使用者来说就是透明的

博主也是看了一个视频教程理解的 感谢大神的无私奉献 代码也是出自大神之手 视频教程传送门

其实jdk动态代理可以这样理解 通过实现目标类的接口实现一个目标类的’兄弟’类 这个’兄弟’类持有这个目标类 在用到目标类的地方都用’兄弟’类代替 因为实现的是相同的接口 那么有相同的接口方法 当调用一个接口方法的时候 其实调用的’兄弟’类的方法 而’兄弟’类持有目标对象 ‘兄弟’类可以在目标类的方法执行前后做一些额外的工作
同样的对于cglib动态代理来说 不过是生成了一个目标类的子类 而不是生成目标类的’兄弟’类

我们以下面的类与接口为例来实现我们自己的静态代理与动态代理


public interface Moveable {
    void move();
}

public class Car implements Moveable {

    @Override
    public void move() {
        //实现开车
        try {
            Thread.sleep(new Random().nextInt(1000));
            System.out.println("汽车行驶中....");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

静态代理

用组合的方式实现代理

public class CarTimeProxy implements Moveable {

    public CarTimeProxy(Moveable m) {
        super();
        this.m = m;
    }

    private Moveable m;

    @Override
    public void move() {
        long starttime = System.currentTimeMillis();
        System.out.println("汽车开始行驶....");
        m.move();
        long endtime = System.currentTimeMillis();
        System.out.println("汽车结束行驶....  汽车行驶时间:" 
                + (endtime - starttime) + "毫秒!");
    }

}

public class Test {

    public static void main(String[] args) {
        Car car = new Car();
        Moveable m = new CarTimeProxy(car);
        m.move();
    }
}

这样就对目标对象实现了代理 在汽车行驶的前后加入了我们自己代码
当然这样的缺点很明显 被代理的方法要实现自某一个接口方法 只能代理这么一个car对象 如果以后我们需要对bicycle对象做代理 我们该怎么办呢 我们还是需要重新写一份这样的代码 虽然代理前后的功能都不变

jdk动态代理

显然对于这样的问题 jdk早已提供了相应的类供我们使用 直接上代码

public class TimeHandler implements InvocationHandler {

    public TimeHandler(Object target) {
        super();
        this.target = target;
    }

    private Object target;


    @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;
    }
}

InvocationHandler就是jdk提供的类 我们需要把目标对象传递给InvocationHandler invoke方法的第一个参数proxy代表的是代理对象 method就是被代理的方法 args就是被代理方法的参数

public class Test {
    /**
     * JDK动态代理测试类
     */
    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();
    }
}

运行的结果跟我们前面静态代理的结果是一样的 如果我们需要给bicycyle也做这样一个代理呢 那么我们只需要把bicycyle对象传递进去就可以了 不再需要重写一遍代理类

动态代理的实现

那么问题来了 他是怎么实现的呢? 其实就是通过实现目标类的接口生成目标类的’兄弟’类 这个’兄弟’类持有目标类 就像说的那样 我们自己实现一遍
直接上代码

public interface InvocationHandler {

    public void invoke(Object o,Method m);
}

public class TimeHandler implements InvocationHandler {

    private Object target;

    public TimeHandler(Object target) {
        super();
        this.target = target;
    }

    @Override
    public void invoke(Object o, Method m) {

        try {
            long starttime = System.currentTimeMillis();
            System.out.println("汽车开始行驶....");
            m.invoke(target);
            long endtime = System.currentTimeMillis();
            System.out.println("汽车结束行驶....  汽车行驶时间:"
                            + (endtime - starttime) + "毫秒!");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}


public class Proxy {

    @SuppressWarnings("unchecked")
    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 com.sunms.proxy;" + rt +
        "import java.lang.reflect.Method;" + rt +
        "import com.sunms.proxy.InvocationHandler;" +  rt+
        "public class $Proxy0 implements " + infce.getName() + " {" + rt +
        "   public $Proxy0(InvocationHandler h) {" + rt +
        "       this.h = h;" + rt +
        "   }" + rt +
        "  private InvocationHandler h;" + rt+ 
        methodStr + rt +
        "}" ;
        //产生代理类的java文件
        String filename = "/Users/sunms/workspace/tf8-trunk/3-3代码/Proxy/src/com/sunms/proxy/" + "$Proxy0.java";

        File file = new File(filename);
        FileUtils.writeStringToFile(file, str);

        //编译
        //拿到编译器
        JavaCompiler complier = ToolProvider.getSystemJavaCompiler();
        //文件管理者
        StandardJavaFileManager fileMgr = 
                complier.getStandardFileManager(null, null, null);
        //获取文件
        Iterable units = fileMgr.getJavaFileObjects(filename);
        //编译任务
        CompilationTask t = complier.getTask(null, fileMgr, null, null, null, units);
        //进行编译
        t.call();
        fileMgr.close();

        //load 到内存
        ClassLoader cl = ClassLoader.getSystemClassLoader();
        Class c = cl.loadClass("com.sunms.proxy.$Proxy0");

        Constructor ctr = c.getConstructor(InvocationHandler.class);
        return ctr.newInstance(h);
    }

TimeHandler跟上一节的代码是一样的 这里主要解读下Proxy的实现 (这里的实现考虑的比较简单 没有考虑多借口 方法参数等问题) 大概的过程就是通过代码生成一个实现目标类接口的’兄弟’类 我们看一下生成的代理类

public class $Proxy0 implements com.sunms.proxy.Moveable {
    public $Proxy0(InvocationHandler h) {
        this.h = h;
    }

    private InvocationHandler h;

    @Override
    public void move() {
        try {
            Method md = com.sunms.proxy.Moveable.class.getMethod("move");
            h.invoke(this, md);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

$Proxy0就是自动生成的’car’的代理类 他们有相同的接口方法move 再看一下调用

public class Client {

    /**
     * 测试类
     * @throws Exception 
     */
    public static void main(String[] args) throws Exception {
        Car car = new Car();
        InvocationHandler h = new TimeHandler(car);
        Moveable m = (Moveable)Proxy.newProxyInstance(Moveable.class,h);
        m.move();
    }
}

m对象代表的就是代理类 与目标对象实现相同的接口方法 m.move()调用的就是$Proxy0##move()方法 $Proxy0##move()方法调用invocationHandler.invoke()方法 invoke方法中除了调用目标对象的接口方法外 还做了一些其他的额外的动作 这就是jdk动态代理

;