JDK動態(tài)代理原理
實際上jdk的動態(tài)代理很簡單,最重要的方法就是ProxyGenerator.generateProxyClass(),生成代理類字節(jié)碼文件,動態(tài)編譯之后交給類加載器加載也就是調(diào)用defineClass0(),然后實例化就完成了。
http://blog.csdn.net/qq_25235807/article/details/72084759
這是原來寫的一個動態(tài)代理的過程。接下來主要是仿照jdk動態(tài)代理自己實現(xiàn)一下。
第一步:自己的代理類MyProxy
package myproxy;
import java.lang.reflect.Constructor;
public class MyProxy {
private final static Class[] constructorParams = { MyInvocationHandler.class };
//我們也將構(gòu)造私有化
@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");
}
}
//獲得生成的代理的字節(jié)碼文件路徑
String filePath = MyProxyGenerator.generateProxyClass(proxyName, interfaces);
//通過類加載器加載字節(jié)碼文件到內(nèi)存
MyClassLoader loader0 = new MyClassLoader(filePath);
//返回一個代理類的Class對象
Class<?> proxyClass = loader0.findClass(proxyName);
return proxyClass;
}
}
第二步:拼接源文件,并動態(tài)的編譯生成字節(jié)碼文件
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 生成代理類字節(jié)碼文件,實際就是動態(tài)拼接字節(jié)碼文件的過程
*/
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();
//拿到一個文件管理系統(tǒng)
StandardJavaFileManager fileMgr = javaCompiler.getStandardFileManager(null, null, null);
//獲得java文件
Iterable unit = fileMgr.getJavaFileObjects(filePath+proxyName+".java");
//動態(tài)編譯
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文件裝載到內(nèi)存
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("通知類 ,業(yè)務(wù)方法前調(diào)用--before");
}
private void after() {
System.out.println("通知類 ,業(yè)務(wù)方法后調(diào)用--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); }
*/
}
}

圖片.png
總結(jié)
實際上jdk動態(tài)代理非常簡單,其核心的方法就是ProxyGenerator.generatorProxyClass()方法生成字節(jié)碼文件。在它的內(nèi)部會遍歷它實現(xiàn)接口的方法,并且在內(nèi)部會調(diào)用實現(xiàn)InvocationHandler接口的代理的invoke方法實現(xiàn)代理。這也是為什么代理類為什么必須繼承InInvocationHandler接口的原因,最后通過defineClass0()將字節(jié)碼文件裝載到內(nèi)存。
但是我們自己實現(xiàn)的動態(tài)代理要慢很多,可見動態(tài)代理實際上還有很多值得研究的地方,其中一點就是緩存?。?/p>