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

    实验的目录结构

    1、JDK动态代理

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

    public interface Person {
        
        public void eat();
    }

    实现类PersonImpl

    public class PersonImpl implements Person {
    
        @Override
        public void eat() {
             System.out.println("吃午饭");
        }
    
    }

    调用处理器类PersonInvocationHandler

    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 {
            System.out.println("吃早饭");
            method.invoke(obj, args);
            System.out.println("吃晚饭");
            return null;
        }
    
    }

    测试类

    public class JdkTest {
    
        public static void main(String[] args) throws Exception {
            PersonInvocationHandler personInvocationHandler = new PersonInvocationHandler(new PersonImpl());
            
            //利用JDK的Proxy类的newProxyInstance方法创建代理对象(代理类),该方法需要三个参数:1)目标类 的 类加载器;2)目标类 所实现的所有接口; 3)重写了invoke方法的InvocationHandler类)
            Person personProxy = (Person) Proxy.newProxyInstance(
                    PersonImpl.class.getClassLoader(),
                    PersonImpl.class.getInterfaces(), 
                    personInvocationHandler);

    personProxy.eat(); }

    测试结果

     

    2、自定义动态代理

    针对(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.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 {
            System.out.println("吃早饭");
            method.invoke(obj, args);//new的被代理类实例,传来使用。
            System.out.println("吃晚饭");
            return null;
        }
    
    }

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

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

    分析说明:

    Proxy的newProxyInstance()方法,需要三个参数(类加载器,被代理类实现的所有接口,调用处理器类),返回值为代理类的实例对象,它的工作内容是:

    1)生成代理类的java源碼:起个类名并实现所有的接口(传入的第二个参数);定义个有参构造器,调用处理器类作为参数(传入的第三个参数);代理类的eat()方法, 拿到上述的所有接口中的方法的实例对象;运行调用处理类的invoke()方法()。

    (所以,我们对这个代理类实例化后,运行eat()方法时,是调用了调用处理器类(MyPersonInvocationHandler)的invoke()方法。这个invoke()方法就是增强功能后的方法,从上面的调用处理器类可以看到,invoke方法里包括增强的功能method(从代理类传来的接口中方法类的实例)的invoke方法),方法实例的invoke方法里有两个参数,一个是对象实例(即得知道是哪个对象的这个方法,很明显是被代理类的实例),一个是这个方法的参数是什么,说了这么多其实就是增强的功能加原理被代理类的功能。

    2)把1)中生成的java代码写到$Proxy0.java文件中;

    3)把2)中生成的java文件编译为.calss文件;

    4)用自定义的类加载器把.calss文件加载到jvm得到Class对象;

    5)对Class对象实例化得到代理类对象返回。

    生成的代理类 ,$Proxy0.java

    package bean;
    
    import java.lang.reflect.Method;
    
    public class $Proxy0 implements bean.Person {
        private MyInvocationHandler h;
    
        public $Proxy0(MyInvocationHandler h) {
            this.h = h;
        }
    
        public void eat() {
            try {
                Method m = bean.Person.class.getMethod("eat", new Class[] {});//这里拿到了接口中的方法的实例对象,但是是空的
                this.h.invoke(this, m, null);//把空的方法的实例对象传递到调用处理器的invoke方法中填充允许。
            } catch (Throwable e) {
                e.printStackTrace();
            }
        }
    }

    具体代码如下

    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);
                //System.out.println("java源码:" + src);
        
                // 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,如下所示,

    其实自定义类加载器非常容易,自定义一个类继承ClassLoader类,然后重写它的findClass()方法。

    findClass()方法里做两件事:

    1)读入 xx.class文件,并放到字节数组中 byte[ ] ;

    2)调用defineClass()函数,传入这个Class的名字(即.class的文件名)、字节数组、数组长度,这几个参数,就可以把这二进制的class文件,转换为jvm里的Class对象。

     1 public class MyClassLoader extends ClassLoader {
     2 
     3     private File classPathfile;
     4 
     5     public MyClassLoader() {
     6         String classpth = MyClassLoader.class.getResource("").getPath();
     7         classPathfile = new File(classpth);
     8     }
     9 
    10     @Override
    11     public Class<?> findClass(String name) throws ClassNotFoundException {
    12         String className = MyClassLoader.class.getPackage().getName() + "." +name;
    13         if (classPathfile != null) {
    14             File file = new File(classPathfile, name + ".class");
    15             FileInputStream fileInputStream = null;
    16             ByteArrayOutputStream outputStream = null;
    17             try{
    18                 fileInputStream = new FileInputStream(file);
    19                 outputStream = new ByteArrayOutputStream();
    20                 byte[] buff = new byte[1024];
    21                 int len;
    22                 while((len=fileInputStream.read(buff))!=-1){
    23                     outputStream.write(buff, 0, len);
    24                 }
    25                 return defineClass(className, outputStream.toByteArray(), 0, outputStream.size());
    26             }catch(Exception e){
    27                 e.printStackTrace();
    28             }finally{
    29                 if(null!=fileInputStream){
    30                     try {
    31                         fileInputStream.close();
    32                     } catch (IOException e) {
    33                         // TODO Auto-generated catch block
    34                         e.printStackTrace();
    35                     }
    36                 }
    37                 if(null!=outputStream){
    38                     try {
    39                         outputStream.close();
    40                     } catch (IOException e) {
    41                         // TODO Auto-generated catch block
    42                         e.printStackTrace();
    43                     }
    44                 }
    45             }
    46         }
    47         return null;
    48     }
    49 
    50 }

    最后测试类代码如下所示

    public class CustomTest {
    
        public static void main(String[] args) {
            MyPersonInvocationHandler personInvocationHandler = new MyPersonInvocationHandler(
                    new PersonImpl());//生成代理类实例传入调用处理器
            
         //MyProxy的newProxyInstanc方法中的工作,根据第二和第三个参数生成代理类的java代码(具体代码看上面),写入文件xx.java,编译生成.calss,
         //用第一个参数(类加载器),加载.class得到Class对象,然后new得到代理类实例对象返回。 Person personProxy
    = (Person) MyProxy.newProxyInstance( new MyClassLoader(), PersonImpl.class.getInterfaces(), personInvocationHandler);
    personProxy.eat(); } }

    结果

    3,invoke小实验

    public class A {
    
        public void say(String name){
            System.out.println("Hello, " + name);
        }
    }
    import java.lang.reflect.Method;
    
    public class InvokeTest {
        
        public static void main(String[] args) throws Exception {
            Class<A> clz = A.class;//得到A的Class实例
            Object o = clz.newInstance();//得到A的实例对象
            Method m = clz.getMethod("say", String.class);//根据方法名和方法的参数类型得到方法实例
            for (int i = 0; i < 16; i++) {
                m.invoke(o, Integer.toString(i));//运行方法实例,传入实例对象(运行哪个对象的这个方法)和方法的参数
            }
        }
    }

    运行结果

    可以看到方法实例的invoke方法(对象,方法入参),就是运行哪个对象的这个方法。

    http://www.cnblogs.com/rjzheng/p/8798556.html

  • 相关阅读:
    企业微信api接口调用-触发推送企业微信微信好友
    企业微信api接口调用-触发推送企业微信微信好友
    企业微信api接口调用-企业微信好友收发消息
    抖音api接口调用-同步抖音推荐的好友
    <转载>医学图像存储与传输系统(PACS)
    <转载>RESTful API 最佳实践
    <转载>DICOMweb——将DICOM影像接入互联网
    API设计规范---RESTful架构详解
    开源医学影像平台---Cornerstonejs开发指南
    开源医学影像平台---Cornerstonejs学习笔记<5>
  • 原文地址:https://www.cnblogs.com/xdyixia/p/9359020.html
Copyright © 2011-2022 走看看