自己实现JDK动态代理
实验的目录结构
1、JDK动态代理
先来一段jdk动态代理的demo,
首先创建一个接口,Person
public interface Person { public void eat(); }
实现类PersonImpl
public class PersonImpl implements Person { @Override public void eat() { System.out.println("吃午饭"); } }
调用处理器类PersonInvocationHandler
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class PersonInvocationHandler implements InvocationHandler { private Object obj; public PersonInvocationHandler(Object obj) { this.obj = obj; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("吃早饭"); method.invoke(obj, args); System.out.println("吃晚饭"); return null; } }
测试类
public class JdkTest { public static void main(String[] args) throws Exception { PersonInvocationHandler personInvocationHandler = new PersonInvocationHandler(new PersonImpl()); //利用JDK的Proxy类的newProxyInstance方法创建代理对象(代理类),该方法需要三个参数:1)目标类 的 类加载器;2)目标类 所实现的所有接口; 3)重写了invoke方法的InvocationHandler类) Person personProxy = (Person) Proxy.newProxyInstance( PersonImpl.class.getClassLoader(), PersonImpl.class.getInterfaces(), personInvocationHandler);
personProxy.eat(); }
测试结果
2、自定义动态代理
针对(1),我们有如下代码,先抄袭JDK的InvocationHandler,改个名字成为MyInvocationHandler
package custom; import java.lang.reflect.Method; public interface MyInvocationHandler { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable; }
编写一个JAVA类MyPersonInvocationHandler继承MyInvocationHandler,这段代码与PersonInvocationHandler的代码无异,如下所示
package custom; import java.lang.reflect.Method; public class MyPersonInvocationHandler implements MyInvocationHandler { private Object obj; public MyPersonInvocationHandler(Object obj) { this.obj = obj; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("吃早饭"); method.invoke(obj, args);//new的被代理类实例,传来使用。 System.out.println("吃晚饭"); return null; } }
针对(2),我们实现一个自己的代理生成类MyProxy,其生成java代理类的步骤分为以下5步
- 生成java源碼
- 將源码输出到java文件中
- 将java文件编译成class文件
- 将class加载进jvm
- 返回代理类对象
分析说明:
Proxy的newProxyInstance()方法,需要三个参数(类加载器,被代理类实现的所有接口,调用处理器类),返回值为代理类的实例对象,它的工作内容是:
1)生成代理类的java源碼:起个类名并实现所有的接口(传入的第二个参数);定义个有参构造器,调用处理器类作为参数(传入的第三个参数);代理类的eat()方法, 拿到上述的所有接口中的方法的实例对象;运行调用处理类的invoke()方法()。
(所以,我们对这个代理类实例化后,运行eat()方法时,是调用了调用处理器类(MyPersonInvocationHandler)的invoke()方法。这个invoke()方法就是增强功能后的方法,从上面的调用处理器类可以看到,invoke方法里包括增强的功能和method(从代理类传来的接口中方法类的实例)的invoke方法),方法实例的invoke方法里有两个参数,一个是对象实例(即得知道是哪个对象的这个方法,很明显是被代理类的实例),一个是这个方法的参数是什么,说了这么多其实就是增强的功能加原理被代理类的功能。
2)把1)中生成的java代码写到$Proxy0.java文件中;
3)把2)中生成的java文件编译为.calss文件;
4)用自定义的类加载器把.calss文件加载到jvm得到Class对象;
5)对Class对象实例化得到代理类对象返回。
生成的代理类 ,$Proxy0.java
package bean;
import java.lang.reflect.Method;
public class $Proxy0 implements bean.Person {
private MyInvocationHandler h;
public $Proxy0(MyInvocationHandler h) {
this.h = h;
}
public void eat() {
try {
Method m = bean.Person.class.getMethod("eat", new Class[] {});//这里拿到了接口中的方法的实例对象,但是是空的
this.h.invoke(this, m, null);//把空的方法的实例对象传递到调用处理器的invoke方法中填充允许。
} catch (Throwable e) {
e.printStackTrace();
}
}
}
具体代码如下
public class MyProxy { public static final String ln = "\r\n"; public static Object newProxyInstance(MyClassLoader myClassLoder, Class<?>[] interfaces, MyInvocationHandler h) { try{ // 1 java源碼 String src = generateSrc(interfaces); //System.out.println("java源码:" + src); // 2 將源码输出到java文件中 String filePath = MyProxy.class.getResource("").getPath(); System.out.println(filePath); File f = new File(filePath + "$Proxy0.java"); FileWriter fw = new FileWriter(f); fw.write(src); fw.flush(); fw.close(); //3、将java文件编译成class文件 JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); StandardJavaFileManager manage = compiler.getStandardFileManager(null,null,null); Iterable iterable = manage.getJavaFileObjects(f); JavaCompiler.CompilationTask task = compiler.getTask(null,manage,null,null,null,iterable); task.call(); manage.close(); //4、将class加载进jvm Class proxyClass=myClassLoder.findClass("$Proxy0"); f.delete(); //5、返回代理类对象 Constructor constructor = proxyClass.getConstructor(MyInvocationHandler.class); return constructor.newInstance(h); }catch(Exception e){ e.printStackTrace(); } return null; } private static String generateSrc(Class<?>[] interfaces) { // TODO Auto-generated method stub StringBuffer sb = new StringBuffer(); sb.append("package custom;" + ln); sb.append("import java.lang.reflect.Method;" + ln); sb.append("public class $Proxy0 implements " + interfaces[0].getName() + "{" + ln); sb.append("private MyInvocationHandler h;"+ln); sb.append("public $Proxy0(MyInvocationHandler h) { " + ln); sb.append("this.h = h;"+ln); sb.append("}" + ln); for (Method m : interfaces[0].getMethods()) { sb.append("public " + m.getReturnType().getName() + " " + m.getName() + "() {" + ln); sb.append("try{" + ln); sb.append("Method m = " + interfaces[0].getName() + ".class.getMethod(\"" + m.getName() + "\",new Class[]{});" + ln); sb.append("this.h.invoke(this,m,null);" + ln); sb.append("}catch(Throwable e){" + ln); sb.append("e.printStackTrace();" + ln); sb.append("}"+ln); sb.append("}"+ln); } sb.append("}" + ln); return sb.toString(); } }
针对(3),我们继承ClassLoader,实现一套自己的类加载机制MyClassLoader,如下所示,
其实自定义类加载器非常容易,自定义一个类继承ClassLoader类,然后重写它的findClass()方法。
findClass()方法里做两件事:
1)读入 xx.class文件,并放到字节数组中 byte[ ] ;
2)调用defineClass()函数,传入这个Class的名字(即.class的文件名)、字节数组、数组长度,这几个参数,就可以把这二进制的class文件,转换为jvm里的Class对象。
1 public class MyClassLoader extends ClassLoader { 2 3 private File classPathfile; 4 5 public MyClassLoader() { 6 String classpth = MyClassLoader.class.getResource("").getPath(); 7 classPathfile = new File(classpth); 8 } 9 10 @Override 11 public Class<?> findClass(String name) throws ClassNotFoundException { 12 String className = MyClassLoader.class.getPackage().getName() + "." +name; 13 if (classPathfile != null) { 14 File file = new File(classPathfile, name + ".class"); 15 FileInputStream fileInputStream = null; 16 ByteArrayOutputStream outputStream = null; 17 try{ 18 fileInputStream = new FileInputStream(file); 19 outputStream = new ByteArrayOutputStream(); 20 byte[] buff = new byte[1024]; 21 int len; 22 while((len=fileInputStream.read(buff))!=-1){ 23 outputStream.write(buff, 0, len); 24 } 25 return defineClass(className, outputStream.toByteArray(), 0, outputStream.size()); 26 }catch(Exception e){ 27 e.printStackTrace(); 28 }finally{ 29 if(null!=fileInputStream){ 30 try { 31 fileInputStream.close(); 32 } catch (IOException e) { 33 // TODO Auto-generated catch block 34 e.printStackTrace(); 35 } 36 } 37 if(null!=outputStream){ 38 try { 39 outputStream.close(); 40 } catch (IOException e) { 41 // TODO Auto-generated catch block 42 e.printStackTrace(); 43 } 44 } 45 } 46 } 47 return null; 48 } 49 50 }
最后测试类代码如下所示
public class CustomTest { public static void main(String[] args) { MyPersonInvocationHandler personInvocationHandler = new MyPersonInvocationHandler( new PersonImpl());//生成代理类实例传入调用处理器
//MyProxy的newProxyInstanc方法中的工作,根据第二和第三个参数生成代理类的java代码(具体代码看上面),写入文件xx.java,编译生成.calss,
//用第一个参数(类加载器),加载.class得到Class对象,然后new得到代理类实例对象返回。 Person personProxy = (Person) MyProxy.newProxyInstance( new MyClassLoader(), PersonImpl.class.getInterfaces(), personInvocationHandler);
personProxy.eat(); } }
结果
3,invoke小实验
public class A { public void say(String name){ System.out.println("Hello, " + name); } }
import java.lang.reflect.Method; public class InvokeTest { public static void main(String[] args) throws Exception { Class<A> clz = A.class;//得到A的Class实例 Object o = clz.newInstance();//得到A的实例对象 Method m = clz.getMethod("say", String.class);//根据方法名和方法的参数类型得到方法实例 for (int i = 0; i < 16; i++) { m.invoke(o, Integer.toString(i));//运行方法实例,传入实例对象(运行哪个对象的这个方法)和方法的参数 } } }
运行结果
可以看到方法实例的invoke方法(对象,方法入参),就是运行哪个对象的这个方法。
http://www.cnblogs.com/rjzheng/p/8798556.html