Java 动态代理与CGLIB详细介绍

静态代理模式

因为需要对一些函数进行二次处理,或是某些函数不让外界知道时,可以使用代理模式,通过访问第三方,间接访问原函数的方式,达到以上目的。

interface Hosee{
  String sayhi();
}

class Hoseeimpl implements Hosee{

  @Override
  public String sayhi()
  {
    return "Welcome oschina hosee's blog";
  }

}

class HoseeProxy implements Hosee{

  Hosee h;

  public HoseeProxy(Hosee h)
  {
    this.h = h;
  }

  @Override
  public String sayhi()
  {
    System.out.println("I'm proxy!");
    return h.sayhi();
  }

}

public class StaticProxy
{

  public static void main(String[] args)
  {
    Hoseeimpl h = new Hoseeimpl();
    HoseeProxy hp = new HoseeProxy(h);
    System.out.println(hp.sayhi());
  }

}

1.1 静态代理的弊端

如果要想为多个类进行代理,则需要建立多个代理类,维护难度加大。

仔细想想,为什么静态代理会有这些问题,是因为代理在编译期就已经决定,如果代理哪个发生在运行期,这些问题解决起来就比较简单,所以动态代理的存在就很有必要了。

2.动态代理

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

interface HoseeDynamic
{
  String sayhi();
}

class HoseeDynamicimpl implements HoseeDynamic
{
  @Override
  public String sayhi()
  {
    return "Welcome oschina hosee's blog";
  }
}

class MyProxy implements InvocationHandler
{
  Object obj;
  public Object bind(Object obj)
  {
    this.obj = obj;
    return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj
        .getClass().getInterfaces(), this);
  }
  @Override
  public Object invoke(Object proxy, Method method, Object[] args)
      throws Throwable
  {
    System.out.println("I'm proxy!");
    Object res = method.invoke(obj, args);
    return res;
  }
}

public class DynamicProxy
{
  public static void main(String[] args)
  {
    MyProxy myproxy = new MyProxy();
    HoseeDynamicimpl dynamicimpl = new HoseeDynamicimpl();
    HoseeDynamic proxy = (HoseeDynamic)myproxy.bind(dynamicimpl);
    System.out.println(proxy.sayhi());
  }
}

类比静态代理,可以发现,代理类不需要实现原接口了,而是实现InvocationHandler。通过

Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj
        .getClass().getInterfaces(), this);

来动态生成一个代理类,该类的类加载器与被代理类相同,实现的接口与被代理类相同。

通过上述方法生成的代理类相当于静态代理中的代理类。

这样就实现了在运行期才决定代理对象是怎么样的,解决了静态代理的弊端。

当动态生成的代理类调用方法时,会触发invoke方法,在invoke方法中可以对被代理类的方法进行增强。

通过动态代理可以很明显的看到它的好处,在使用静态代理时,如果不同接口的某些类想使用代理模式来实现相同的功能,将要实现多个代理类,但在动态代理中,只需要一个代理类就好了。

除了省去了编写代理类的工作量,动态代理实现了可以在原始类和接口还未知的时候,就确定代理类的代理行为,当代理类与原始类脱离直接联系后,就可以很灵活地重用于不同的应用场景中。

2.1 动态代理的弊端

代理类和委托类需要都实现同一个接口。也就是说只有实现了某个接口的类可以使用Java动态代理机制。但是,事实上使用中并不是遇到的所有类都会给你实现一个接口。因此,对于没有实现接口的类,就不能使用该机制。

而CGLIB则可以实现对类的动态代理

2.2 回调函数原理

上文说了,当动态生成的代理类调用方法时,会触发invoke方法。

很显然invoke方法并不是显示调用的,它是一个回调函数,那么回调函数是怎么被调用的呢?

上述动态代理的代码中,唯一不清晰的地方只有

Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj
        .getClass().getInterfaces(), this);

跟踪这个方法的源码,可以看到程序进行了验证、优化、缓存、同步、生成字节码、显示类加载等操作,前面的步骤并不是我们关注的重点,而最后它调用了

byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
        proxyName, interfaces);

该方法用来完成生成字节码的动作,这个方法可以在运行时产生一个描述代理类的字节码byte[]数组。

在main函数中加入

System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");

加入这句代码后再次运行程序,磁盘中将会产生一个名为”$Proxy().class”的代理类Class文件,反编译(反编译工具我使用的是 JD-GUI )后可以看见如下代码:

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 HoseeDynamic
{
 private static Method m1;
 private static Method m3;
 private static Method m0;
 private static Method m2;

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

 public final String sayhi()
  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 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 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);
  }
 }

 static
 {
  try
  {
   m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
   m3 = Class.forName("HoseeDynamic").getMethod("sayhi", new Class[0]);
   m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
   m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
   return;
  }
  catch (NoSuchMethodException localNoSuchMethodException)
  {
   throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
  }
  catch (ClassNotFoundException localClassNotFoundException)
  {
   throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
  }
 }
}

动态代理类不仅代理了显示定义的接口中的方法,而且还代理了java的根类Object中的继承而来的equals()、hashcode()、toString()这三个方法,并且仅此三个方法。

可以在上述代码中看到,无论调用哪个方法,都会调用到InvocationHandler的invoke方法,只是参数不同。

2.3 动态代理与静态代理的区别

Proxy类的代码被固定下来,不会因为业务的逐渐庞大而庞大;

可以实现AOP编程,这是静态代理无法实现的;

解耦,如果用在web业务下,可以实现数据层和业务层的分离。

动态代理的优势就是实现无侵入式的代码扩展。 静态代理这个模式本身有个大问题,如果类方法数量越来越多的时候,代理类的代码量是十分庞大的。所以引入动态代理来解决此类问题

3. CGLIB

cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。

import java.lang.reflect.Method;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

class CGlibHosee
{
  public String sayhi()
  {
    return "Welcome oschina hosee's blog";
  }
}

class CGlibHoseeProxy
{
  Object obj;

  public Object bind(final Object target)
  {
    this.obj = target;
    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(obj.getClass());
    enhancer.setCallback(new MethodInterceptor()
    {
      @Override
      public Object intercept(Object obj, Method method, Object[] args,
          MethodProxy proxy) throws Throwable
      {
        System.out.println("I'm proxy!");
        Object res = method.invoke(target, args);
        return res;
      }
    });
    return enhancer.create();
  }

}

public class CGlibProxy
{
  public static void main(String[] args)
  {
    CGlibHosee cGlibHosee = new CGlibHosee();
    CGlibHoseeProxy cGlibHoseeProxy = new CGlibHoseeProxy();
    CGlibHosee proxy = (CGlibHosee) cGlibHoseeProxy.bind(cGlibHosee);
    System.out.println(proxy.sayhi());
  }
}

cglib需要指定父类和回调方法。当然cglib也可以与Java动态代理一样面向接口,因为本质是继承。

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

(0)

相关推荐

  • java 代理模式及动态代理机制深入分析

    java 代理模式及动态代理机制深入分析 代理设计模式 代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问.代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理. 代理模式的作用是:为其他对象提供一种代理以控制对这个对象的访问.在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用. 代理模式一般涉及到的角色有: 抽象角色:声明真实对象和代理对象的共同接口: 代理角色:代理对象角色内

  • java 动态代理的方法总结

    java 动态代理的方法总结 AOP的拦截功能是由java中的动态代理来实现的.说白了,就是在目标类的基础上增加切面逻辑,生成增强的目标类(该切面逻辑或者在目标类函数执行之前,或者目标类函数执行之后,或者在目标类函数抛出异常时候执行.不同的切入时机对应不同的Interceptor的种类,如BeforeAdviseInterceptor,AfterAdviseInterceptor以及ThrowsAdviseInterceptor等). 那么动态代理是如何实现将切面逻辑(advise)织入到目标类

  • JAVA动态代理模式(从现实生活角度理解代码原理)

    所谓动态代理,即通过代理类:Proxy的代理,接口和实现类之间可以不直接发生联系,而可以在运行期(Runtime)实现动态关联. java动态代理主要是使用java.lang.reflect包中的两个类. InvocationHandler类 public Object invoke(Object obj,Method method,Object[] obs) 其中第一个参数 obj 指的是代理类,method是被代理的方法,obs是指被代理的方法的参数组.此方法由代理类来实现. Proxy类

  • 详解java JDK 动态代理类分析(java.lang.reflect.Proxy)

    详解java JDK 动态代理类分析(java.lang.reflect.Proxy) /** * JDK 动态代理类分析(java.lang.reflect.Proxy使用) * * @author 张明学 * */ public class ProxyStudy { @SuppressWarnings("unchecked") public static void main(String[] args) throws Exception { // 动态代理类:通用指定类加载器,和接

  • java 1.8 动态代理源码深度分析

    JDK8动态代理源码分析 动态代理的基本使用就不详细介绍了: 例子: class proxyed implements pro{ @Override public void text() { System.err.println("本方法"); } } interface pro { void text(); } public class JavaProxy implements InvocationHandler { private Object source; public Jav

  • Java动态代理分析及理解

    Java动态代理分析及理解 代理设计模式 定义:为其他对象提供一种代理以控制对这个对象的访问. 动态代理使用 java动态代理机制以巧妙的方式实现了代理模式的设计理念. 代理模式示例代码 public interface Subject { public void doSomething(); } public class RealSubject implements Subject { public void doSomething() { System.out.println( "call

  • java 中动态代理(JDK,cglib)实例代码

    java 动态代理实例详解 1.jdk动态代理 /** * */ package com.sinosoft; /** *接口:编写一个委托类的接口,即静态代理的(Apple接口) * */ public interface Apple { public void phoneCall(); } /** * */ package com.sinosoft; /** * 实现一个真正的委托类,即静态代理的(AppleImpl类) * */ public class AppleImpl implemen

  • Java静态代理和动态代理总结

    静态代理 第一种实现(基于接口): 1>接口 public interface Hello { void say(String msg); } 2>目标类,至少实现一个接口 public class HelloImpl implements Hello { public void say(String msg) { System.out.println("Hi,"+msg); } } 3>代理类(与目标类实现相同接口,从而保证功能一致) public class He

  • java 中动态代理详解及实例

    Java动态代理相关 先来看静态代理模式代码: package test; public interface Subject { public void doSomething(); } package test; public class RealSubject implements Subject{ public void doSomething() { System.out.println( "call doSomething()" ); } } package test; pu

  • Java动态代理分析及简单实例

      Java动态代理 要想了解Java动态代理,首先要了解什么叫做代理,熟悉设计模式的朋友一定知道在Gof总结的23种设计模式中,有一种叫做代理(Proxy)的对象结构型模式,动态代理中的代理,指的就是这种设计模式. 在我看来所谓的代理模式,和23种设计模式中的"装饰模式"是一个东西.23种设计模式中将它们作为两种模式,网上也有些文章讲这两种模式的异同,从细节来看,确实可以人为地区分这两种模式,但是抽象到一定高度后,我认为这两种模式是完全一样的.因此学会了代理模式,也就同时掌握了装饰模

  • Java 动态代理深入理解

    要想了解Java动态代理,首先要了解什么叫做代理,熟悉设计模式的朋友一定知道在Gof总结的23种设计模式中,有一种叫做代理(Proxy)的对象结构型模式,动态代理中的代理,指的就是这种设计模式. 在我看来所谓的代理模式,和23种设计模式中的"装饰模式"是一个东西.23种设计模式中将它们作为两种模式,网上也有些文章讲这两种模式的异同,从细节来看,确实可以人为地区分这两种模式,但是抽象到一定高度后,我认为这两种模式是完全一样的.因此学会了代理模式,也就同时掌握了装饰模式. 代理模式 代理模

  • 深入理解java动态代理机制

    retrofit是一个解耦性非常高的网络请求框架,最近在研究的时候发现了动态代理这个非常强大且实用的技术,这篇文章将作为retrofit的前置知识,让大家认识:动态代理有哪些应用场景,什么是动态代理,怎样使用,它的局限性在什么地方? 动态代理的应用场景 1. AOP-面向切面编程,程序解耦 简言之当你想要对一些类的内部的一些方法,在执行前和执行后做一些共同的的操作,而在方法中执行个性化操作的时候--用动态代理.在业务量庞大的时候能够降低代码量,增强可维护性. 2. 想要自定义第三放类库中的某些方

  • 深度剖析java中JDK动态代理机制

    摘要 相比于静态代理,动态代理避免了开发人员编写各个繁锁的静态代理类,只需简单地指定一组接口及目标类对象就能动态的获得代理对象. 代理模式 使用代理模式必须要让代理类和目标类实现相同的接口,客户端通过代理类来调用目标方法,代理类会将所有的方法调用分派到目标对象上反射执行,还可以在分派过程中添加"前置通知"和后置处理(如在调用目标方法前校验权限,在调用完目标方法后打印日志等)等功能. 使用动态代理的五大步骤 1.通过实现InvocationHandler接口来自定义自己的Invocati

随机推荐