代理模式

1.静态代理

如下图所示(来自Head First设计模式)


proxy.PNG

示例:

 /**
     * 为 Proxy和 被Proxy控制和访问的对象 提供的接口,通过实现同一个接口,
     * Proxy可以取代被Proxy控制和访问的对象
     * (Subject)
     */
    interface Human{
        void sayHello();
    }

    /**
     * 代理类(Proxy)
     */
    class Proxy implements Human{
        private Human human;

        public Proxy(Human human) {
            this.human = human;
        }

        @Override
        public void sayHello() {
            human.sayHello();
        }
    }

    /**
     * 被Proxy控制和访问的对象(RealSubject)
     */
    class Chinese implements Human{

        @Override
        public void sayHello() {
            System.out.println("你好!");
        }
    }

    /**
     * 被Proxy控制和访问的对象(RealSubject)
     */
    class England implements Human{
        @Override
        public void sayHello() {
            System.out.println("Hello!");
        }
    }
    
    @Test
    public void test(){
        Proxy proxy = new Proxy(new Chinese());
        proxy.sayHello();
        Proxy proxy2 = new Proxy(new England());
        proxy2.sayHello();
    }

总结:
(1)代理使客户端不需要知道实现类是什么,客户端只需知道代理即可(解耦合)。
(2)但是一个代理对象只能服务于一种类型,比如上面的例子,proxy只能代理Human。而且由于被代理类和代理类实现了同一个接口,当该接口增加删除方法时,被代理类和代理类也需要修改,增加了代码维护复杂度。

2.JDK动态代理

如下图所示(来自Head First设计模式)


proxy.PNG

Proxy类不再持有被代理类(RealSubject)引用,但持有InvocationHandler的引用。
示例:

//===============动物类型================

    interface Animal {
        String getName();
    }

    class AnimalImpl implements Animal {

        private String name;

        public AnimalImpl(String name) {
            this.name = name;
        }

        @Override
        public String getName() {
            return name;
        }

        @Override
        public String toString() {
            return "AnimalImpl{" +
                    "name='" + name + '\'' +
                    '}';
        }
    }

    //===========日志类型===============

    interface Logger{
        void log(String msg);
    }

    class LoggerImpl implements Logger{
        @Override
        public void log(String msg) {
            System.out.println("========"+msg+"=========");
        }
    }

    //===========代理类================
    class MyInvocationHandler implements InvocationHandler {

        Object targetObject;

        public Object newProxyInstance(Object object){
            this.targetObject = object;
            return Proxy.newProxyInstance(object.getClass().getClassLoader()
                    ,object.getClass().getInterfaces(),this);
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            return method.invoke(targetObject,args);
        }
    }


    @org.junit.Test
    public void test(){
        MyInvocationHandler myInvocationHandler = new MyInvocationHandler();
        //test animal
        Animal animal = (Animal) myInvocationHandler.newProxyInstance(new AnimalImpl("Doggy"));
        System.out.println(animal.getName());
        //test logger
        Logger logger = (Logger) myInvocationHandler.newProxyInstance(new LoggerImpl());
        logger.log("test logger");
        //生成动态代理Animal的class文件
        ProxyUtil.generateClassFile(ani.getClass(),"AnimalProxyClass");
    }

看下Proxy.newProxyInstance具体是怎么实现的呢?为了方便理解,下面摘改自源码:

    public static Object newProxyInstance(ClassLoader loader,
                                          Class[] interfaces,
                                          InvocationHandler h)
    {
        //1.动态创建实现了 Interfaces接口的代理类
        Class cl = getProxyClass0(loader, interfaces);
        //2.通过反射从生成的类对象获得构造函数对象  
        Constructor cons = cl.getConstructor(InvocationHandler.class);
        //3.通过构造函数对象创建动态代理类实例
        cons.newInstance(new Object[]{h})
    }

通过ProxyGenerator.generateProxyClass()方法生成动态代理Animal的class文件保存到本地(工具代码在文章底部),反编译看到:
(1)代理类继承了java.lang.reflect.Proxy类并且实现被代理的接口
(2)代理类的方法使用了final修饰
(3)代理类的方法中调用了InvocationHandler的invoke方法

public final class AnimalProxyClass extends Proxy
  implements Animal
{
  private static Method m3;
  //方法是final修饰的
  public final String getName()
  {
      return ((String)this.h.invoke(this, m3, null));//调用Proxy父类持有的InvocationHandler的invoke方法
  }
  static
  {
      m3 = Class.forName("包路径.Animal").getMethod("getName", new Class[0]);//静态初始化,反射获取Animal接口的getName方法
  }
}

上面的例子中一个代理InvocationHandler代理了两种类型
来看下InvocationHandler的invoke()是如何被调用的:
当获取了MyInvocationHandler 后强转为Logger,调用log(),logger 会接着调用invoke方法。

pic.PNG

总结:
JDK动态代理能够代理各种类型的对象,但是只能对实现了接口的类生成代理,而不能针对类,也就是说生成的代理类只能代理某个类接口定义的方法。可以使用cglib代理没有接口的类。(深入理解CGLIB动态代理机制)

public class ProxyUtil {

    /*
     * 将根据类信息 动态生成的二进制字节码保存到硬盘中,
     * 默认的是clazz目录下
     * params :clazz 需要生成动态代理类的类
     * proxyName : 为动态生成的代理类的名称
     */
    public static void generateClassFile(Class clazz, String proxyName) {
        //根据类信息和提供的代理类名称,生成字节码
        byte[] classFile = ProxyGenerator.generateProxyClass(proxyName, clazz.getInterfaces());
        String paths = clazz.getResource(".").getPath();
        System.out.println(paths);
        FileOutputStream out = null;
        try {
            //保留到硬盘中
            out = new FileOutputStream(paths + proxyName + ".class");
            out.write(classFile);
            out.flush();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

参考:
[1]Head First设计模式

你可能感兴趣的:(代理模式)