java 代理模式使用与原理

概念:

通俗来讲:中介、黄牛等都是一种代理,他们有你的信息,来为你提供额外的支持。映射到程序上来讲就是代理对象持有被代理对象的引用,以控制对这个对象的访问。

代理类和委托类有共同的父类或父接口,这样在任何使用委托类对象的地方都可以用代理对象替代。代理类负责请求的预处理、过滤、将请求分派给委托类处理、以及委托类执行完请求后的后续处理。 

代理分为静态代理和动态代理

静态代理

静态就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了

租客接口 

public interface Renter {

    /**
     * 租房
     */
    void rentHouse();
}

北京租客(被代理对象):

public class BeiJingRenter implements Renter {

    private String name;

    @Override
    public void rentHouse() {
        System.out.println("我是:"+name);
        System.out.println("我租房的要求是一个卧室即可");
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

代理类(持有被代理对象的引用):

public class StaticProxy implements Renter {

    private BeiJingRenter beiJingRenter;

    public StaticProxy(BeiJingRenter beiJingRenter) {
        this.beiJingRenter = beiJingRenter;
    }

    @Override
    public void rentHouse() {
        System.out.println("我是中介");
        beiJingRenter.rentHouse();
        System.out.println("找到房子");
    }


    public static void main(String[] args) {
        BeiJingRenter beiJingRenter = new BeiJingRenter("zhangsan");
        StaticProxy staticProxy = new StaticProxy(beiJingRenter);
        staticProxy.rentHouse();
    }
}

执行结果:这样就可以在不修改被代理对象的同时对其进行前后置的扩展。

静态代理类优缺点 

  • 优点:
  1. 业务类只需要关注业务逻辑本身,保证了业务类的重用性。这是代理的共有优点。
  • 缺点:
  1. 代理对象的一个接口只服务于一种类型的对象,如果要代理的方法很多,势必要为每一种方法都进行代理,静态代理在程序规模稍大时就无法胜任了。 
  2. 如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。 

动态代理:

动态代理类的源码是在程序运行期间由JVM根据反射等机制动态的生成,所以不存在代理类的字节码文件。代理类和委托类的关系是在程序运行时确定。 

动态代理分为两种:jdk的动态代理和cglib动态代理

jdk动态代理:

还是以上面的租客举例子,下面是jdk动态代理下代理对象的实现:

public class Intermediary implements InvocationHandler {

    /**
     * 被代理对象的引用,这里指的是需要帮忙租房的
     */
    private Renter renter;

    public Object newInstance(Renter renter){
        this.renter = renter;
        Class clazz = renter.getClass();
        return Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
    }


    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("我是中介,我要帮忙找房子");
        method.invoke(renter,args);
        System.out.println("找完了");
        return null;
    }
}

测试:

public static void main(String[] args) throws Exception {
        BeiJingRenter beiJingRenter = new BeiJingRenter("zhangsan");
        Intermediary intermediary = new Intermediary();
        Renter renter = (Renter) intermediary.newInstance(beiJingRenter);
        renter.rentHouse();
    }

结果:

实现重点:
1 被代理对象需要有接口
2 代理对象需要持有被代理对象的引用
3 代理对象需要实现InvocationHandler 并重写它的invoke方法

InvocationHandler中有说明:当代理对象的某个方法被调用时,会调用invoke方法,通过反射来进行执行
* When a method is invoked on a proxy instance, the method
* invocation is encoded and dispatched to the {@code invoke}
* method of its invocation handler.

原理:
JDK Proxy 生成对象的步骤如下:

1、拿到被代理对象的引用,并且获取到它的所有的接口,反射获取。

2、JDK Proxy 类重新生成一个新的类、同时新的类要实现被代理类所有实现的所有的接 口。

3、动态生成 Java 代码,把新加的业务逻辑方法由一定的逻辑代码去调用(在代码中体 现)。

4、编译新生成的 Java 代码.class。

5、再重新加载到 JVM 中运行。
当调用代理对象的某个方法时,通过this.h.invoke(this, m5, null);从而定位到代理对象的invoke方法来通过反射进行执行,而我们要做的也仅仅是重写inoke方法即可实现动态代理的功能。

感兴趣的话可以看一下生成的代理类,通过下面的方法来生成(jdk8):

public static void writeClassToDisk(String path) {

        byte[] classFile = ProxyGenerator.generateProxyClass("$proxy4", new Class[]{BeiJingRenter.class});
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(path);
            fos.write(classFile);
            fos.flush();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

生成出来的代理类通过反编译查看,可以看到生成的代理类继承了proxy类,并实现了BeiJingRenter所实现的接口:

public final class $proxy4 extends Proxy
  implements BeiJingRenter
{
  private static Method m1;
  private static Method m4;
  private static Method m3;
  private static Method m10;
  private static Method m2;
  private static Method m5;
  private static Method m8;
  private static Method m7;
  private static Method m9;
  private static Method m11;
  private static Method m0;
  private static Method m6;

  public $proxy4(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 (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final void setName(String paramString)
    throws 
  {
    try
    {
      this.h.invoke(this, m4, new Object[] { paramString });
      return;
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final String getName()
    throws 
  {
    try
    {
      return (String)this.h.invoke(this, m3, null);
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final void notify()
    throws 
  {
    try
    {
      this.h.invoke(this, m10, null);
      return;
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final String toString()
    throws 
  {
    try
    {
      return (String)this.h.invoke(this, m2, null);
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final void rentHouse()
    throws 
  {
    try
    {
      this.h.invoke(this, m5, null);
      return;
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final void wait(long paramLong)
    throws InterruptedException
  {
    try
    {
      this.h.invoke(this, m8, new Object[] { Long.valueOf(paramLong) });
      return;
    }
    catch (Error|RuntimeException|InterruptedException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final void wait(long paramLong, int paramInt)
    throws InterruptedException
  {
    try
    {
      this.h.invoke(this, m7, new Object[] { Long.valueOf(paramLong), Integer.valueOf(paramInt) });
      return;
    }
    catch (Error|RuntimeException|InterruptedException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final Class getClass()
    throws 
  {
    try
    {
      return (Class)this.h.invoke(this, m9, null);
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final void notifyAll()
    throws 
  {
    try
    {
      this.h.invoke(this, m11, null);
      return;
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final int hashCode()
    throws 
  {
    try
    {
      return ((Integer)this.h.invoke(this, m0, null)).intValue();
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final void wait()
    throws InterruptedException
  {
    try
    {
      this.h.invoke(this, m6, null);
      return;
    }
    catch (Error|RuntimeException|InterruptedException 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") });
      m4 = Class.forName("com.xibei.designpattern.proxy.jdk.inner.BeiJingRenter").getMethod("setName", new Class[] { Class.forName("java.lang.String") });
      m3 = Class.forName("com.xibei.designpattern.proxy.jdk.inner.BeiJingRenter").getMethod("getName", new Class[0]);
      m10 = Class.forName("com.xibei.designpattern.proxy.jdk.inner.BeiJingRenter").getMethod("notify", new Class[0]);
      m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
      m5 = Class.forName("com.xibei.designpattern.proxy.jdk.inner.BeiJingRenter").getMethod("rentHouse", new Class[0]);
      m8 = Class.forName("com.xibei.designpattern.proxy.jdk.inner.BeiJingRenter").getMethod("wait", new Class[] { Long.TYPE });
      m7 = Class.forName("com.xibei.designpattern.proxy.jdk.inner.BeiJingRenter").getMethod("wait", new Class[] { Long.TYPE, Integer.TYPE });
      m9 = Class.forName("com.xibei.designpattern.proxy.jdk.inner.BeiJingRenter").getMethod("getClass", new Class[0]);
      m11 = Class.forName("com.xibei.designpattern.proxy.jdk.inner.BeiJingRenter").getMethod("notifyAll", new Class[0]);
      m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
      m6 = Class.forName("com.xibei.designpattern.proxy.jdk.inner.BeiJingRenter").getMethod("wait", new Class[0]);
      return;
    }
    catch (NoSuchMethodException localNoSuchMethodException)
    {
      throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
    }
    catch (ClassNotFoundException localClassNotFoundException)
    {
      throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
    }
  }
}

cglib动态代理

下面是cglib动态代理下代理对象的实现:

public class Intermediary implements MethodInterceptor {

    private Object target;

    public Object newInstance(Object target) throws Exception{
        this.target = target;

        Enhancer enhancer = new Enhancer();
        //设置生成的实例时继承了被代理对象
        enhancer.setSuperclass(this.target.getClass());
        enhancer.setCallback(this);
        return enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("我是中介,我要帮忙找房子");
        //执行代理对象父类的方法,即被代理对象的方法
        methodProxy.invokeSuper(o,args);
        System.out.println("找完了");
        return null;
    }
}

测试:

public static void main(String[] args) {
        try {
            BeiJingRenter beiJingRenter = (BeiJingRenter) (new Intermediary().newInstance(new BeiJingRenter("zhangsan")));
            beiJingRenter.rentHouse();
        }catch (Exception e){
            e.printStackTrace();
        }
    }

结果:

实现重点:
1 被代理对象可以被继承,final方法不可以被代理
2 代理对象需要持有被代理对象的引用
3 代理对象需要实现MethodInterceptor 并重写它的intercept方法

原理:
Proxy根据被代理对象生成一个代理类,该类继承了被代理对象,并重写了他的所有方法。

注:可以用过在main方法中加入下面这句话将生成的代理对象cglib的代理类生成到该目录下。

            System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"C://cglib_proxy_classes");

如有有兴趣的话可以看一下生成的代理类。这儿先不做研究。

jdk动态代理与cglib动态代理对比

1.JDK动态代理是实现了被代理对象的接口,CGLib是继承了被代理对象。
2.JDK和CGLib都是在运行期生成字节码, JDK是直接写Class字节码, CGLib使用ASM
框架写Class字节码,Cglib代理实现更复杂,生成代理类比JDK效率低。
3.JDK调用代理方法,是通过反射机制调用, CGLib是通过FastClass机制直接调用方法,
CGLib执行效率更高。
 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

每年进步一点点

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值