JDK动态代理原理
实际上jdk的动态代理很简单,最重要的方法就是ProxyGenerator.generateProxyClass(),生成代理类字节码文件,动态编译之后交给类加载器加载也就是调用defineClass0(),然后实例化就完成了。
http://blog.csdn.net/qq_25235807/article/details/72084759
这是原来写的一个动态代理的过程。接下来主要是仿照jdk动态代理自己实现一下。
第一步:自己的代理类MyProxy
package myproxy;
import java.lang.reflect.Constructor;
public class MyProxy {
private final static Class[] constructorParams = { MyInvocationHandler.class };
//我们也将构造私有化
@SuppressWarnings("unused")
private MyProxy() {
}
protected MyInvocationHandler h;
protected MyProxy(MyInvocationHandler h) {
this.h = h;
}
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, MyInvocationHandler h) {
Object newInstance = null;
// 获得代理类class对象
try {
Class<?> c1 = getProxyClass(loader, interfaces);
//实例化代理类对象
Constructor<?> cons = c1.getConstructor(constructorParams);
newInstance = cons.newInstance(h);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return newInstance;
}
private static final Class<?> getProxyClass(ClassLoader loader, Class<?>[] interfaces) throws Exception {
final String proxyName = "$proxy0";
Class<?> interfaceClass = null;
for (Class<?> intf : interfaces) {
interfaceClass = Class.forName(intf.getName(), false, loader);
// 判断被代理的类是否为接口
if (!interfaceClass.isInterface()) {
throw new Exception(intf + "is not an interface");
}
}
//获得生成的代理的字节码文件路径
String filePath = MyProxyGenerator.generateProxyClass(proxyName, interfaces);
//通过类加载器加载字节码文件到内存
MyClassLoader loader0 = new MyClassLoader(filePath);
//返回一个代理类的Class对象
Class<?> proxyClass = loader0.findClass(proxyName);
return proxyClass;
}
}
第二步:拼接源文件,并动态的编译生成字节码文件
package myproxy;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Method;
import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
public class MyProxyGenerator {
static final String rn="\r\n";
/**
* @param proxyName
* @param interfaces
* @return 生成代理类字节码文件,实际就是动态拼接字节码文件的过程
*/
public static String generateProxyClass(String proxyName, Class<?>[] interfaces) {
StringBuffer simpleIntfName=new StringBuffer();
StringBuffer intfName=new StringBuffer();
for(int i=0;i<interfaces.length;i++){
intfName.append("import ").append(interfaces[i].getName()).append(";"+rn);
simpleIntfName.append(interfaces[i].getSimpleName());
if(i!=interfaces.length-1){
simpleIntfName.append(",");
}
}
String proxyStr="import java.lang.reflect.Method;"
+rn
+"import myproxy.MyInvocationHandler;"
+rn
+"import myproxy.MyProxy;"
+rn
+intfName.toString()
+rn
+"public final class "+proxyName +" extends MyProxy implements " +
simpleIntfName.toString()+" {"
+rn
+"public "+proxyName+"(MyInvocationHandler h){"
+rn
+" super(h);"
+rn
+"}"
+rn
+createMethods(interfaces)
+rn
+"}";
String filePath ="F:/EclipseEEWorkPace/DataStru/src/myproxy/";
try {
FileWriter writer = new FileWriter(filePath+proxyName+".java");
writer.write(proxyStr);
writer.flush();
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
//拿到编译器
JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
//拿到一个文件管理系统
StandardJavaFileManager fileMgr = javaCompiler.getStandardFileManager(null, null, null);
//获得java文件
Iterable unit = fileMgr.getJavaFileObjects(filePath+proxyName+".java");
//动态编译
javaCompiler.getTask(null,
fileMgr,
null,
null,
null,
unit).call();
return filePath;
}
private static String createMethods(Class<?>[] interfaces) {
String methodStr="";
for(Class<?> intf : interfaces ){
Method[] methods = intf.getMethods();
for(Method method : methods){
String returnType = method.getReturnType().getSimpleName();
StringBuffer isreturn =new StringBuffer();
StringBuffer changeReurn =new StringBuffer();
if(!returnType.equals("void")){
isreturn.append("return ");
changeReurn.append("("+returnType+")");
}
Class<?>[] parameterTypes = method.getParameterTypes();
StringBuffer parameterType=new StringBuffer();
StringBuffer parameterType1=new StringBuffer();
StringBuffer parameterName =new StringBuffer();
for(int i=0;i<parameterTypes.length;i++){
parameterType1.append(parameterTypes[i].getSimpleName()).append(".class");
parameterType.append(parameterTypes[i].getSimpleName())
.append(" arg"+i);
parameterName.append("arg"+i);
if(i!=parameterTypes.length-1){
parameterType.append(",");
parameterType1.append(",");
parameterName.append(",");
}
}
methodStr +="public final " +returnType+" "+ method.getName()+"("+parameterType.toString()+") throws Exception{"
+rn
+"Method md =" +intf.getSimpleName()+".class.getMethod(\""+method.getName()+"\",new Class[]{"+parameterType1.toString()+"});"
+rn
+isreturn.toString()+changeReurn.toString()
+"this.h.invoke(this, md, new Object[] {"+parameterName.toString()+"});"
+rn
+ "}"
+rn;
}
}
return methodStr;
}
}
查看反编译后的class文件
import java.lang.reflect.Method;
import myproxy.MyInvocationHandler;
import myproxy.MyProxy;
import proxy.DataService;
import proxy.DataService1;
public final class $proxy0
extends MyProxy
implements DataService, DataService1
{
public $proxy0(MyInvocationHandler paramMyInvocationHandler)
{
super(paramMyInvocationHandler);
}
public final void update(String paramString)
throws Exception
{
Method localMethod = DataService.class.getMethod("update", new Class[] { String.class });
this.h.invoke(this, localMethod, new Object[] { paramString });
}
public final int save(String paramString, int paramInt)
throws Exception
{
Method localMethod = DataService.class.getMethod("save", new Class[] { String.class, Integer.TYPE });
return ((Integer)this.h.invoke(this, localMethod, new Object[] { paramString, Integer.valueOf(paramInt) })).intValue();
}
public final String create(String paramString)
throws Exception
{
Method localMethod = DataService1.class.getMethod("create", new Class[] { String.class });
return (String)this.h.invoke(this, localMethod, new Object[] { paramString });
}
}
第三步:通过类加载器,将class文件装载到内存
package myproxy;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class MyClassLoader extends ClassLoader {
private String proxyClassFilePath;
FileInputStream fis = null;
ByteArrayOutputStream baos = null;
public MyClassLoader(String proxyClassFilePath) {
this.proxyClassFilePath = proxyClassFilePath;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
byte[] data = loadClassFile(name);
return defineClass(name, data, 0, data.length);
}
private byte[] loadClassFile(String name) {
File file = new File(proxyClassFilePath + name + ".class");
if (file.exists()) {
try {
fis = new FileInputStream(file);
baos = new ByteArrayOutputStream();
byte[] buf = new byte[1024];
int len = 0;
while ((len = fis.read(buf)) != -1) {
baos.write(buf, 0, len);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
return baos.toByteArray();
}
}
获得代理类实例,验证是否成功
package proxy;
import java.lang.reflect.Method;
import myproxy.MyInvocationHandler;
public class DataInvocationHandler implements MyInvocationHandler {
private DataService dataService;
public DataInvocationHandler(DataService dataService) {
this.dataService = dataService;
}
private void before() {
System.out.println("通知类 ,业务方法前调用--before");
}
private void after() {
System.out.println("通知类 ,业务方法后调用--after");
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Exception {
before();
method.invoke(dataService, args);
after();
return null;
}
}
package proxy;
import java.io.IOException;
import myproxy.MyProxy;
public class Main {
public static void main(String[] args) throws IOException {
DataService d = (DataService) MyProxy.newProxyInstance(Main.class.getClassLoader(),
new Class<?>[] { DataService.class, DataService1.class },
new DataInvocationHandler(new DataServiceImpl()));
try {
d.update("name");
} catch (Exception e) {
e.printStackTrace();
}
/*
* byte[] bs = ProxyGenerator.generateProxyClass("$proxy1", new
* Class<?>[]{DataService.class,DataService1.class}); FileOutputStream
* fs=new FileOutputStream("$proxy1.class"); fs.write(bs); }
*/
}
}
总结
实际上jdk动态代理非常简单,其核心的方法就是ProxyGenerator.generatorProxyClass()方法生成字节码文件。在它的内部会遍历它实现接口的方法,并且在内部会调用实现InvocationHandler接口的代理的invoke方法实现代理。这也是为什么代理类为什么必须继承InInvocationHandler接口的原因,最后通过defineClass0()将字节码文件装载到内存。
但是我们自己实现的动态代理要慢很多,可见动态代理实际上还有很多值得研究的地方,其中一点就是缓存!!