自己动手实现JDK动态代理

服务器

浏览数:143

2019-4-14

AD:资源代下载服务

引言

项目结构如下图所示,maven项目

1、JDK动态代理

先来一段jdk动态代理的demo,
首先创建一个接口,Person

package bean;

public interface Person {
    
    public void eat();
    
}

然后写一个实现类PersonImpl

package bean;

public class PersonImpl implements Person{
    
    @Override
    public void eat() {
        // TODO Auto-generated method stub
        System.out.println("time to eat ");
    }
    
}

然后写个使用类PersonInvocationHandler

package jdk;

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

public class PersonInvocationHandler implements InvocationHandler {

    private Object obj;

    public PersonInvocationHandler(Object obj) {
        this.obj = obj;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        // TODO Auto-generated method stub
        System.out.println("before time to eat");
        method.invoke(obj, args);
        System.out.println("after time to eat");
        return null;
    }

}

最后 再写个测试类

package jdk;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.lang.reflect.Proxy;

import sun.misc.ProxyGenerator;
import bean.Person;
import bean.PersonImpl;

public class jdkTest {

    public static void main(String[] args) throws Exception {
        PersonInvocationHandler personInvocationHandler = new PersonInvocationHandler(
                new PersonImpl());
        Person personProxy = (Person) Proxy.newProxyInstance(
                PersonImpl.class.getClassLoader(),
                PersonImpl.class.getInterfaces(), personInvocationHandler);
        personProxy.eat();
    }
}

输出如下

before time to eat
time to eat 
after time to eat

接下里我们不使用JDK的API,自己实现一套代理类

2、自定义动态代理

先上测试类的代码,如下图所示,共有(1)(2)(3)处不同

针对(1),我们有如下代码,先抄袭JDK的InvocationHandler,改个名字成为MyInvocationHandler

package custom;

import java.lang.reflect.Method;

public interface MyInvocationHandler {
    
     public Object invoke(Object proxy, Method method, Object[] args)
                throws Throwable;
     
}

编写一个JAVA类MyPersonInvocationHandler继承MyInvocationHandler,这段代码与PersonInvocationHandler的代码无异,如下所示

package custom;

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

public class MyPersonInvocationHandler implements MyInvocationHandler {

    private Object obj;

    public MyPersonInvocationHandler(Object obj) {
        this.obj = obj;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        // TODO Auto-generated method stub
        System.out.println("before time to eat");
        method.invoke(obj, args);
        System.out.println("after time to eat");
        return null;
    }

}

针对(2),我们实现一个自己的代理生成类MyProxy,其生成java代理类的步骤分为以下5步

  1. 生成java源碼
  2. 將源码输出到java文件中
  3. 将java文件编译成class文件
  4. 将class加载进jvm
  5. 返回代理类对象

具体代码如下

package custom;

import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;


public class MyProxy {

    public static final String ln = "\r\n";

    public static Object newProxyInstance(MyClassLoader myClassLoder,
            Class<?>[] interfaces, MyInvocationHandler h) {
        try{
            // 1 java源碼
            String src = generateSrc(interfaces);
    
            // 2 將源码输出到java文件中
            String filePath = MyProxy.class.getResource("").getPath();
            System.out.println(filePath);
            File f = new File(filePath + "$Proxy0.java");
            FileWriter fw = new FileWriter(f);
            fw.write(src);
            fw.flush();
            fw.close();
            
            //3、将java文件编译成class文件
            JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
            StandardJavaFileManager manage = compiler.getStandardFileManager(null,null,null);
            Iterable iterable = manage.getJavaFileObjects(f);

            JavaCompiler.CompilationTask task = compiler.getTask(null,manage,null,null,null,iterable);
            task.call();
            manage.close();
            
            //4、将class加载进jvm
            Class proxyClass=myClassLoder.findClass("$Proxy0");
            f.delete();
            
            //5、返回代理类对象
            Constructor constructor = proxyClass.getConstructor(MyInvocationHandler.class);
            return constructor.newInstance(h);
        }catch(Exception e){
             e.printStackTrace();
        }
        return null;
    }

    private static String generateSrc(Class<?>[] interfaces) {
        // TODO Auto-generated method stub
        StringBuffer sb = new StringBuffer();
        sb.append("package custom;" + 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() + "() {" + ln);
            sb.append("try{" + ln);
            sb.append("Method m = " + interfaces[0].getName()
                    + ".class.getMethod(\"" + m.getName()
                    + "\",new Class[]{});" + ln);
            sb.append("this.h.invoke(this,m,null);" + ln);
            sb.append("}catch(Throwable e){" + ln);
            sb.append("e.printStackTrace();" + ln);
            sb.append("}"+ln);
            sb.append("}"+ln);
        }
        sb.append("}" + ln);
        return sb.toString();
    }
}

针对(3),我们继承ClassLoader,实现一套自己的类加载机制MyClassLoader,如下所示,

package custom;

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

public class MyClassLoader extends ClassLoader {

    private File classPathfile;

    public MyClassLoader() {
        String classpth = MyClassLoader.class.getResource("").getPath();
        classPathfile = new File(classpth);
    }

    @Override
    public Class<?> findClass(String name) throws ClassNotFoundException {
        String className = MyClassLoader.class.getPackage().getName() + "." +name;
        if (classPathfile != null) {
            File file = new File(classPathfile, name + ".class");
            FileInputStream fileInputStream = null;
            ByteArrayOutputStream outputStream = null;
            try{
                fileInputStream = new FileInputStream(file);
                outputStream = new ByteArrayOutputStream();
                byte[] buff = new byte[1024];
                int len;
                while((len=fileInputStream.read(buff))!=-1){
                    outputStream.write(buff, 0, len);
                }
                return defineClass(className, outputStream.toByteArray(), 0, outputStream.size());
            }catch(Exception e){
                e.printStackTrace();
            }finally{
                if(null!=fileInputStream){
                    try {
                        fileInputStream.close();
                    } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
                if(null!=outputStream){
                    try {
                        outputStream.close();
                    } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }
        }
        return null;
    }

}

最后测试类代码如下所示

package custom;

import java.lang.reflect.Proxy;

import custom.MyPersonInvocationHandler;
import bean.Person;
import bean.PersonImpl;

public class CustomTest {

    public static void main(String[] args) {
        MyPersonInvocationHandler personInvocationHandler = new MyPersonInvocationHandler(
                new PersonImpl());
        Person personProxy = (Person) MyProxy.newProxyInstance(
                new MyClassLoader(), PersonImpl.class.getInterfaces(),
                personInvocationHandler);
        personProxy.eat();
    }
}

输出如何所示

before time to eat
time to eat 
after time to eat

至此,我们已完全实现了一套自定义的jdk动态代理类