• JAVA设计模式之动态代理


    动态代理模式主要由四个元素共同构成:

      1. 接口,接口中的方法是要真正去实现的

      2. 被代理类,实现上述接口,这是真正去执行接口中方法的类

      3. 代理类,实现InvocationHandler,帮助被代理类去实现方法

      4. 测试用例:

    举例详解:  

      1. 接口:

    public interface Person {
        void searchHouse();
    }

      2. 被代理类Master,实现上述接口:

    public interface Person {    
        void searchHouse();
    }

      3. 代理类HomeLine,实现InvocationHandler接口

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    public class HomeLink implements InvocationHandler{
        
        private Person target;
        
        public Object getInstance(Person target){
            this.target = target;
            Class clazz = target.getClass();
            Object obj = Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);
            return obj;
        }
    
        @Override
        public Object invoke(Object obj, Method method, Object[] args) throws Throwable {
            System.out.println("我是链家,我帮别人找房子..");
            //第一个参数是target,也就是被代理类的对象;第二个参数是方法中的参数
            method.invoke(target, args);
            System.out.println("我是链家,已经找完了..");
            return null;
        }
    }

      4.测试用例:

    public class TestSearchHouse {
        public static void main(String[] args) {
            Person person = (Person) new HomeLink().getInstance(new Master());
            person.searchHouse();
        }
    }

      5. 结果:

    我是链家,我帮别人找房子..
    我是主人,我要找房子,一室一厅!
    我是链家,已经找完了..

    动态代理类详细分析:

      动态代理的步骤:

          1. 首先获得一个被代理对象的引用,

          2. 获得该引用的接口

          3. 生成一个类,这个类实现了我们给的代理对象所实现的接口

          4. 上述类编译生成了.class字节码供JVM使用

          5. 调用上述生成的class

      对于生成的字节码,我们可以输出都磁盘中:

        try {
                byte[] data = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{Person.class});
                FileOutputStream os = new FileOutputStream("D:/$Proxy0.class");
                os.write(data);
                os.flush();
                os.close();
            } catch (Exception e) {
                e.printStackTrace();
            }

         对于生成的$Proxy0.class文件,可以用java反编译工具jd-gui-0.3.3.windows查看,

    import com.yding.pattern.proxy.Person;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.lang.reflect.UndeclaredThrowableException;
    
    public final class $Proxy0 extends Proxy
      implements Person
    {
      private static Method m1;
      private static Method m2;
      private static Method m3;
      private static Method m0;
    
      public $Proxy0(InvocationHandler paramInvocationHandler)
        throws 
      {
        super(paramInvocationHandler);
      }
    
      public final boolean equals(Object paramObject)
        throws 
      {
        try
        {
          return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
        }
        catch (RuntimeException localRuntimeException)
        {
          throw localRuntimeException;
        }
        catch (Throwable localThrowable)
        {
        }
        throw new UndeclaredThrowableException(localThrowable);
      }
    
      public final String toString()
        throws 
      {
        try
        {
          return (String)this.h.invoke(this, m2, null);
        }
        catch (RuntimeException localRuntimeException)
        {
          throw localRuntimeException;
        }
        catch (Throwable localThrowable)
        {
        }
        throw new UndeclaredThrowableException(localThrowable);
      }
    
      public final void searchHouse()
        throws 
      {
        try
        {
          this.h.invoke(this, m3, null);
          return;
        }
        catch (RuntimeException localRuntimeException)
        {
          throw localRuntimeException;
        }
        catch (Throwable localThrowable)
        {
        }
        throw new UndeclaredThrowableException(localThrowable);
      }
    
      public final int hashCode()
        throws 
      {
        try
        {
          return ((Integer)this.h.invoke(this, m0, null)).intValue();
        }
        catch (RuntimeException localRuntimeException)
        {
          throw localRuntimeException;
        }
        catch (Throwable localThrowable)
        {
        }
        throw new UndeclaredThrowableException(localThrowable);
      }
    
      static
      {
        try
        {
          m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
          m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
          m3 = Class.forName("com.yding.pattern.proxy.Person").getMethod("searchHouse", new Class[0]);
          m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
          return;
        }
        catch (NoSuchMethodException localNoSuchMethodException)
        {
          throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
        }
        catch (ClassNotFoundException localClassNotFoundException)
        {
        }
        throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
      }
    }
    View Code

         我们还不仅仅满足于上述的理解,需要更进一步的理解,手写java动态代理的细节过程:

    有三个类我们需要自己手写,分别是Proxy, ClassLoader, InvocationHandler

         1. MyProxy类,有一个方法,newInstance(ClassLoader loader, Class<?> interfaces,MyInvocationHandler h)

    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.JavaCompiler.CompilationTask;
    import javax.tools.StandardJavaFileManager;
    import javax.tools.ToolProvider;
    
    public class MyProxy {
        
        private static String ln = "
    ";
        
        /**
         * Proxy最重要的方法是newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h){ ..}
         * @param loader
         * @param interfaces
         * @param h
         * @return
         */
        public static Object newProxyInstance(MyClassLoader loader,Class<?>[] interfaces,MyInvocationHandler h){
            try {
                //1. 生成源代码
                String proxySrc = generateSrc(interfaces[0]);
                //2. 将源代码输出到磁盘中
                String filePath= MyProxy.class.getResource("").getPath();
                File f = new File(filePath+"$Proxy0.java");
                FileWriter fw = new FileWriter(f);
                System.out.println(f.getAbsolutePath());
                fw.write(proxySrc);
                fw.flush();
                fw.close();
                //3. 编译源代码,并且生成.class文件
                JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
                StandardJavaFileManager manager = compiler.getStandardFileManager(null, null, null);
                //4. 将class文件中的内容,动态加载到jvm中
                Iterable iterable = manager.getJavaFileObjects(f);
                CompilationTask task = compiler.getTask(null, manager, null, null, null, iterable);
                task.call();
                manager.close();
                //5. 返回被代理后的对象
                Class proxyClass = loader.findClass("$Proxy0");
                Constructor c = proxyClass.getConstructor(MyInvocationHandler.class);
                f.delete();
                return c.newInstance(h);
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            return null;
        }
    
        private static String generateSrc(Class<?> interfaces) {
            StringBuffer src = new StringBuffer();
            src.append("package com.yding.pattern.proxy.customer;" + ln);
            src.append("import java.lang.reflect.Method;" + ln);
            src.append("public class $Proxy0 implements " + interfaces.getName() + "{ "+ ln);
            
            src.append("MyInvocationHandler h;" + ln);
            src.append("public $Proxy0(MyInvocationHandler h){" + ln);
            src.append("this.h = h;" + ln);
            src.append("}" +ln);
            
            for(Method m : interfaces.getMethods()){
                src.append("public " + m.getReturnType().getName() + " " + m.getName() + "()" + "{" + ln);
                
                src.append("try{" + ln);
                
                //输出双引号 """
                src.append("Method m = " + interfaces.getName()+".class.getMethod("" + m.getName() + "",new Class[]{});"+ln);
                src.append("this.h.invoke(this,m,null);" + ln);
                
                src.append("}catch(Throwable e){e.printStackTrace();}" + ln);
                
                src.append("}" + ln);
            }
            src.append("}");
            return src.toString();
        }
    
    }

          2. MyClassLoader(),有一个方法,findClass(String className)

    import java.io.ByteArrayOutputStream;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.IOException;
    
    public class MyClassLoader extends ClassLoader {
        private File baseDir;
    
        public MyClassLoader() {
            // TODO Auto-generated constructor stub
            String baseDir = MyClassLoader.class.getResource("").getPath();
            this.baseDir = new java.io.File(baseDir);
        }
    
        @Override
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            String className = MyClassLoader.class.getPackage().getName() + "." + name;
            if (baseDir != null) {
                File classFile = new File(baseDir, name.replaceAll("\.", "/") + ".class");
                System.out.println("class File :  " + classFile);
                System.out.println(classFile.exists());
                if (classFile.exists()) {
                    FileInputStream in = null;
                    ByteArrayOutputStream out = null;
                    try {
                        in = new FileInputStream(classFile);
                        out = new ByteArrayOutputStream();
                        byte[] buff = new byte[1024];
                        int len;
                        while ((len = in.read(buff)) != -1) {
                            out.write(buff, 0, len);
                        }
                        return defineClass(className, out.toByteArray(), 0, out.size());
                    } catch (Exception e) {
                        e.printStackTrace();
                    } finally {
                        if (in != null) {
                            try {
                                in.close();
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                        if (out != null) {
                            try {
                                out.close();
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                        classFile.delete();
                    }
                }
            }
            return null;
        }
    }

          3. MyInvocationHandler

    /**
     * InvocationHandler
     * @author Rick
     *
     */
    public interface MyInvocationHandler {
        //接口的方法invoke
         public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
    
    }

          4. 用新定义的类再来执行代码:

    public class TestSearchHouse {
        public static void main(String[] args) {
            Person person = (Person) new HomeLink().getInstance(new Master());
            System.out.println(person.getClass());
            person.searchHouse();
        }
    }

      

      

  • 相关阅读:
    Eclipse背景颜色设置
    SQL ROW_NUMBER() OVER函数的基本用法用法
    hdu 2844 Coins 多重背包问题
    VC++学习/MFC (1)
    java学习 (1)
    hdu 1506 City Game 二维的多重背包
    java学习(2)
    VC++学习/MFC (2)
    hdu 1506 Largest Rectangle in a Histogram
    hdu 1171 Big Event in HDU
  • 原文地址:https://www.cnblogs.com/lfdingye/p/7717063.html
Copyright © 2020-2023  润新知