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();
    }
}

  

  

原文地址:https://www.cnblogs.com/lfdingye/p/7717063.html