02、动态代理--CGLib动态代理

一、概述

  • CGLIB(Code Generation Library)是一个开源项目,是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。例如:Hibernate用它来实现PO(Persistent Object 持久化对象)字节码的动态生成。
  • CGLib动态代理采用底层字节码技术,可以为没有实现接口的类动态创建子类(即代理类是被代理类的子类),在子类中采用方法拦截的技术拦截所有被代理类方法的调用并顺势织入横切逻辑。
  • CGLib动态代理主要涉及 Enhancer 和 MethodIntercepter 两个类。
  • 动态代理(JDK动态代理和CGLib动态代理)是实现AOP的绝好底层技术。

二、实例

package com.offer.note.Java基础.动态代理.CGLib动态代理;

public interface Browser {
    void visitInternet();
}

 

package com.offer.note.Java基础.动态代理.CGLib动态代理;

/**
 * 目标类:被代理类
 *
 * @author: xueguanfeng
 * @date: 2018-05-15 09:57
 */
public class ChromeBrowser implements Browser {
    @Override
    public void visitInternet() {
        System.out.println("visit YouTube");
    }

    //非接口方法
    public void listenToMusic() {
        System.out.println("listen to Cranberries");
    }
}

 

package com.offer.note.Java基础.动态代理.CGLib动态代理;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * CGLib动态代理
 * MethodInterceptor是方法拦截器
 * MethodInterceptor:这里的MethodInterceptor是SpringAOP的,而不是aopalliance的
 *
 * @author: xueguanfeng
 * @date: 2018-05-15 12:51
 */
public class CglibBrowserProxy implements MethodInterceptor {

    private static CglibBrowserProxy proxy = new CglibBrowserProxy();

    private CglibBrowserProxy() {

    }

    public static CglibBrowserProxy getInstance() {
        return proxy;
    }

    ////////////////////////////////以上是单例模式的实现/////////////////////////////////////

    public <T> T getProxy(Class<T> clazz) {
        //clazz是原对象的Class对象,callback是回调方法接口
        return (T) Enhancer.create(clazz, this);
    }

    /**
     *
     * @param obj       是被代理的类的实例对象
     * @param method    是被代理类的目标方法
     * @param args      是目标方法的入参
     * @param proxy     动态生成的代理对象
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        encrypt();
        //通过动态生成的代理对象,调用被代理类的目标方法
        Object retVal = proxy.invokeSuper(obj, args);
        decrypt();
        return retVal;
    }

    ////////////////////////////////增强///////////////////////////////////////
    /**
     * 加密
     */
    private void encrypt() {
        System.out.println("encrypt ...");
    }

    /**
     * 解密
     */
    private void decrypt() {
        System.out.println("decrypt ...");
    }
}

 

package com.offer.note.Java基础.动态代理.CGLib动态代理;

/**
 * 测试类
 *
 * @author: xueguanfeng
 * @date: 2018-05-15 12:57
 */
public class CglibDynamicProxyTest {

    public static void main(String[] args) {
        //没有使用Browser接口来接受代理对象,而是直接使用ChromeBrowser对象
        //这样的方式就可以代理ChromeBrowser中未在Chrome接口中的方法,例如 listenToMusic()方法
        ChromeBrowser browser = CglibBrowserProxy.getInstance().getProxy(ChromeBrowser.class);
        browser.visitInternet();
        browser.listenToMusic();

        //动态生成的代理类的类名
        System.out.println("动态生成的代理类的类名:" + browser.getClass().getName());
        //动态生成的代理类的父类的类名
        System.out.println("动态生成的代理类的父类的类名:" + browser.getClass().getSuperclass().getName());

        //从运行结果可以看出,新生成的对象确实是原对象类的子类所产生的对象
    }
}

运行结果:

encrypt ...
visit YouTube
decrypt ...
encrypt ...
listen to Cranberries
decrypt ...
================================================================
动态生成的代理类的类名:com.offer.note.Java基础.动态代理.CGLib动态代理.ChromeBrowser$$EnhancerByCGLIB$$389ddb0f
动态生成的代理类的父类的类名:com.offer.note.Java基础.动态代理.CGLib动态代理.ChromeBrowser

 

三、源码解析

1、动态生成代理类的语句

 
//clazz是被代理类的Class对象,this是MethodInterceptor的实例
return (T) Enhancer.create(clazz, this);

2、最终动态生成代理类源码的语句

 

//AbstractClassGenerator抽象类的 Class generate(AbstractClassGenerator.ClassLoaderData data) 方法
protected
Class generate(AbstractClassGenerator.ClassLoaderData data) { Object save = CURRENT.get(); CURRENT.set(this); try { ClassLoader classLoader = data.getClassLoader(); if (classLoader == null) { throw new IllegalStateException("ClassLoader is null while trying to define class " + this.getClassName() + ". It seems that the loader has been expired from a weak reference somehow. Please file an issue at cglib's issue tracker."); } else { String className; synchronized(classLoader) { className = this.generateClassName(data.getUniqueNamePredicate()); data.reserveName(className); this.setClassName(className); } Class gen; if (this.attemptLoad) { try { gen = classLoader.loadClass(this.getClassName()); Class var25 = gen; return var25; } catch (ClassNotFoundException var20) { ; } }           //动态生成代理类源码 byte[] b = this.strategy.generate(this); className = ClassNameReader.getClassName(new ClassReader(b)); ProtectionDomain protectionDomain = this.getProtectionDomain(); synchronized(classLoader) { if (protectionDomain == null) { gen = ReflectUtils.defineClass(className, b, classLoader); } else { gen = ReflectUtils.defineClass(className, b, classLoader, protectionDomain); } } Class var8 = gen; return var8; } } catch (RuntimeException var21) { throw var21; } catch (Error var22) { throw var22; } catch (Exception var23) { throw new CodeGenerationException(var23); } finally { CURRENT.set(save); } }

 

package com.offer.note.Java基础.动态代理.CGLib动态代理;

/**
* 测试类
*
* @author: xueguanfeng
* @date: 2018-05-15 12:57
*/
public class CglibDynamicProxyTest {

public static void main(String[] args) {
//没有使用Browser接口来接受代理对象,而是直接使用ChromeBrowser对象
//这样的方式就可以代理ChromeBrowser中未在Chrome接口中的方法,例如 listenToMusic()方法
ChromeBrowser browser = CglibBrowserProxy.getInstance().getProxy(ChromeBrowser.class);
browser.visitInternet();
browser.listenToMusic();

//动态生成的代理类的类名
System.out.println("动态生成的代理类的类名:" + browser.getClass().getName());
//动态生成的代理类的父类的类名
System.out.println("动态生成的代理类的父类的类名:" + browser.getClass().getSuperclass().getName());

//从运行结果可以看出,新生成的对象确实是原对象类的子类所产生的对象
}
}
posted @ 2018-05-15 17:14  windjammer  阅读(170)  评论(0编辑  收藏  举报