Spring aop 深入jdk動態(tài)代理(自己寫動態(tài)代理)

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>

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,388評論 25 708
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,724評論 19 139
  • title: Jdk動態(tài)代理原理解析 tags:代理 categories:筆記 date: 2017-06-14...
    行徑行閱讀 19,651評論 3 36
  • 寫給老公 后天就是女神節(jié),趕在這個日子從北京回來,我其實蠻高興的,盡管你這個時候趕回來是因為銀行的房貸批下來了,要...
    艾雨xz閱讀 176評論 2 0
  • 二十年前,一窮二白,聽得最多的就是XX迎來了發(fā)展的春天。 二十年后,各方面條件大幅改善,寒冬卻成了大眾口頭禪?!熬?..
    云非云閱讀 805評論 0 0

友情鏈接更多精彩內(nèi)容