zoukankan      html  css  js  c++  java
  • 自己动手实现JDK动态代理

     

    出自:作者:孤独烟   http://rjzheng.cnblogs.com/

    ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------

     

    引言

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

    image

    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)处不同
    image-w500

    针对(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 = "
    ";
    
        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动态代理类

    -------------------------------------------------------------------------------------------------

    示例总结:

    动态代理实际上: 它私下新建了一个java类$proxy0个$proxy0是实现Person理类InvocationHander 

    后重写Person接口eat而eatInvocationHandlerPersonInvocationHandlereatProxy

    $proxy0,这个java类是实现需要动态代理的接口Person的实现类。然后它的属性是动态代理类的引用,然后重写Person类的方法eat,

    而每一个方法的方法体调用动态代理类InvocationHandler实现类PersonInvocationHandler的方法eat.

    这样在通过代理类工具Proxy获取代理newProxyInstance时,其实获取的是私下的这个$proxy0的对象。 


    描述完 豁然开朗。使用的都是JDK最基本的一些东西,反射,类加载,编译等等 信息。

     
  • 相关阅读:
    [Practical Git] Clean up commits with git rebase
    [Practical Git] Show who changed a line last with git blame
    [Practical Git] Compare file changes with git diff
    [Practical Git] Filter commit history with git log arguments
    [Practical Git] Format commit history with git log arguments
    [Practical Git] Navigate git command pager output with Unix less commands
    [Practical Git] Switching between current branch and last checkout branch
    [ES6] ES6 Parameter Object Destructuring with Required Values
    [React] Higher Order Components (replaces Mixins)
    HDU 1242 Rescue (BFS(广度优先搜索))
  • 原文地址:https://www.cnblogs.com/shoshana-kong/p/9110612.html
Copyright © 2011-2022 走看看