转载

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

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

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

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

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

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

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实现类的构造参数类的需要实现逻辑代码添加的方法的基础上,加上逻辑代码。

现在让我们来做一个实验,验证动态代理类可以实现代理任意的对象和任意的逻辑方法体(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不就是这样子吗?    拦截器? 登录校验?权限控制?

正文到此结束
Loading...