cglib简介与callback解析

简介

cglib是一套java动态代理实现框架,cglib被应用到spring app,hibernate等高级业务框架,spring事务在业务实现类未实现接口的情况下也会使用该技术。

实际上,cglib基于继承实现,这也就意味着final,private相关的method无法被代理(在《Spring AOP 实现原理》有示例)。基于asm框架对class字节码编辑改动,从而达到动态代理的目的,总之,被代理类没有实现接口的情况下cglib为首选

引用官网介绍的一段话:

字节码生成库是用于生成和转换JAVA字节码的高级API。AOP,测试,数据访问框架使用它来生成动态代理对象并拦截字段访问。

简单的来说就是使用字节码处理框架ASM来转换字节码并生成新的需要代理类的子类,其核心就是通过继承这一个面向对象的特点来完成代理,子类需重写被代理的父类中所有非final的方法。同时在子类中采用方法拦截的技术拦截所有父类方法的调用, 顺势织入横切逻辑, 比JDK动态代理要快。但缺点是对于fianl方法无法进行代理。

实现动态代理的几种方案

  • javasisit
  • jdk 动态代理
  • cglib
  • asm(cglib 内部也是用asm更改其字节码)

cglib核心类:Enhancer

Enhancer是cglib的字节码增群器, 可以方便的对final类进行扩展, 它动态创建了给定类型的子类,但是拦截了所有非final方法。

callbacks简介

这里的callback可以认为是cglib用于生成字节码的实现手段,cglib一共实现了6种callback,用于对代理类目标进行不同手段的代理,非常灵活,分别为:

  • MethodInterceptor:是一个功能强大的接口,类似于AOP中的环绕增强
  • FixedValue:是可以让我们很方便的替换掉源方法的返回值
  • InvocationHandler:需要实现InvocationHandler接口,实现invoke对象,该拦截传入了proxy对象,用于自定义实现,与MethodInterceptor相似,慎用method的invoke方法。切忌不要造成循环调用
  • LazyLoader:就和名字一样,提供了一种懒加载模式,这个类提供的loadObject方法就是实现这种懒加载模式,它会在第一次被调用的时候触发,返回一个代理类的实例,这个实例会被存储起来然后负责所有被代理类方法调用,如果代理类的对象创建比较麻烦且不能确定是否会被使用的时候可以使用这个模式来延迟生成代理。这个懒加载模式会在我们之后试下bean convert的时候大量使用。
  • Dispatcher:这个模式和LazyLoader一样,也提供了loadObject方法,这个方法同样也是返回一个实例代理对象。但是不同之处在于Dispatcher的loadObject方法在每次发生对原方法的调用时都会被调用并返回一个代理对象来调用原方法。也就是Dispatcher的loadObject方法返回的对象并不会存储起来,每次调用都会重写再生成新的代理类。可以简单的理解为两种模式的scope域不同,LazyLoader是Singleton单例,Dispatcher是Prototype。
  • NoOp:啥都不干只是调用了原来的方法

而Callback可以理解为我们需要对原始类方法做的增强处理。
一个简单的callback示例:

package com.dxz.cglib3;

import java.lang.reflect.Method;

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

public class CglibTest {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(QService.class);
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy)
                    throws Throwable {
                System.out.println("my callback");
                methodProxy.invokeSuper(o, objects);
                return o;
            }
        });
        QService qService = (QService) enhancer.create();
        System.out.println("测试test1方法 --------");
        qService.test1();
        System.out.println("测试test2方法---------");
        qService.test2();
        System.out.println("getClass方法---------");
        qService.getClass();
        System.out.println("hashCode方法--------");
        qService.hashCode();
    }
}

class QService {
    public void test1() {
        System.out.println("test1");
    }

    public void test2() {
        System.out.println("test2");
    }
}

 

结果:

测试test1方法 --------
my callback
test1
测试test2方法---------
my callback
test2
getClass方法---------
hashCode方法--------
my callback
Exception in thread "main" java.lang.ClassCastException: com.dxz.cglib3.QService$$EnhancerByCGLIB$$c5844154 cannot be cast to java.lang.Number
    at com.dxz.cglib3.QService$$EnhancerByCGLIB$$c5844154.hashCode(<generated>)
    at com.dxz.cglib3.CglibTest.main(CglibTest.java:30)

 

测试的结果是只输出了3个my callback,这是Enhancer生成的代理只拦截了test1,test2,hashCode等非final方法,未拦截getClass这个final修饰的方法。

Enhancer支持多个Callback,实现不同方式的增强,而CallbackFilter的作用就是帮助代理类找到某个方法需要的Callback。

6种Callback的模式

MethodInterceptor

是一个功能强大的接口,类似于AOP中的环绕增强,下面看一下它的源码

public interface MethodInterceptor extends Callback {
    Object intercept(Object var1, Method var2, Object[] var3, MethodProxy var4) throws Throwable;
}

注意这个类也是spring中内部编译的类,无法直接查看到。可以看到使用这个接口需要实现intercept方法,和字面意思一样,这个方法就会使用到字节码技术去拦截父类方法,该方法分别需要四个参数,其中MethodProxy也是spring内部编译的类,这个类很关键,下面看看这个类具体做什么。

import java.lang.reflect.Method;

import org.springframework.cglib.core.AbstractClassGenerator;
import org.springframework.cglib.core.CodeGenerationException;
import org.springframework.cglib.core.GeneratorStrategy;
import org.springframework.cglib.core.NamingPolicy;
import org.springframework.cglib.core.Signature;
import org.springframework.cglib.reflect.FastClass;

@SuppressWarnings({"rawtypes", "unchecked"})
public class MethodProxy {

    private Signature sig1;

    private Signature sig2;

    private CreateInfo createInfo;

    private final Object initLock = new Object();

    private volatile FastClassInfo fastClassInfo;

    public static MethodProxy create(Class c1, Class c2, String desc, String name1, String name2) {
        MethodProxy proxy = new MethodProxy();
        proxy.sig1 = new Signature(name1, desc);
        proxy.sig2 = new Signature(name2, desc);
        proxy.createInfo = new CreateInfo(c1, c2);
        return proxy;
    }

    private void init() {
        if (fastClassInfo == null) {
            synchronized (initLock) {
                if (fastClassInfo == null) {
                    CreateInfo ci = createInfo;

                    FastClassInfo fci = new FastClassInfo();
                    fci.f1 = helper(ci, ci.c1);
                    fci.f2 = helper(ci, ci.c2);
                    fci.i1 = fci.f1.getIndex(sig1);
                    fci.i2 = fci.f2.getIndex(sig2);
                    fastClassInfo = fci;
                    createInfo = null;
                }
            }
        }
    }

 

静态内部类CreateInfo、FastClassInfo:

    private static class FastClassInfo {

        FastClass f1;

        FastClass f2;

        int i1;

        int i2;
    }


    private static class CreateInfo {

        Class c1;

        Class c2;

        NamingPolicy namingPolicy;

        GeneratorStrategy strategy;

        boolean attemptLoad;

        public CreateInfo(Class c1, Class c2) {
            this.c1 = c1;
            this.c2 = c2;
            AbstractClassGenerator fromEnhancer = AbstractClassGenerator.getCurrent();
            if (fromEnhancer != null) {
                namingPolicy = fromEnhancer.getNamingPolicy();
                strategy = fromEnhancer.getStrategy();
                attemptLoad = fromEnhancer.getAttemptLoad();
            }
        }
    }

 

invoke()、和invokeSuper()方法:

    public Object invoke(Object obj, Object[] args) throws Throwable {
        try {
            init(); //会先初始化Proxy对象
            FastClassInfo fci = fastClassInfo;
            return fci.f1.invoke(fci.i1, obj, args);
        }
        catch (InvocationTargetException ex) {
            throw ex.getTargetException();
        }
        catch (IllegalArgumentException ex) {
            if (fastClassInfo.i1 < 0)
                throw new IllegalArgumentException("Protected method: " + sig1);
            throw ex;
        }
    }

    
    public Object invokeSuper(Object obj, Object[] args) throws Throwable {
        try {
            init();
            FastClassInfo fci = fastClassInfo;
            return fci.f2.invoke(fci.i2, obj, args);
        }
        catch (InvocationTargetException e) {
            throw e.getTargetException();
        }
    }

看看上面invoke()和invokeSuper()方法,写法基本一样:

  1. 调用init方法初始化:判断fastClassInfo是否存在,如果不存在,使用Double-checked locking创建fastClassInfo,并存放在private volatile FastClassInfo fastClassInfo的成员变量中。这个对象是实现字节码技术的关键。之后为FastClassInfo中的两个成员变量f1,f2赋值。
  2. invoke调用,一个是执行f1的invoke方法,一个是执行f2的invoke方法。

最后实际上是调用了FastClass中的invoke方法,作用就是代理执行父类方法,可以整体理解为intercept方法为拦截父类方法,在用invokeSuper方法放行父类的方法。

在《》中的3个cglib的class文件

 

 

MethodProxy调用流程

  • 首先main方法中调用getInstance调用Enhancer创建代理类class文件,并将代理类引用返回。
  • 然后调用代理对象的sayHello()
  • 然后通过反编译代理类,查看sayHello方法,发现在sayHello中会调用拦截器方法。
  • 然后在拦截器方法中会通过MethodProxy调用invokeSuper方法。

FastClass机制

Cglib动态代理执行代理方法效率之所以比JDK高是因为Cglib采用了FastClass机制,它为代理类和被代理类各生成了一个class,这个class会为代理类与被代理类的方法分类index。这个index作为方法参数,FastClass可以直接定位到要调用的方法进行调用,这样省去了反射调用,所以效率比JDK动态代理快。FastClass不是与代理类一起生成的,而是在第一次执行MethodProxy invoke/invokeSuper时生成的并放入缓存。

public class HelloService$$FastClassByCGLIB$$8656ab6f extends FastClass {
    public HelloService$$FastClassByCGLIB$$8656ab6f(Class var1) {
        super(var1);
    }

    public int getIndex(Signature var1) {
        String var10000 = var1.toString();
        switch(var10000.hashCode()) {
        case -1488716497:
            if (var10000.equals("sayOthers(Ljava/lang/String;)Ljava/lang/String;")) {
                return 1;
            }
            break;
        }

        return -1;
    }

    public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
        HelloService var10000 = (HelloService)var2;
        int var10001 = var1;

        try {
            switch(var10001) {
            case 0:
                var10000.sayHello();
                return null;
            case 1:
                return var10000.sayOthers((String)var3[0]);
            case 2:
                return new Boolean(var10000.equals(var3[0]));
            case 3:
                return var10000.toString();
            case 4:
                return new Integer(var10000.hashCode());
            }
        } catch (Throwable var4) {
            throw new InvocationTargetException(var4);
        }
    }
}
因为生成的FastClass代理类较长,这里只截取其中一部分,只为了说明问题。这里面包含了getIndex方法和invoke方法。getIndex方法会为代理类和真实类的方法分类index,这样每个index会对应到具体方法,那么在上面那个invokeSuper那里,实际上就是用fastClass对象调用的invoke方法,传入的参数就包含这个index,那么在invoke方法中,就直接根据index定位到具体方法执行了。

 

编写实现案例

callbacks代码编写:

Dispatcher
实现Dispatcher接口,要求实现loadObject方法,返回期望的代理类。值的一提的是,loadobject方法在每次调用被拦截方法的时候都会被调用一次

package com.dxz.cglib2;

import org.springframework.cglib.proxy.Dispatcher;

/****
 * 与lazy不同的是,每一次调用代理方法的时候,都会调用一次Dispatcher的loadObject获取对象
 * 而lazy则会缓存下来。
 */
public class DispatcherCallBack implements Dispatcher {
    public Object loadObject() throws Exception {
        CallbackBean callbackBean = new CallbackBean();
        return callbackBean;
    }
}

FixedValue
实现FixedValue接口,该callback同样要求实现一个loadobject方法,只不过需要注意的是该loadobject方法相同与重写了被代理类的相应方法,因为在被代理之后,FixedValue callback只会调用loadobject,而不会再调用代理目标类的相应方法!

package com.dxz.cglib2;

import org.springframework.cglib.proxy.FixedValue;

/*****
 * 该callback相当于重写了相应的函数实现。并不会调用原函数
 */
public class FixValueCallback implements FixedValue {


    /*****
     * 被代理方法的指定函数将会无条件的返回改object,动态的变更返回值
     * @return
     * @throws Exception
     */
    public Object loadObject() throws Exception {
        System.out.println("this is fixvalue callback .....    overwrite the code....");
        return true;
    }
}

InvocationHandler
需要实现InvocationHandler接口,实现invoke对象,该拦截传入了proxy对象,用于自定义实现,与MethodInterceptor相似,慎用method的invoke方法。切忌不要造成循环调用

package com.dxz.cglib2;

import java.lang.reflect.Method;

import org.springframework.cglib.proxy.InvocationHandler;

public class InvocationHandlerCallback implements InvocationHandler {
    /*****
     * invocationHandler的invoke方法传入的method和proxy都是代理本身对象 切忌重复调用,会循环调用
     * 
     * @param proxy
     *            代理类本身
     * @param method
     *            代理类内部的方法
     * @param args
     *            参数
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("invocationHandlerCallback Before....");
        method.invoke(proxy.getClass().getSuperclass().newInstance(), args);
        // 会无限循环
        // method.invoke(proxy,args);
        System.out.println("invocationHandlerCallback after....");
        return null;
    }

}

 


LazyLoader
实现LazyLoader的loadObject方法,返回对象实例,该实例只有第一次调用的时候进行初始化,之后不再重新调用,proxy类初始化时进行了成员的赋值,之后使用该成员进行调用父类方法

package com.dxz.cglib2;

import org.springframework.cglib.proxy.LazyLoader;

/****
 *
 * 延迟加载初始化 类似于spring prototype的singleton ,在第一次调用的时候进行初始化,并且将此实例存储起来,之后都将返回改实例
 * 可参考资料: https://shensy.iteye.com/blog/1881277
 */
public class LazyLoaderCallback implements LazyLoader {
    @Override
    public Object loadObject() throws Exception {
        CallbackBean callbackBean = new CallbackBean();
        return callbackBean;
    }
}

 


MethodInterceptor
实现MethodInterceptor的intercept,实现被代理对象的逻辑植入。也是最常用的callback

package com.dxz.cglib2;

import java.lang.reflect.Method;

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


/****
 * 生成代理类字节码。对方法进行拦截调用
 */
public class MethodInterceptorCallback implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("before invoke........");
        proxy.invokeSuper(obj, args);
        System.out.println("after invoke.........");
        return null;
    }
}

NoOp
通过接口声明了一个单例对象,该代理不对被代理类执行任何操作

package com.dxz.cglib2;

import org.springframework.cglib.proxy.Callback;

/**
 * Methods using this {@link Enhancer} callback will delegate directly to the
 * default (super) implementation in the base class.
 */
public interface NoOp extends Callback
{
    /**
     * A thread-safe singleton instance of the <code>NoOp</code> callback.
     */
    public static final NoOp INSTANCE = new NoOp() { };
}

被代理类code

package com.dxz.cglib2;
public class CallbackBean {

    public void methodForDispatcher(){
        System.out.println("methodForDispatcher...");
    }

    public void methodForFixValue(){
        System.out.println("methodForFixValue...");
    }

    public void methodForInvocationHandler(){
        System.out.println("methodForInvocationHandler...");
    }

    public void methodForLazy(){
        System.out.println("methodForLazy...");
    }

    public void methodForInterceptor(){
        System.out.println("methodForInterceptor...");
    }

    public void methodForNoop(){
        System.out.println("methodForNoop...");
    }
}

编写Enhancer调用

package com.dxz.cglib2;

import org.junit.Test;
import org.springframework.cglib.proxy.Callback;
import org.springframework.cglib.proxy.CallbackFilter;
import org.springframework.cglib.proxy.Enhancer;

import java.lang.reflect.Method;

public class CallbackTest {

    @Test
    public void testCallback() {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(CallbackBean.class);
        enhancer.setCallbacks(initCallBacks());
        enhancer.setCallbackFilter(initCallbackFilter());
        CallbackBean callbackBean = (CallbackBean) enhancer.create();

        System.out.println("----callbackBean.methodForNoop()");
        callbackBean.methodForNoop();
        System.out.println();
        System.out.println("----callbackBean.methodForInterceptor()");
        callbackBean.methodForInterceptor();
        System.out.println();
        System.out.println("----callbackBean.methodForLazy()");
        callbackBean.methodForLazy();
        System.out.println();
        System.out.println("----callbackBean.methodForDispatcher()");
        callbackBean.methodForDispatcher();
        System.out.println();
        System.out.println("----callbackBean.methodForInvocationHandler()");
        callbackBean.methodForInvocationHandler();
        System.out.println();
        System.out.println("----callbackBean.methodForFixValue()");
        callbackBean.methodForFixValue();
    }

    /****
     * 初始化callback拦截数组
     * 
     * @return
     */
    private static final Callback[] initCallBacks() {
        MethodInterceptorCallback methodInterceptorCallback = new MethodInterceptorCallback();
        LazyLoaderCallback lazyLoaderCallback = new LazyLoaderCallback();
        InvocationHandlerCallback invocationHandlerCallback = new InvocationHandlerCallback();
        FixValueCallback fixValueCallback = new FixValueCallback();
        DispatcherCallBack dispatcherCallBack = new DispatcherCallBack();
        Callback[] callbacks = new Callback[] {methodInterceptorCallback, // NoOp.INSTANCE 会报错
                methodInterceptorCallback, lazyLoaderCallback, dispatcherCallBack,
                invocationHandlerCallback, fixValueCallback };
        return callbacks;
    }

    private static final CallbackFilter initCallbackFilter() {
        return new CallbackFilter() {
            public int accept(Method method) {
                if (method.getName().equals("methodForNoop")) {
                    return 0;
                }
                if (method.getName().equals("methodForInterceptor")) {
                    return 1;
                }
                if (method.getName().equals("methodForLazy")) {
                    return 2;
                }
                if (method.getName().equals("methodForDispatcher")) {
                    return 3;
                }
                if (method.getName().equals("methodForInvocationHandler")) {
                    return 4;
                }
                if (method.getName().equals("methodForFixValue")) {
                    return 5;
                }
                return 0;
            }
        };

    }
}

 结果:

----callbackBean.methodForNoop()
before invoke........
methodForNoop...
after invoke.........

----callbackBean.methodForInterceptor()
before invoke........
methodForInterceptor...
after invoke.........

----callbackBean.methodForLazy()
methodForLazy...

----callbackBean.methodForDispatcher()
methodForDispatcher...

----callbackBean.methodForInvocationHandler()
invocationHandlerCallback Before....
methodForInvocationHandler...
invocationHandlerCallback after....

----callbackBean.methodForFixValue()
this is fixvalue callback .....    overwrite the code....

总结

以上即为本篇的全部内容,主要简单介绍了cglib与几种callback的使用。比较简单、基础。

当然,cglib不只是这些内容。在实现的生成proxy的同时,作者抽象了很多东西,这包括

  • 缓存机制
  • 生成策略
  • 弱引用WeakHashMap,WeakReference
  • LoadingCache搭配FutureTask线程竞争模型
  • 对反射的封装
  • 对asm的封装
  • 类加载的ClassLoader
  • 灵活的Function设计

原文链接:https://blog.csdn.net/zhang6622056/article/details/87286498

https://www.jianshu.com/p/a20d5c949ac4

posted on 2015-04-09 18:06  duanxz  阅读(3894)  评论(0编辑  收藏  举报