当前位置: 移动技术网 > IT编程>开发语言>Java > Java 动态代理与CGLIB详细介绍

Java 动态代理与CGLIB详细介绍

2019年07月22日  | 移动技术网IT编程  | 我要评论
静态代理模式 因为需要对一些函数进行二次处理,或是某些函数不让外界知道时,可以使用代理模式,通过访问第三方,间接访问原函数的方式,达到以上目的。 inter

静态代理模式

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

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动态代理一样面向接口,因为本质是继承。

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

如您对本文有疑问或者有任何想说的,请点击进行留言回复,万千网友为您解惑!

相关文章:

验证码:
移动技术网