您的位置:首页 > 编程语言 > Java开发

动态代理 原理简析(java. 动态编译,动态代理)

2014-04-25 17:30 555 查看
动态代理:
  1.动态编译 JavaCompiler.CompilationTask 动态编译想理解自己查API文档
  2.反射被代理类 主要使用Method.invoke(Object o,Object... args);对带有指定参数的指定对象调用由此 Method 对象表示的底层方法。
  3.类的加载 URLClassLoader可以加载硬盘任意位置的.java文件。class.getClassLoader只能加载classPath目录下的类。
动态代理可以理解为 动态生成发射代理的类。这其中可以动态增加逻辑操作。比如日志的打印,事物的处理等。spring的AOP操作也是动态代理的。

假设我们有一个接口GrowAble可成长的。

package com.cn;

public interface GrowAble {
void growUp();
}


一棵小树苗实现了这个接口

package com.cn;
public class Tree implements GrowAble {
@Override
public void growUp() {
System.out.println("I am a tree , I'm grow up!");
}

}


这时我们想不在不改变源码的情况下想知道树长了多少这个操作?
我们需要一个转换接口。

package com.cn;
import java.lang.reflect.Method;

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


一个实现接口类。

package com.cn;
import java.lang.reflect.Method;
import java.util.Random;

public class HeightInvactionHandle implements InvactionHandle {
@Override
public void invoke(Object c, Method m) {
try {
m.invoke(this.o);
System.out.println("这棵树长了" + new Random().nextInt(9527)+"米!!!" );
} catch (Exception e) {
e.printStackTrace();
}
}
private Object o;
public HeightInvactionHandle(Object o) {
super();
this.o = o;
}
}


现在最重要的Proxy类了。把上述两个接口接口起来。

package com.cn;
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.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import javax.tools.JavaCompiler.CompilationTask;
/**
* 动态代理
* @author 灵台方寸小道士
*/
public class Proxy {
public static Object getNewInstance(Class<?> c,Object object) throws Exception {
String path = System.getProperty("user.dir") + File.separator + "mybin"
+ File.separator + "com" + File.separator + "cn"
+ File.separator;
String fileName = "$Proxy.java";
String nextLine = System.getProperty("line.separator");
// create java File
String fileValue = "package com.cn;"+nextLine+
"import com.cn.*;"+nextLine+
"import java.lang.reflect.Method;"+nextLine+
"public class $Proxy implements "+ c.getName() +"{"+nextLine+
"    private InvactionHandle h;"+nextLine+
"    public $Proxy(InvactionHandle hin)"+ nextLine+
"     {"+nextLine+
"           this.h = hin;"+nextLine+
"     }"+nextLine;
Method[] methods = c.getDeclaredMethods();
for (Method m:methods) {
fileValue += "    public "+ m.getReturnType()+" "+m.getName()+"()"+nextLine+
"     {"+nextLine+
"          try{            "+nextLine+
//测试方法不带参数  所以new Class<?>[]{}空参数传入
"          Method me = "+c.getName()+".class.getDeclaredMethod(\""+m.getName()+"\",  new Class<?>[]{});"+nextLine+
"          h.invoke(this,me);"+nextLine+
"          }catch(Exception e){ "+nextLine+
"           e.printStackTrace(); }"+nextLine+
"     }"+nextLine;
}
fileValue +="}"+nextLine;
File f =  new File(path);//是否存在此目录
if (!f.exists())
f.mkdirs();
FileWriter writer = new FileWriter(new File(f,fileName));
writer.write(fileValue);
writer.flush();
writer.close();
System.out.println("***************     create java file over      ******************");
// compiler 生成 class文件  调取javac编译
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager manager = compiler.getStandardFileManager(null,null, null);
Iterable<? extends JavaFileObject> in = manager.getJavaFileObjects(path+ fileName);
CompilationTask task = compiler.getTask(null, manager, null, null,null, in);
task.call();
System.out.println("***************     complier class file over      ******************");

// loader 加载class文件 的第一种方法 URLClassLoader可以load任意目录下的类!
URL[] urls = new URL[] { new URL("file:/" + System.getProperty("user.dir") + File.separator + "mybin"+ File.separator) };
URLClassLoader loader = new URLClassLoader(urls);
Class<?> d = loader.loadClass("com.cn.$Proxy");
System.out.println("***************     loader class file over      ******************");

// newInstance class JVM
Constructor<?> con = d.getConstructor(InvactionHandle.class);
Object o = con.newInstance(object);
// newInstance...
/**
加载class文件 的第二种方法 ClassLoader只能load位于classpath(src目录)下的类
Class<?> second = Proxy.class.getClassLoader().loadClass("com.cn.$Proxy");
System.out.println(second.getSimpleName());
*/
return o;
}
}


JavaCompiler 是用于编译生成的java代码。在用URLClassLoader将class文件加载进内存。在实例化。

下面一个测试类 Client

package com.cn;
public class Client {

public static void main(String[] args) throws Exception {
Tree tree = new Tree();
InvactionHandle handle = new HeightInvactionHandle(tree);
GrowAble gro = (GrowAble)Proxy.getNewInstance(GrowAble.class, handle);
gro.growUp();
System.out.println("测试结束");
}
}


运行结果

***************     create java file over      ******************
***************     complier class file over      ******************
***************     loader class file over      ******************
I am a tree , I'm grow up!
这棵树长了2174米!!!
测试结束


现在我们在用JDK来做做

package com.cn;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Random;

public class JDKInvocationHandle implements InvocationHandler {

private Object o;
public JDKInvocationHandle(Object o)
{
super();
this.o = o;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object result =  null;
try {
result = method.invoke(o,args);
System.out.println("这棵树长了" + new Random().nextInt(9527)+"米!!!" );
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
}


测试类

package com.cn;
public class Client2 {

public static void main(String[] args) {
java.lang.reflect.InvocationHandler h = new JDKInvocationHandle(new Tree());
GrowAble gro = (GrowAble) java.lang.reflect.Proxy.newProxyInstance(
GrowAble.class.getClassLoader(),
new Class[] { GrowAble.class },
h);
gro.growUp();
System.out.println("测试结束");
}
}


运行结果

I am a tree , I'm grow up!
这棵树长了726米!!!
测试结束


文章目标 学习java动态代理原理。仅当抛砖引玉作用。

文章原创。转载请注明/article/5702882.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: