设计模式之代理模式(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 ="\r\t";
        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不就是这样子吗?    拦截器? 登录校验?权限控制?

 

posted @ 2015-08-18 11:49  陈其苗  阅读(773)  评论(0编辑  收藏  举报