第六篇 - 手写基于接口实现动态代理

第六篇 - 手写基于接口实现动态代理_第1张图片
Github源码下载地址:https://github.com/chenxingxing6/sourcecode/tree/master/code-proxy

第六篇 - 手写基于接口实现动态代理_第2张图片


一、前言

我们知道常见的动态代理有两种实现方式,基于jdk实现动态代理,基于cglib实现动态代理。本篇博客将自已模仿jdk实现接口动态代理,自己手写源码,实现一个接口的动态代理。


二、实现接口动态代理

如果不了解jdk动态代理的,先去了解一下,在来看此文章…

2.1动态代理原理图

第六篇 - 手写基于接口实现动态代理_第3张图片
Proxy通过(interfaces/invocationHandler)生成代理类 $Proxy0;

Proxy通过ClassLoader来加载生成的代理类$Proxy0的字节码文件;


2.2自己实现

2.2.1 目录结构

第六篇 - 手写基于接口实现动态代理_第4张图片

2.2.2 实现原理图

第六篇 - 手写基于接口实现动态代理_第5张图片

2.2.3 自定义InvocationHandler
package com.my;

import java.lang.reflect.Method;

/**
 * @Author: cxx
 * @Date: 2019/10/2 11:11
 */
public interface MyInvocationHandler {
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}

2.2.4 自定义MyClassLoader
package com.my;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;

/**
 * @Author: cxx
 * @Date: 2019/10/2 14:24
 */
public class MyClassLoader extends ClassLoader{
    private String baseDir;
    public MyClassLoader() {
        this.baseDir = this.getClass().getResource("").getFile();
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        String className = this.getClass().getPackage().getName() + "." + name;
        if (baseDir != null && baseDir != ""){
            File f = new File(baseDir + "/" + name +".class");
            ByteArrayOutputStream out = null;
            FileInputStream fis = null;
            if (f.exists()){
                try {
                    fis = new FileInputStream(f);
                    out = new ByteArrayOutputStream();
                    byte[] b = new byte[1024];
                    int len;
                    while ((len = fis.read(b)) != -1){
                        out.write(b, 0, len);
                    }
                    return defineClass(className, out.toByteArray(), 0, out.size());
                }catch (Exception e){
                    e.printStackTrace();
                }finally {
                    if (fis != null){
                        try {
                            fis.close();
                        }catch (Exception e){
                            e.printStackTrace();
                        }
                    }
                    if (out != null){
                        try {
                            out.close();
                        }catch (Exception e){
                            e.printStackTrace();
                        }
                    }
                    // 查看.class文件
                    f.delete();
                }
            }
        }
        return null;
    }
}

为什么要定义一个自定义的类加载器呢?它的作用是什么呢?

想手写JDK动态代理,那么我们将自己在内存中生成动态代理类加载到jvm,重写了findClass方法,就是为了在指定路径下加载指定的字节码文件。

2.2.5 自定义MyProxy

自己实现代理类MyProxy,步骤分为以下5步

1.生成java源码
2.將源码输出到java文件中
3.将java文件编译成class文件
4.将class加载进jvm
5.返回代理类对象
第六篇 - 手写基于接口实现动态代理_第6张图片

package com.my;

import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

/**
 * @Author: cxx
 * @Date: 2019/10/2 11:14
 */
public class MyProxy implements Serializable {
    private static final String ln = "\r\n";
    public static Object newProxyInstance(MyClassLoader classLoader, Class<?>[] interfaces, MyInvocationHandler myInvocationHandler){
        File file = null;
       try {
           // 1.获取java源码,并输出到文件
           String source = generateSrc(interfaces);
           String filePath = MyProxy.class.getResource("").getPath();
           file = new File(filePath + "$Proxy0.java");
           FileWriter fw = new FileWriter(file);
           fw.write(source);
           fw.flush();
           fw.close();

           // 2.对源码进行编译
           JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
           StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
           Iterable iterable = fileManager.getJavaFileObjects(file);
           JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, null, null, null, iterable);
           task.call();
           fileManager.close();

           // 3.加载class文件到jvm
           Class proxyClass = classLoader.findClass("$Proxy0");

           // 4.返回代理对象
           Constructor c = proxyClass.getConstructor(MyInvocationHandler.class);
           return c.newInstance(myInvocationHandler);
       }catch (Exception e){
           e.printStackTrace();
       }finally {
           file.delete();
       }
       return null;
    }

    /**
     * 获取源码,动态生成java类,看$ProxyMy
     * @param interfaces
     * @return
     */
    private static String generateSrc(Class<?>[] interfaces) {
        StringBuffer sb = new StringBuffer();
        sb.append("package com.my;" + ln);
        sb.append("import java.lang.reflect.Method;" + ln);
        sb.append("public class $Proxy0 implements " + interfaces[0].getName() + "{" + ln);
        sb.append("private MyInvocationHandler h;"+ln);
        sb.append("public $Proxy0(MyInvocationHandler h) { " + ln);
        sb.append("this.h = h;"+ln);
        sb.append("}" + ln);
        for (Method m : interfaces[0].getMethods()) {
            sb.append("public " + m.getReturnType().getName() + " "
                    + m.getName() + "(String id) {" + ln);
            sb.append("try{" + ln);
            sb.append("Method m = " + interfaces[0].getName()
                    + ".class.getMethod(\"" + m.getName()
                    + "\",String.class);" + ln);
            sb.append("this.h.invoke(this,m,new Object[]{id});" + ln);
            sb.append("}catch(Throwable e){" + ln);
            sb.append("e.printStackTrace();" + ln);
            sb.append("}"+ln);
            sb.append("}"+ln);
        }
        sb.append("}" + ln);
        return sb.toString();
    }
}
2.2.6 最终生成$MyProxy.java

生成代理类
第六篇 - 手写基于接口实现动态代理_第7张图片

package com.my;

import com.IUserService;

import java.lang.reflect.Method;

/**
 * @Author: cxx
 * @Date: 2019/10/2 11:29
 * 动态生成代理类:如下
 */
public class $ProxyMy implements IUserService {
    private MyInvocationHandler invocationHandler;
    public $ProxyMy(MyInvocationHandler invocationHandler) {
        this.invocationHandler = invocationHandler;
    }
    public void delete(String id) {
        try {
            Method m = IUserService.class.getMethod("delete", String.class);
            invocationHandler.invoke(this, m, new Object[]{id});
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
    }
}

2.2.7 测试
package com.my;

import com.IUserService;
import com.UserService;

import java.lang.reflect.Method;

/**
 * @Author: cxx
 * @Date: 2019/10/2 11:13
 */
public class MyTest {
    public static void main(String[] args) {
        IUserService userServiceProxy = (IUserService) MyProxy.newProxyInstance(
                new MyClassLoader(),
                UserService.class.getInterfaces(),
                new MyInvocationHandler() {
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("before");
                        method.invoke(new UserService(), args);
                          System.out.println("after");
                        return null;
                    }
                }
        );
        userServiceProxy.delete("100");
    }
}

结果

Connected to the target VM, address: ‘127.0.0.1:49209’, transport: ‘socket’
Disconnected from the target VM, address: ‘127.0.0.1:49209’, transport: ‘socket’
before
删除用户 id:100
after


你可能感兴趣的:(jdk,接口,动态代理,手写,源码,手写源码)