【引用】模拟JDK实现动态代理(自写Proxy类和InvocationHandler接口)

来源:互联网 发布:微信双人夺宝源码 编辑:程序博客网 时间:2024/05/23 01:23

这个模拟实现,非常感谢马士兵老师的视频..在此,谢谢他的帮助.

首先明确下动态代理的用处..我们创建代理的用处就是,在不修改被代理对象源代码的情况下,为被代理对象添加一些其他的附属功能..通俗一点说,.我们想给某个方法的前后加一些逻辑,但是,我又不想修改原代码..然后,我们就想到了代理(可以用继承和聚合两种方式实现),但是,静态代理是我们需要手工的创建某个JAVA类,这样的话,很容易造成类膨胀.于是,又有了动态代理..让JDK自动帮我们生成class文件

 

最后,提醒下,注意看代码里的注释..因为都蛮重要的

在上一节中,我是这个需求,,一个接口Store.java,表示所有的商店,里面有一个方法sell()..然后有个具体的实现类Supermarket.java.这个类实现了Store.java接口.其实这个类也是被代理类,我们要做的就是,动态创建一个Supermarket的代理类...我们在代理类中动态的添加事务的代码...需求差不多就是这样了..

//我把动态生成的class文件放在D:\src\com\cjb\proxy下,所以,如果需要运行我下面写的这个小程序,必须先创建这个目录.

1 Store.java 和Supermarket.java

package com.cjb.proxy;
/**
 *   类说明
 *   表示商店
 *   @creator 陈静波
 *   @email
 *   @create-time  May 20, 2010 9:43:40 AM  
 */
public interface Store
{
 public void sell();
}
package com.cjb.proxy;
/**
 *   类说明
 *   @creator 陈静波
 *   @email
 *   @create-time  May 20, 2010 9:54:48 AM  
 */
public class Supermarket implements Store
{
 
 @Override
 public void sell()
 {
  System.out.println("sel in supermarket.....");
 }
 
}

2 首先放上来客户端的调用方法.

可以看到,从客户端的情况来看,基本和我们用JDK的动态代理没什么区别了..
package com.cjb.proxy;
/**
 *   类说明
 *   @creator 陈静波
 *   @email
 *   @create-time  May 20, 2010 4:48:15 PM  
 */
public class Test2
{

 /**
  * @param args
  */
 public static void main(String[] args)
 {
  try
  {
   Supermarket sk = new Supermarket();//创建一个被代理对象..其实也就是真实对象..
   Store s = (Store)Proxy.newInstance(Store.class, new TransactionInvocation(sk));//这个是代理对象,可以看出来,完全是动态创建出来的.我们根本没有创建这个类..
   s.sell();
  } catch (Exception e)
  {
   e.printStackTrace();
  }
 }
}

3 InvocationHandler接口.从字面的意思来看,这个接口是 执行管理者 ..也就是,动态代理里面,方法如何执行通过这个接口来管理..动态代理可以认为是面向切面的编程..,我们对于方法的前后所做的逻辑是可以动态改变的..比如说,我们今天需要在被代理类的方法前后加一个事务管理,但是下次,我们需要加权限管理..为了满足这个需求,所以我们将如何执行(是添加事务管理还是添加权限管理)抽象出一个接口,然后我们去实现这个接口..在上面那个客户端的代码里,我们是 TransactionInvocation ,表示事务管理,如果明天我需要改成权限管理,那我们只需要实现一个 AuthorityInvocation,然后给Proxy.newInstance方法中传入这个参数就OK..而且,TransactionInvocation 和 AuthorityInvocation都是可以复用的..不只是创建Supermarket的动态代理里才可以用这个Handler..

 package com.cjb.proxy;

import java.lang.reflect.Method;

/**
 *   类说明
 *   @creator 陈静波
 *   @email
 *   @create-time  May 20, 2010 3:29:51 PM  
 */
public interface InvocationHandler
{
 /**
  *
  * @param m 这个表示的是被代理的方法.我们可以理解为切入的方法
  *    Invoke方法表示执行方法,m就是指定了执行哪个方法
  */
 void invoke(Method m);
}
4 TransactionInvocation 这个表示事务的管理..在第三条里面可以很清楚的说明了InvocationHandler是干什么用的..

package com.cjb.proxy;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 *   类说明
 *   @creator 陈静波
 *   @email
 *   @create-time  May 20, 2010 3:56:43 PM  
 */
public class TransactionInvocation implements InvocationHandler
{
 Object target;
 
 public TransactionInvocation(Object target)
 {
  super();
  this.target = target;
 }

 @Override
 public void invoke(Method m)
 {
  try
  {
   System.out.println("trasaction begin....");
   m.invoke(target, new Object[]{});//这里必须是target,在JDK提供的invoke方法中,还有一个object
            //这里必须澄清下,我在看马士兵老师说的模拟实现里,他加了一个object类型的参数,表示我们动态产生的
            //代理对象..但是,我在实现以后发现不需要这个参数..
   System.out.println("trasaction commit....");
  } catch (IllegalArgumentException e)
  {
   e.printStackTrace();
  } catch (IllegalAccessException e)
  {
   e.printStackTrace();
  } catch (InvocationTargetException e)
  {
   e.printStackTrace();
  }
 }

}

5 最后就是最重要的Proxy类..在这个类里面用到了动态的创建java文件并编译它.这个我在上一节说明了..

package com.cjb.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.StandardJavaFileManager;
import javax.tools.ToolProvider;
import javax.tools.JavaCompiler.CompilationTask;

/**
 *   类说明
 *   @creator 陈静波
 *   @email
 *   @create-time  May 20, 2010 3:17:18 PM  
 */
public class Proxy
{
 //动态产生对应的方法实现.这个比较重要..必须看懂
 private static String getMethodStr(Class c)
 {
  Method[] methods = c.getMethods();
  StringBuilder methodStr = new StringBuilder();
  String rt = "\r\n";
  
  for(Method m : methods)//遍历接口里的所有方法
  {
   methodStr.append("public void ").append(m.getName())//因为这个是模拟,所以直接写了void..呵呵
     .append("()").append("{").append(rt)
     .append("try{")
     .append("Method method = ").append(c.getName()).append(".class.getMethod(\""+m.getName()+"\");").append(rt)
     .append("h.invoke(method);").append(rt)
     .append("}").append("catch (Exception e){e.printStackTrace();}").append(rt)
     .append("}").append(rt);
  }
  return methodStr.toString();
 }
 /**
  * @param c 表示被代理类接口对应的Class对象,我们这里是Store.class.注意,这里是个接口
  * @param h 表示实现了invacationHandler接口的对象
  *    这个就是我们实现切面的地方.这里定义了我们在被代理方法的前后添加什么功能
  *    比如这里我们就是需要添加事务,那么,添加事务的代码就是写在这个对象里面
  * @return
  * @throws Exception
  */
 @SuppressWarnings("unchecked")
 public static Object newInstance(Class c,InvocationHandler h) throws Exception
 {
  String rt = "\r\n";
  String source = "package com.cjb.proxy;"+rt
    + "import java.lang.reflect.Method;"+rt
    + "/**" + " *   类说明"+ rt
    + "*   @creator 陈静波" + " *   @email "+ rt
    + " *   @create-time  May 20, 2010 10:05:23 AM   " + " */"+ rt
    + "public class Proxy1 implements "+c.getName()+ rt  + "{"+ rt  + "private InvocationHandler h;" + rt +
    "public Proxy1(InvocationHandler h)"+ rt  + " {" + "  this.h = h;"+ rt
    + " }" + rt +

    Proxy.getMethodStr(c) + rt+
    "}";

  String fileName = "d:/src/com/cjb/proxy/Proxy1.java";//在JDK的实现里,类名是$Proxy1,但是,可以发现,我们在使用的时候,这个类名无关紧要.
  File f = new File(fileName);
  FileWriter fw = new FileWriter(f);
  fw.write(source);
  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:/d:/src/") };//最后的"/"别忘记加
  URLClassLoader ul = new URLClassLoader(urls);
  Class claz = ul.loadClass("com.cjb.proxy.Proxy1");
System.out.println(claz.getName());
  
  Constructor ctr = claz.getConstructor(InvocationHandler.class);
  return  ctr.newInstance(h);
 }
}

0 0
原创粉丝点击