[Proxy] 深入理解jdk动态代理

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_22271479/article/details/84721486

[Proxy] 深入理解jdk动态代理

分析

jdk的proxy主要有三个类,ProxyProxy.ProxyClassFactoryProxyGeneratorProxy是一个面向用户的Client,主要是管理proxy class cache,jdk的proxy必须是interface,且必须传入一个InvocationHandler接口的实例,规约比较强。Proxy.ProxyClassFactory主要是生产proxy class实例。ProxyGenerator是生产class实例的执行者。最终将生成proxy class 字节码,ProxyGenerator的saveGeneratedFiles属性决定了是否保存class文件。文件路径是BaseDir+/com/sun/proxy/$xx.class。最终ClassLoader将会load此class字节码到JVM中。返回interface的动态代理实例。

自定义proxy

  1. 创建java文件,保存至工作路径。
  2. 通过JavaCompiler将java文件编译成class文件,保存在工作路径。
  3. 通过ClassLoader加载此class文件
  4. 返回interface实例

Proxy

package com.yzz.study.proxy.custom;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.util.Map;
import java.util.WeakHashMap;

/**
 * author:yzz
 * date:2018/12/1
 * E-mail:[email protected]
 * com.yzz.study.proxy.custom
 * {@link java.lang.reflect.Proxy}
 */
public class Proxy {

    /**
     * 缓存 {@link java.lang.reflect.Proxy}
     */
    private static Map<String, Class> cachedProxyClassInstaceMap = new WeakHashMap<String, Class>();

    protected InvocationHandler h;

    private Proxy(){

    }

    protected Proxy(InvocationHandler h){
        this.h = h;
    }

    /**
     * {@link java.lang.reflect.Proxy}
     * 这里实现比较简单的   Class<T>[] interfaces --> Class<T> ince
     * @param loader
     * @param ince
     * @param h
     * @param <T>
     * @return
     */
    public static <T> T newInstance(MyClassLoader loader,
                                   Class<T> ince,
                                   InvocationHandler h) throws Exception {

        String className = "$proxy"+cachedProxyClassInstaceMap.size()+1;
        //1.从cache里查找
        Class targetClass = cachedProxyClassInstaceMap.get(ince.getName());
        if (null != targetClass){
            return (T) targetClass;
        }
        //2.创建java file
        String javaFilePth = ProxyGenerator.createProxyJavaFile(loader,className,ince);
        //3.编译 java file
        CompileJavaCode.compile(javaFilePth);
        //4.load class 文件
        Class<T> _class = (Class<T>) loader.findClass(className);
        //这里加入InvocationHandler实例
        Constructor constructor = _class.getConstructor(InvocationHandler.class);
        return (T) constructor.newInstance(h);
    }
}

ProxyGenerator(负责创建java文件)

package com.yzz.study.proxy.custom;

import javax.sound.sampled.Line;
import java.io.*;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
 * author:yzz
 * date:2018/12/1
 * E-mail:[email protected]
 * com.yzz.study.proxy.custom
 * 注释:创建proxy class 文件
 */
class ProxyGenerator {

    private static final String LINE = "\r\n";

    public static final String LEFT_CLOSE = "}";

    public static final String RIGHT_CLOSE = "{";

    public static final String INVOCATIONHANDLER = "InvocationHandler";

    public static final String PARAMATER = "var";

    public static String createProxyJavaFile(ClassLoader classLoader, String className, Class ince) {
        String filePath = classLoader.getClass().getResource("").getPath() + "/" + className + ".java";
        String packgeNmae = classLoader.getClass().getPackage().getName();
        StringBuffer sb = new StringBuffer();
        sb.append("package ").append(packgeNmae).append(";").append(LINE);
        sb.append("import ").append("java.lang.reflect.InvocationHandler;").append(LINE);
        sb.append("import java.lang.reflect.Method;").append(LINE);
        sb.append("import com.yzz.study.proxy.custom.Proxy;").append(LINE);
        createHead(sb, className,ince.getName());
        Map<String, String> methodNames = createField(sb, ince);
        createConstruct(sb, className);
        createMethods(sb, ince, methodNames);
        createStaticBlock(ince,methodNames,sb);
        sb.append(LEFT_CLOSE);
        try {
            BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(filePath)));
            writer.write(sb.toString());
            writer.flush();
            writer.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return filePath;
    }

    public static void createHead(StringBuffer sb, String className,String inceName) {
        sb.append("public class ")
                .append(className)
                .append(" extends Proxy implements ")
                .append(inceName)
                .append(" ")
                .append(RIGHT_CLOSE)
                .append(" ")
                .append(LINE);
    }

    public static Map<String, String> createField(StringBuffer sb, Class ince) {
        Method[] ms = ince.getMethods();
        Map<String, String> map = new HashMap<String, String>();
        for (int i = 0; i < ms.length; i++) {
            Method method = ms[i];
            sb.append("private static Method ")
                    .append("m")
                    .append(i + 1)
                    .append(";").append(LINE);
            map.put(method.getName(), "m" + (i + 1));
        }
        return map;
    }

    public static void createConstruct(StringBuffer sb, String className) {
        sb.append("public ")
                .append(className)
                .append(" (")
                .append(INVOCATIONHANDLER)
                .append(" h )")
                .append(RIGHT_CLOSE)
                .append("super(h);")
                .append(LEFT_CLOSE)
                .append(LINE);
    }

    public static void createMethods(StringBuffer sb, Class ince, Map<String, String> methodNmaes) {
        Method[] ms = ince.getMethods();
        for (Method m : ms) {
            String methodName = m.getName();
            Class returnType = m.getReturnType();
            Class[] pts = m.getParameterTypes();
            createMethod(methodName, returnType, pts, sb, ince, methodNmaes.get(m.getName()));
        }
    }

    public static void createMethod(String methodName, Class returnType, Class[] pts, StringBuffer sb, Class ince, String mName) {
        sb.append(" public ")
                .append(returnType)
                .append(" ")
                .append(methodName)
                .append("(");
        String[] args = new String[pts.length];
        for (int i = 0; i < pts.length; i++) {
            Class c = pts[i];
            sb.append(c.getName())
                    .append(" ")
                    .append(PARAMATER)
                    .append(i);
            if (i != pts.length - 1) {
                sb.append(",");
            }
            args[i] = PARAMATER + i;
        }
        sb.append(")");
        sb.append(RIGHT_CLOSE);
        createMethodBody(mName, args, sb);
        sb.append(LEFT_CLOSE)
                .append(LINE);

    }

    /***
     *  public Object invoke(Object proxy, Method method, Object[] args)
     *  throws Throwable;
     * @return
     */
    public static void createMethodBody(String methodName, String[] args, StringBuffer sb) {
        sb.append("try ")
                .append(LINE)
                .append(RIGHT_CLOSE)
                .append(LINE)
                .append("super.h.invoke(this,")
                .append(methodName)
                .append(",")
                .append("new Object[]{");
        //这里需要构造参数
        for (int i = 0; i < args.length; i++) {
            sb.append(args[i]);
            if (i != args.length - 1) {
                sb.append(",");
            }
        }
        sb.append("});")
                .append(LINE)
                .append("}catch(Throwable e){")
                .append(LINE)
                .append("e.printStackTrace();")
                .append(LINE)
                .append(LEFT_CLOSE)
                .append(LINE);
    }

    private static void createStaticBlock(Class ince,Map<String,String> classMethodMap,StringBuffer sb){
        sb.append("static ")
                .append(RIGHT_CLOSE)
                .append("try ")
                .append(RIGHT_CLOSE);
        Method[] ms = ince.getMethods();
        for (Method method:ms){
            Class[] ps = method.getParameterTypes();
            String pcs = "";
            for (int i = 0; i < ps.length; i++) {
                pcs += "Class.forName(\""+ps[i].getName() + "\")";
                if (i != ps.length-1){
                    pcs += ",";
                }
            }
            sb.append(classMethodMap.get(method.getName()))
                    .append("=")
                    .append("Class.forName(\"")
                    .append(ince.getName())
                    .append("\")")
                    .append(".getMethod(")
                    .append("\""+method.getName()+"\"")
                    .append(",")
                    .append("new Class[]{")
                    .append(pcs)
                    .append("});")
                    .append(LINE);
        }
        sb.append("}catch(Exception e)")
                .append(RIGHT_CLOSE)
                .append(LINE)
                .append("e.printStackTrace();")
                .append(LINE)
                .append(LEFT_CLOSE);
        sb.append(LEFT_CLOSE);
        sb.append(LINE);
    }
}

CompileJavaCode(负责编译java文件)

编译工作是依赖javax.tools下的资源包

package com.yzz.study.proxy.custom;

import javax.tools.*;
import java.nio.charset.Charset;

/**
 * author:yzz
 * date:2018/12/1
 * E-mail:[email protected]
 * com.yzz.study.proxy.custom
 * 注释:
 */

public class CompileJavaCode {

    public static void compile(String javaFileName){
        try {
            JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
            StandardJavaFileManager manager = compiler.getStandardFileManager(null, null, Charset.defaultCharset());
            Iterable iterable = manager.getJavaFileObjects(javaFileName);
            JavaCompiler.CompilationTask compilationTask = compiler.getTask(null, manager, null, null, null, iterable);
            compilationTask.call();
            manager.close();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

ClassLoader(负责装载指定目录下的class文件)

package com.yzz.study.proxy.custom;

import java.io.*;

/**
 * author:yzz
 * date:2018/12/1
 * E-mail:[email protected]
 * com.yzz.study.proxy.custom
 * 注释: 加载class文件
 */
public class MyClassLoader extends ClassLoader{

    private static String baseDir;

    public MyClassLoader(){
        baseDir = MyClassLoader.class.getResource("").getPath();
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        String className = MyClassLoader.class.getPackage().getName()+"."+name;
        BufferedInputStream bufferedInputStream = null;
        ByteArrayOutputStream byteArrayOutputStream = null;
        try {
            File classFile = new File(baseDir,name.replaceAll("\\.","/") + ".class");
            bufferedInputStream = new BufferedInputStream(new FileInputStream(classFile));
            byteArrayOutputStream = new ByteArrayOutputStream();
            byte[] temp = new byte[1024];
            int len;
            while((len = bufferedInputStream.read(temp)) != -1){
                byteArrayOutputStream.write(temp,0,len);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            if (null != bufferedInputStream){
                try {
                    byteArrayOutputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (null != byteArrayOutputStream){
                try {
                    byteArrayOutputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        return defineClass(className,byteArrayOutputStream.toByteArray(),0,byteArrayOutputStream.size());
    }
}

interface

package com.yzz.study.proxy.custom;

/**
 * author:yzz
 * date:2018/12/1
 * E-mail:[email protected]
 * com.yzz.study.proxy.jdk
 * 注释:
 */
public interface ILog {

    void printLog(String a,Object b);
    void printLog1(String a,Object b);
    void printLog2(String a,Object b);
}

测试

package com.yzz.study.proxy.custom;


import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * author:yzz
 * date:2018/12/2
 * E-mail:[email protected]
 * com.yzz.study.proxy.custom
 * 注释:
 */
public class Test {

    public static void main(String[] args) {
        try {
            ILog log = Proxy.newInstance(new MyClassLoader(), ILog.class, new InvocationHandler() {
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    System.out.println("==================");
                    System.out.println(method.getName());
                    return null;
                }
            });
            System.out.println(log);
            log.printLog("1","2");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

展示

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.yzz.study.proxy.custom;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class $proxy01 extends Proxy implements ILog {
    private static Method m1;
    private static Method m2;
    private static Method m3;

    public $proxy01(InvocationHandler var1) {
        super(var1);
    }

    public void printLog1(String var1, Object var2) {
        try {
            super.h.invoke(this, m1, new Object[]{var1, var2});
        } catch (Throwable var4) {
            var4.printStackTrace();
        }

    }

    public void printLog2(String var1, Object var2) {
        try {
            super.h.invoke(this, m2, new Object[]{var1, var2});
        } catch (Throwable var4) {
            var4.printStackTrace();
        }

    }

    public void printLog(String var1, Object var2) {
        try {
            super.h.invoke(this, m3, new Object[]{var1, var2});
        } catch (Throwable var4) {
            var4.printStackTrace();
        }

    }

    static {
        try {
            m1 = Class.forName("com.yzz.study.proxy.custom.ILog").getMethod("printLog1", new Class[]{Class.forName("java.lang.String"), Class.forName("java.lang.Object")});
            m2 = Class.forName("com.yzz.study.proxy.custom.ILog").getMethod("printLog2", new Class[]{Class.forName("java.lang.String"), Class.forName("java.lang.Object")});
            m3 = Class.forName("com.yzz.study.proxy.custom.ILog").getMethod("printLog", new Class[]{Class.forName("java.lang.String"), Class.forName("java.lang.Object")});
        } catch (Exception var1) {
            var1.printStackTrace();
        }

    }
}

总结

jdk的proxy严重依赖InvocationHandler接口,代理实例的方法都会回调该接口的invoke()方法,在invoke()方法下,可以自定义代理逻辑。

猜你喜欢

转载自blog.csdn.net/qq_22271479/article/details/84721486
今日推荐