【Java设计模式】代理模式

本文详细介绍了代理模式的概念及其在Java中的两种实现方式:静态代理和动态代理。通过代码示例展示了如何使用JDK动态代理和cglib动态代理创建代理对象,并对比了两者之间的区别。

Proxy Pattern

现实生活中,租房时的中介就相当于代理对象,房东相当于实际对象,我们相当于客户端,通过中介把房东的房子租下来;

代理的外部功能和实际对象一般是一样的,用户与代理打交道,不直接接触实际对象;

代理存在的价值:

  • 节省成本比较高的实际对象的创建开销,按需延迟加载;
  • 执行权限检查,代理检查权限后,再调用实际对象;
  • 屏蔽网络差异和复杂性,代理在本地,而实际对象在其他服务器上;

一、静态代理

/**
 * 静态代理示例
 */
public class SimpleStaticProxy {
    /**
     * 代理和实际对象共同的接口
     */
    static interface IService {
        void sayHello();
    }
    /**
     * 实际对象类
     */
    static class RealService implements IService {
        @Override
        public void sayHello() {
            System.out.println("Hello");
        }
    }
    /**
     * 代理对象类
     */
    static class ProxyService implements  IService {
        /**
         * IService的成员变量,指向实际对象
         */
        private IService realService;

        public ProxyService(IService realService) {
            this.realService = realService;
        }
        @Override
        public void sayHello() {
            System.out.println("enter sayHello");
            // 转发给了实际对象的sayHello方法
            this.realService.sayHello();
            System.out.println("leave sayHello");
        }
    }
    public static void main(String[] args) {
        IService realService = new RealService();
        IService proxyService = new ProxyService(realService);
        proxyService.sayHello();
    }
}
enter sayHello
Hello
leave sayHello

二、动态代理

2.1 JDK动态代理

2.1.1 JDK动态代理代码示例

JDK动态代理代码如下:

/**
 * JDK动态代理示例
 */
public class SimpleJdkDynamicProxy {
    /**
     * 代理和实际对象共同的接口
     */
    static interface IService {
        void sayHello();
    }
    /**
     * 实际对象类
     */
    static class RealService implements IService {
        @Override
        public void sayHello() {
            System.out.println("Hello");
        }
    }
    static class SimpleInvocationHandler implements InvocationHandler {
        private Object realObj;
        public SimpleInvocationHandler(Object realObj) {
            this.realObj = realObj;
        }
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("enter " + method.getName());
            Object result = method.invoke(realObj, args);
            System.out.println("leave " + method.getName());
            return result;
        }
    }
    public static void main(String[] args) {
        IService realService = new RealService();
        IService proxyService = (IService) Proxy.newProxyInstance(IService.class.getClassLoader(),
                new Class<?>[]{IService.class}, new SimpleInvocationHandler(realService));
        proxyService.sayHello();
    }
}

代理对象proxyService的创建方式,不再是静态代理中我们自己实现的代理类new出来的了,而是由Proxy类的静态方法newProxyInstance来创建的;

public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
                                        throws IllegalArgumentException

三个参数:

  1. loader为类加载器;
  2. interfaces为代理类要实现的接口列表,元素的类型只能是接口;
  3. h类型是InvocationHandler,是一个接口,只定义了一个invoke方法,对代理接口所有方法的调用都会转给该方法;

返回类型为Object,可以强制转换为interfaces数组中的某个接口类型,但不能强制转换为某个类类型;

SimpleInvocationHandler实现了InvocationHandler,构造方法接收一个参数realObj表示被代理的对象(即实际对象),invoke方法处理所有的接口调用,三个参数:

  1. proxy表示代理对象本身,不是被代理的对象,一般用处不大;
  2. method表示正在被调用的方法;
  3. args表示方法的参数;

不能将proxy作为参数传递给method.invoke(),否则会出现死循环;

2.1.2 动态代理基本原理

运行以下命令,将动态生成的代理类$Proxy0保存到$Proxy.class文件中:

java -Dsun.misc.ProxyGenerator.saveGeneratedFiles=true com.transformers.SimpleJdkDynamicProxy

JDK动态生成的代理类代码:

package com.transformers;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

final class $Proxy0
  extends Proxy
  implements SimpleJdkDynamicProxy.IService
{
  private static Method m1;
  private static Method m3;
  private static Method m2;
  private static Method m0;
  
  public $Proxy0(InvocationHandler paramInvocationHandler)
  {
    super(paramInvocationHandler);
  }
  
  public final boolean equals(Object paramObject)
  {
    try
    {
      return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }
  
  public final void sayHello()
  {
    try
    {
      this.h.invoke(this, m3, null);
      return;
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }
  
  public final String toString()
  {
    try
    {
      return (String)this.h.invoke(this, m2, null);
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }
  
  public final int hashCode()
  {
    try
    {
      return ((Integer)this.h.invoke(this, m0, null)).intValue();
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }
  
  static
  {
    try
    {
      m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
      m3 = Class.forName("com.transformers.SimpleJdkDynamicProxy$IService").getMethod("sayHello", new Class[0]);
      m2 = Class.forName("java.lang.Object").getMethod("toString", 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());
    }
  }
}

$Proxy0的父类是Proxy,有一个接收InvocationHandler类型的构造方法,保存为实例变量h;$Proxy0实现了IService接口,对于IService每个方法,都会调用h的invoke方法(这里只有sayHello);对于Object中的方法,同样转发给了InvocationHandler(这里有equals、toString、hashCode);

这个类本身与被代理对象没有关系,与InvocationHandler的具体实现也没有关系,而主要与接口数组有关!给定这个接口数组,动态创建每个接口的实现代码,其实现就是转发给InvocationHandler,与被代理对象的关系以及对它的调用由InvocationHandler的实现管理。

查看Proxy.newProxyInstance源码,JDK动态代理创建proxyService的代码可以用如下代码来代替:

Class<?> cl = Proxy.getProxyClass(IService.class.getClassLoader(), new Class<?>[]{IService.class});
Constructor<?> cons = cl.getConstructor(new Class<?>[] { InvocationHandler.class });
InvocationHandler handler = new SimpleInvocationHandler(realService);
IService proxyService = (IService) cons.newInstance(handler);
proxyService.sayHello();

创建过程分三步:

  1. 通过Proxy.getProxyClass创建代理类定义,类定义被缓存;
  2. 获取代理类的构造方法,构造方法中有一个InvocationHandler类型的参数;
  3. 创建InvocationHandler对象,创建代理类对象;

Proxy.getProxyClass需要两个参数,一个是类加载器ClassLoader,另一个是接口数组。该方法会动态生成一个类,类名以$Proxy开头,后跟一个数字,即上面的$Proxy0;

2.1.3 动态代理的优点

相比于静态代理,动态代理代码复杂且麻烦了许多,但是,使用动态代理,可以编写通用的代理逻辑,用于各种类型的被代理对象,而不需要为每个被代理的类型都创建一个静态代理类;

通用动态代理类示例:

public class GeneralProxy {
    static interface IServiceA {
        void a();
    }
    static class ServiceAImpl implements IServiceA {
        @Override
        public void a() {
            System.out.println("ServiceAImpl.a");
        }
    }
    static interface IServiceB {
        void b();
    }
    static class ServiceBImpl implements IServiceB {
        @Override
        public void b() {
            System.out.println("ServiceBImpl.b");
        }
    }
    static class SimpleInvocationHandler implements InvocationHandler {
        private Object realObj;
        public SimpleInvocationHandler(Object realObj) {
            this.realObj = realObj;
        }
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            Object result = null;
            System.out.println("enter " + realObj.getClass().getSimpleName() + "::" + method.getName());
            result = method.invoke(realObj, args);
            System.out.println("leave " + realObj.getClass().getSimpleName() + "::" + method.getName());
            return result;
        }
    }
    public static <T> T getProxy(Class<?> cls, T realObj) {
        return (T) Proxy.newProxyInstance(cls.getClassLoader(), new Class<?>[]{cls},
                new SimpleInvocationHandler(realObj));
    }
    public static void main(String[] args) {
        IServiceA realServiceA = new ServiceAImpl();
        IServiceB realServiceB = new ServiceBImpl();
        IServiceA proxyServiceA = getProxy(IServiceA.class, realServiceA);
        proxyServiceA.a();
        IServiceB proxyServiceB = getProxy(IServiceB.class, realServiceB);
        proxyServiceB.b();
    }
}
enter ServiceAImpl::a
ServiceAImpl.a
leave ServiceAImpl::a
enter ServiceBImpl::b
ServiceBImpl.b
leave ServiceBImpl::b

两个接口IServiceA和IServiceB,实现类分别是ServiceAImpl和ServiceBImpl,虽然接口和实现类不同,但利用动态代理,可以调用同样的getProxy获取代理对象,共享同样的代理逻辑SimpleInvocationHandler;

2.2 cglib动态代理

/**
 * cglib动态代理示例
 */
public class SimpleCGLibDemo {
    /**
     * 被代理的类
     */
    static class RealService {
        public void sayHello() {
            System.out.println("hello");
        }
    }
    static class SimpleInterceptor implements MethodInterceptor {
        @Override
        public Object intercept(Object object, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            System.out.println("enter " + method.getName());
            Object result = methodProxy.invokeSuper(object, objects);
            System.out.println("leave " + method.getName());
            return result;
        }
    }
    /**
     * 为一个类生成代理对象
     * @param cls
     * @param <T>
     * @return
     */
    private static <T> T getProxy(Class<T> cls) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(cls); // 设置被代理的类
        enhancer.setCallback(new SimpleInterceptor()); // 设置被代理类的public非final方法被调用时的处理类
        return (T) enhancer.create();
    }
    public static void main(String[] args) {
        RealService proxyService = getProxy(RealService.class);
        proxyService.sayHello();
    }
}

与JDK的InvocationHandler不同的是,SimpleInterceptor中没有被代理的对象,通过MethodProxy的invokeSuper方法调用被代理的方法:

Object result = methodProxy.invokeSuper(object, objects);

但是,不能使用如下的代码调用代理类的方法:

Object result = method.invoke(object, objects);

因为object是代理对象,调用这个方法还会调用到SimpleInterceptor的intercept方法,造成死循环;

在main方法中,没有创建被代理的对象;

cglib实现机制是通过继承实现的,动态创建了一个类,这个类的父类就是被代理的类,代理类重写了父类所有public非final方法,改为调用Callback中的相关方法,在示例代码中,调用了SimpleInterceptor的intercept方法;

3、JDK代理与cglib代理对比

JDK代理面向的是一组接口,为接口动态创建了一个实现类;接口的具体实现逻辑是通过自定义的InvocationHandler实现的,其背后不一定有真正被代理的对象,也可能有多个实际对象,根据情况动态选择;

cglib代理面向的是一个具体的类,动态创建一个新类,继承了该类,重写了其方法;

JDK代理的是对象,需要先有一个实际对象,自定义的InvocationHandler引用该对象,然后创建一个代理类和代理对象,客户端访问的是代理对象,代理对象最后再调用实际对象的方法;

cglib代理的是类,创建的对象只有一个;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值