动态代理解释-JDK,CGLIB,JAVASSIST,ASM

 

动态代理是指在运行时,动态生成代理类。代理类的字节码将在运行时生成并载入当前的ClassLoader.

         生成动态代理类的方法很多,如JDK自带的动态代理、CGLIB、Javassist或者ASM库。

          JDK动态代理使用简单,它内置在JDK中,因此不需要引入第三方Jar包,但相对功能比较弱。CGLIB和Javassist都是高级的字节码生成库,总体性能比JDK自带的动态代理好,而且功能十分强大。ASM是低级的字节码生成工具,使用ASM已经近乎在于使用Javabytecode编程,对开发人员要求较高,也是性能最好的一种动态代理生辰工具。但ASM的使用是在过于繁琐,而且性能也没有数量级的提升,与CGLIB等高级字节码生成工具相比,ASM程序的可维护性也较差。

 

JDK实现

1、步骤

 

1)通过实现InvocationHandler接口创建自己的调用处理器

2)通过为Proxy类指定ClassLoader对象和一组interface来创建动态代理类

3)通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型

4)通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入

 

2、创建代理

//InvocationHandlerImpl 实现了InvocationHandler接口,并能实现方法调用从代理类到委托类的分派转发  
//其内部通常包含指向委托类实例的引用,用于真正执行分派转发过来的方法调用  
InvocationHandler handler = new InvocaitonHandlerImpl(..);  
  
//通过Proxy为包括Interface接口在内的一组接口动态创建代理类的对象  
Class clazz = Proxy.getProxyClass(classLoader,new Class[]{Interface.class,...});  
  
//通过反射从生成的类对象获得构造函数对象  
       Constructor constructor = clazz.getConstructor(new Class[]{InvocationHandler.class});  
  
//通过构造函数对象创建动态代理类实例  
Interface Proxy = (Interface)constructor.newInstance(new Object[]{handler});  
  
//Proxy类的静态方法newProxyInstance对上面具体步骤的后三步做了封装,简化了动态代理对象的获取过程。  
//InvocationHandlerImpl实现了InvocaitonHandler接口,并能实现方法调用从代理类到委托类的分派转发  
InvocaitonHandler handler = new InvocationHandlerImpl(..);  
//通过Proxy直接创建动态代理类实例  
  
nterface proxy = (Interface)Proxy.newProxyInstance(classLoader,new Class[]{Interface.class},handler); 

 

3、代码

/** 
 * 接口 
 * @author Emily 
 * 
 */  
public interface IDBQuery {  
  
    String request();  
}  
  
/** 
 * 真实的实现类,具体的目标对象 
 * @author Emily 
 * 
 */  
public class DBQuery implements IDBQuery {  
  
      
    public DBQuery(){  
        try {  
            Thread.sleep(1000); //可能包含数据库连接等耗时操作  
        } catch (InterruptedException e) {  
            e.printStackTrace();  
        }  
    }  
      
    @Override   
    public String request() {  
          
        return "request string";  
    }  
  
}  
  
/** 
 * JDK动态代理的实现类 
 * @author Emily 
 * 
 */  
public class JdkDbQueryHandler implements InvocationHandler{  
  
    IDBQuery real = null; //主题接口  
  
    /** 
     * 生成Handler 
     */  
    @Override   
    public Object invoke(Object proxy, Method method, Object[] args)  
            throws Throwable {  
        if(real == null)  
            real = new DBQuery(); //如果是第一次调用,则生成真实对象  
        return real.request();  //使用真实主题完成实际的操作  
    }  
      
    /** 
     * 利用Handler生成动态代理对象 
     * @return 
     */  
    public static IDBQuery createJdkProxy(){  
          
        //根据指定的类加载器和接口以及截获器,返回代理类的一个实例对象  
        //ClassLoader loader :指定被代理对象的类加载器  
        //Class[] Interfaces :指定被代理对象所以事项的接口  
        //InvocationHandler h :指定需要调用的InvocationHandler对象  
        IDBQuery jdkProxy = (IDBQuery) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{IDBQuery.class}, new JdkDbQueryHandler());  
        return jdkProxy;  
    }  
      
      
} 

 

执行过程:

       如图,目标对象的方法调用被Proxy拦截,在InvocationHandler中的回调方法中通过反射调用,这种动态代理的方法实现了对类的方法的运行时修改。

        JDK动态代理有个缺点,那就是不能对类进行代理,只能对接口进行代理,如果我们的类没有实现任何接口,那么就不能使用这种方式进行动态代理(因为$Proxy()这个类集成了Proxy,Java的集成不允许出现多个父类)。准确的说这不应该是缺点,一个良好的系统,每个类都应该有一个接口与之对应。针对接口编程。

 

CGLIB实现

package com.ltt.dynamic;  
  
import java.lang.reflect.Method;  
  
import net.sf.cglib.proxy.Enhancer;  
import net.sf.cglib.proxy.MethodInterceptor;  
import net.sf.cglib.proxy.MethodProxy;  
  
/** 
 * CGLIB动态代理 
 * @author Emily 
 * 
 */  
public class CglibDbQueryInterceptor implements MethodInterceptor{  
    IDBQuery real = null;  
  
    /** 
     * 处理代理逻辑的切入类 
     */  
    @Override  
    public Object intercept(Object arg0, Method arg1, Object[] arg2,  
            MethodProxy arg3) throws Throwable {  
        if (real == null) {    //代理类 的内部逻辑  
            real = new DBQuery();  
            return real.request();  
        }  
        return null;  
    }  
      
    /** 
     * 生成动态代理 
     * @return 
     */  
    public static IDBQuery createCglibProxy(){  
        Enhancer enhancer = new Enhancer();  
        //指定切入器,定义代理类逻辑  
        enhancer.setCallback(new CglibDbQueryInterceptor());  
        //指定实现的接口  
        enhancer.setInterfaces(new Class[]{IDBQuery.class});  
          
        IDBQuery cglibProxy = (IDBQuery) enhancer.create();  
        return cglibProxy;  
    }  
      
  
} 

 

 

Javassist实现

       一种是使用代理工厂创建,另一种通过使用动态代码创建。使用代理工厂创建时,方法与CGLIB类似,也需要实现一个用于代理逻辑处理的Handler:例如createJavassistDynProxy();使用动态代码创建,生成字节码,这种方式可以非常灵活,甚至可以在运行时生成业务逻辑,如createJavassistBytecodeDynamicProxy()方法。

/** 
 * Javassist动态代理 
 * @author Emily 
 * 
 */  
public class JavassistDynDbQueryHandler implements MethodHandler{  
  
    IDBQuery real = null;  
  
    /* 
     * (non-Javadoc) 实现用于代理逻辑处理的Handler 
     * @see javassist.util.proxy.MethodHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.reflect.Method, java.lang.Object[]) 
     */  
    @Override  
    public Object invoke(Object arg0, Method arg1, Method arg2, Object[] arg3)  
            throws Throwable {  
        if (real == null)   
            real = new DBQuery();  
            return real.request();  
          
    }  
      
    /** 
     * 创建动态代理 
     * @return 
     * @throws Exception 
     */  
    public static IDBQuery createJavassistDynProxy() throws Exception{  
        ProxyFactory proxyFactory = new ProxyFactory();  
        proxyFactory.setInterfaces(new Class[]{IDBQuery.class});  //指定接口  
        Class proxyClass = proxyFactory.createClass();  
        IDBQuery javassistProxy = (IDBQuery) proxyClass.newInstance(); //设置Handler处理器  
        ((ProxyObject) javassistProxy).setHandler(new JavassistDynDbQueryHandler());  
        return javassistProxy;  
    }  
      
    /** 
     * 运行时生成业务逻辑 
     * @return 
     * @throws Exception 
     */  
    public static IDBQuery createJavassistBytecodeDynamicProxy() throws Exception{  
        ClassPool mPool = new ClassPool(true);  
        //定义类名  
        CtClass mCtc = mPool.makeClass(IDBQuery.class.getName() + "JavaassistBytecodeProxy");  
        //需要实现接口  
        mCtc.addInterface(mPool.get(IDBQuery.class.getName()));  
        //添加构造函数  
        mCtc.addConstructor(CtNewConstructor.defaultConstructor(mCtc));  
          
        //添加类的字段信息,使用动态Java代码  
        mCtc.addField(CtField.make("public" + IDBQuery.class.getName() + "real;", mCtc));  
        String dbQueryname = DBQuery.class.getName();  
        //添加方法,这里使用动态Java代码指定内部逻辑  
        mCtc.addMethod(CtNewMethod.make("public String request() { if(real==null) real = new " +dbQueryname+"(); return real.request();}", mCtc));  
          
        //基于以上信息,生成动态类  
        Class pc = mCtc.toClass();  
        //生成动态类的实例  
        IDBQuery bytecodeProxy = (IDBQuery) pc.newInstance();  
        return bytecodeProxy;  
    }  
      
} 

 


      三种动态代理:JDK动态代理创建速度优于CGLIB动态代理,但是在函数的调用性能上远不如CGLIBJavassist。故CGLIB和Javassist整体性能上比JDK动态代理好。

 

性能比较:

Create JDK Proxy: 13 ms    
Create CGLIB Proxy: 217 ms    
Create JAVAASSIST Proxy: 99 ms    
Create JAVAASSIST Bytecode Proxy: 168 ms    
================    
Run JDK Proxy: 2224 ms, 634,022 t/s    
Run CGLIB Proxy: 1123 ms, 1,255,623 t/s    
Run JAVAASSIST Proxy: 3212 ms, 438,999 t/s    
Run JAVAASSIST Bytecode Proxy: 206 ms, 6,844,977 t/s     
----------------    
Run JDK Proxy: 2169 ms, 650,099 t/s    
Run CGLIB Proxy: 1059 ms, 1,331,506 t/s    
Run JAVAASSIST Proxy: 3328 ms, 423,697 t/s    
Run JAVAASSIST Bytecode Proxy: 202 ms, 6,980,521 t/s     
----------------    
Run JDK Proxy: 2174 ms, 648,604 t/s    
Run CGLIB Proxy: 1032 ms, 1,366,342 t/s    
Run JAVAASSIST Proxy: 3119 ms, 452,088 t/s    
Run JAVAASSIST Bytecode Proxy: 207 ms, 6,811,910 t/s    
----------------

    

Javassist字节码最快,CGLIB次之,是JDK的两倍。

      它们都是通过字节码生成来实现动态代理的。只不过是它们生成的字节码不一样,像JDK,CGLIB都考虑了很多因素,以及继承或包装了自己的一些类,所以生成的字节码非常大,而我们很多时候用不上这些,手工生成的字节码非常小(Javassist是手工生成的字节码),所以速度快。

       另外,ASM也是手工生成的字节码,速度也很快,但是它没有一个数量级,通常情况下选用Javassist生成字节码的方式。

 

        另外,从严格意义上将Aspectj不能算动态代理,应该是静态代理,因为它采用的是编译器植入。用aspectj,需要写相应的xml,定义切面,织入点等,然后由aspectj的编译器来编译出新的字节码文件,这明显是静态代理。

 

下面附上jdk proxy内幕

 


public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
            throws IllegalArgumentException {
        //InvocationHandler不能为空,因为对代理对象的所有方法调用实际上都会委托到InvocationHandler的invoke方法,
        //这个我们后面通过查看产生的代理类的源代码便会一目了然
        if (h == null) {
            throw new NullPointerException();
        }

        //这个是核心的地方,通过提供的ClassLoader和interface列表来产生代理类,具体的实现可以参考getProxyClass这个方法的实现,
        //真正的工作是由sun.misc.ProxyGenerator这个类来完成的,可以google查看具体的逻辑.在我们的程序中通过设置
        //System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true")可以查看产生的类文件
        Class cl = getProxyClass(loader, interfaces);

        //因为代理类继承了Proxy类.而Proxy中定义了构造函数protected Proxy(InvocationHandler h),所以可以反射得到Constructer实例
        //创建代理对象
        try {
            Constructor cons = cl.getConstructor(constructorParams);
            return (Object) cons.newInstance(new Object[] { h });
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString());
        } catch (IllegalAccessException e) {
            throw new InternalError(e.toString());
        } catch (InstantiationException e) {
            throw new InternalError(e.toString());
        } catch (InvocationTargetException e) {
            throw new InternalError(e.toString());
        }
    }

 


public interface Subject {
    void pub(String key, String content);

    String sub(String key);
}


public class SimpleSubject implements Subject {
    private Map<String, String> msg = new ConcurrentHashMap<String, String>();

    public void pub(String key, String content) {
        System.out.println("pub msg: key is " + key + ", content is " + content);
        msg.put(key, content);
    }

    public String sub(String key) {
        if (msg.containsKey(key)) {
            String ret = msg.get(key);
            System.out.println("sub msg: key is " + key + ", result is " + ret);
            return ret;
        }

        return null;
    }

}


public class SubjectProxyFactory {
    //TODO: cache 
    public static Subject getSubject(final Subject realSubject) {
        return (Subject) Proxy.newProxyInstance(realSubject.getClass().getClassLoader(), new Class[] { Subject.class },
                new InvocationHandler() {

                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("\naction before method invocation....");
                        Object retVal = method.invoke(realSubject, args);
                        System.out.println("action after method invocation....\n");
                        return retVal;
                    }
                });
    }
}


public class Demo {
    public static void main(String[] args) {
        //设置此系统属性,以查看代理类文件
        System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        
        //创建真实对象
        Subject subj = new SimpleSubject();
        subj.pub("name", "kevin.fan");
        subj.sub("name");
        
        //创建代理对象
        Subject proxy = SubjectProxyFactory.getSubject(subj);
        proxy.pub("hobby", "r&b music");
        proxy.sub("name");
    }
}

 

pub msg: key is name, content is kevin.fan
sub msg: key is name, result is kevin.fan

action before method invocation....
pub msg: key is hobby, content is r&b music
action after method invocation....

action before method invocation....
sub msg: key is name, result is kevin.fan
action after method invocation….

 


//这里很清楚了,代理类继承了Proxy类,并且实现了Proxy.newProxyInstance这个方法中传入的接口
public final class $Proxy0 extends Proxy
  implements Subject
{
  
  //这些方法在下面的static init block中进行初始化
  private static Method m4;
  private static Method m1;
  private static Method m3;
  private static Method m0;
  private static Method m2;
  
  static
  {
    try
    {
      m4 = Class.forName("com.aliyun.demo.kevin.coder.lang.proxy.Subject").getMethod("sub", new Class[] { Class.forName("java.lang.String") });
      m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
      m3 = Class.forName("com.aliyun.demo.kevin.coder.lang.proxy.Subject").getMethod("pub", new Class[] { Class.forName("java.lang.String"), Class.forName("java.lang.String") });
      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());
    }
  }

  //构造函数,接收一个 InvocationHandler作为参数,这就是为什么Proxy.newProxyInstance方法里可以
  //通过InvocationHandler实例作为参数来反射获取Constructer实例 
  public $Proxy0(paramInvocationHandler)
    throws 
  {
    super(paramInvocationHandler);
  }

  //下面通过这个来看下代理对象中方法是怎样调用的
  public final String sub(String paramString)
    throws 
  {
    try
    {
       //全部是通过调用InvocationHandler的invoke方法,传入对应的方法和参数
      return (String)this.h.invoke(this, m4, new Object[] { paramString });
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  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 pub(String paramString1, String paramString2)
    throws 
  {
    try
    {
      this.h.invoke(this, m3, new Object[] { paramString1, paramString2 });
      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 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);
    }
  }

}

 

Spring AOP里最重要的两个概念是增强(Advice)和切面(Advisor),增强是织入到目标类连接点上的一段程序代码,切面决定要给什么类的什么方法实施增强。是怎么做到对特定方法实施增强的呢?对invoke方法中的method#name进行判断。


public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("\naction before method invocation....");
                        Object retVal = method.invoke(realSubject, args);
                        System.out.println("action after method invocation....\n");
                        return retVal;
                    }

    Spring提供了ProxyFactoryBean创建代理,及利用BeanPostProcessor实现自动创建代理。

 

    Spring是在运行期利用JDK或CGLib创建代理,我们还可以在类加载期间通过字节码编辑的技术,将切面织入到目标类中,这种织入方式称为LTW(Load Time Weaving)。Spring为LTW的过程提供了细粒度的控制,它支持在单个ClassLoader范围内实施类文件转换,且配置更为简单。

 

附小知识:

Ref ref = new RefImpl(); 
System.out.println(ref.getClass().getSimpleName()); //RefImpl类型 
ref.f(); //调用接口方法 
// ref.g(); //向上转型后实现类添加的方法不能调用 
if(ref instanceof RefImpl){ 
RefImpl ref1 = (RefImpl)ref; //类型识别后转型 
ref1.g("zhouyang"); 
// ref1.w(); //私有方法不能访问 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
CGlib是一个针对Java字节码的代码生成库,它可以在运行时扩展Java类和实现接口的功能。它通过生成目标类的子类来实现代理,从而实现了动态代理的功能。 在使用CGlib动态代理时,首先需要添加CGlib库的依赖。然后,创建一个Enhancer对象,并设置被代理类作为目标类。接下来,可以通过调用Enhancer对象的方法来设置回调函数和拦截器等。最后,使用Enhancer对象的create方法生成代理类的实例。 以下是一个简单的示例代码,演示了如何使用CGlib动态代理: ```java import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; public class Main { public static void main(String[] args) { // 创建Enhancer对象 Enhancer enhancer = new Enhancer(); // 设置被代理类 enhancer.setSuperclass(TargetClass.class); // 设置回调函数 enhancer.setCallback(new MethodInterceptor() { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { // 在方法调用前进行拦截处理 System.out.println("Before method: " + method.getName()); // 调用被代理类的方法 Object result = proxy.invokeSuper(obj, args); // 在方法调用后进行拦截处理 System.out.println("After method: " + method.getName()); return result; } }); // 生成代理类的实例 TargetClass proxy = (TargetClass) enhancer.create(); // 调用代理类的方法 proxy.doSomething(); } } class TargetClass { public void doSomething() { System.out.println("Doing something..."); } } ``` 在上面的示例中,我们创建了一个名为TargetClass的被代理类。然后使用CGlib动态代理生成了一个代理类的实例,并通过调用代理类的方法来间接调用被代理类的方法。在方法调用前后,我们可以通过设置回调函数来添加额外的处理逻辑。 需要注意的是,CGlib动态代理是通过继承来实现的,因此对于final类和方法无法进行代理。另外,由于CGlib是基于字节码操作实现的,所以在生成代理类的过程中会比较耗时。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

武汉红喜

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

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

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

打赏作者

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

抵扣说明:

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

余额充值