CGlib动态代理实战

CGlib是什么

CGlib是一个强大的,高性能,高质量的Code生成类库。它可以在运行期扩展Java类与实现Java接口。
官方介绍:

cglib - Byte Code Generation Library is high level API to generate and transform Java byte code. It is used by AOP, testing, data access frameworks to generate dynamic proxy objects and intercept field access. https://github.com/cglib/cglib/wiki

当然这些实际的功能是ASM所提供的,ASM是一个Java字节码操控框架,官方介绍:

ASM is an all purpose Java bytecode manipulation and analysis
framework. It can be used to modify existing classes or dynamically
generate classes, directly in binary form. Provided common
transformations and analysis algorithms allow to easily assemble
custom complex transformations and code analysis tools.

具体可以去ASM官网详细了解:http://asm.ow2.org/。
cglib封装了ASM,简化了ASM的操作,实现了在运行期动态生成新的class。
Spring aop底层实现就是CGlib;hibernate使用cglib动态生成VO/PO (接口层对象)。

一、JDK中的动态代理

JDK中的动态代理是通过反射类Proxy以及InvocationHandler回调接口实现的,对此不了解的同学可以参考InfoQ上的这篇博客:Java深度历险(七)——Java反射与动态代理
但是JDK中的动态代理有一个缺点:被动态代理的类必须要实现一个接口,也就是说只能对该类所实现接口中定义的方法进行代理,这在实际编程中具有一定的局限性,而且使用反射的效率也并不是很高。

二、CGLib实战

使用CGLib实现动态代理,完全不受代理类必须实现接口的限制,而且CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,比使用Java反射效率要高。唯一需要注意的是,CGLib不能对声明为final的类进行代理,因为CGLib原理是动态生成被代理类的子类。
2.1 使用MethodInterceptor实现AOP
1) 首先,代理类UserDao

package com.ricky.cglib;

/** * 需要被代理的类 * @author Ricky Fung * */
public class UserDao {

    public long insert() {
        System.out.println("insert() is executing !");
        return 1;
    }

    public void query() {
        System.out.println("query() is executing !");
    }

    public int update() {
        System.out.println("update() is executing !");
        return 1;
    }

    public int delete() {
        System.out.println("delete() is executing !");
        return 1;
    }

}

这里代理类是个普通的java类,没有实现任何接口。

2) 方法拦截器

package com.ricky.cglib.interceptor;

import java.lang.reflect.Method;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class AopInterceptor implements MethodInterceptor {  

    public Object intercept(Object arg0, Method arg1, Object[] arg2,  
            MethodProxy arg3) throws Throwable {  

        System.out.println("before method execute");

        Object obj = arg3.invokeSuper(arg0, arg2);

        System.out.println("after method execute");

        return obj;  
    }  
}  

在拦截器里,我们可以对代理类的方法做拦截,我在方法执行之前和执行之后进行了处理,用以模拟AOP。
3) 最后是客户端代码

package com.ricky.cglib.interceptor;

import com.ricky.cglib.UserDao;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;

/** * 实现AOP * @author Ricky Fung * */
public class MethodInterceptorDemo {

    public static void main(String[] args) {

        new MethodInterceptorDemo().run();
    }

    private void run() {

        UserDao userDao = getProxyInstance(new AopInterceptor());
        userDao.insert();
        userDao.query();
    }

    public UserDao getProxyInstance(MethodInterceptor methodInterceptor){  
        Enhancer en = new Enhancer();
        //进行代理 
        en.setSuperclass(UserDao.class);  
        en.setCallback(methodInterceptor);  
        //生成代理实例 
        return (UserDao)en.create();  
    }  
}

通过CGlib提供的Enhancer类的create方法来生成代理类。
运行一下,看看结果:

before method execute
insert() is executing !
after method execute
before method execute
query() is executing !
after method execute

有了CGlib提供的方法拦截器,我们就可以很轻松的实现AOP编程了。

2.2 方法过滤器(CallbackFilter)
在上面的例子中,我们对UserDao的crud方法进行了拦截,假如我们只想对query以外的方法进行拦截,该怎么办呢?当然最简单的方式是去修改我们的方法拦截器,不过这样会使逻辑变得复杂,且不利于维护。还好CGlib给我们提供了方法过滤器(CallbackFilter),CallbackFilter可以明确表明,被代理的类中不同的方法, 被哪个拦截器所拦截。
API文档如下:

下面我们就来实现个过滤器用来过滤query方法。

package com.ricky.cglib.filter;

import java.lang.reflect.Method;
import net.sf.cglib.proxy.CallbackFilter;

public class MyCallbackFilter implements CallbackFilter{

    public int accept(Method arg0) {
        if(!"query".equalsIgnoreCase(arg0.getName()))  
            return 0;
        return 1;
    }
}

CallbackFilter接口只有一个 accept方法

方法返回值的含义

the index into the array of callbacks (as specified by Enhancer.setCallbacks(net.sf.cglib.proxy.Callback[])) to use for the method,

返回值为callback数组中索引位置,也就是说当前的方法要指定哪个拦截器进行。

客户端代码如下

package com.ricky.cglib.filter;

import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.NoOp;
import com.ricky.cglib.UserDao;
import com.ricky.cglib.interceptor.AopInterceptor;

/** * 方法过滤器 * @author Ricky Fung * */
public class CallbackFilterDemo {

    public static void main(String[] args) {

        new CallbackFilterDemo().run();
    }

    private void run() {

        UserDao userDao = getProxyInstance(new AopInterceptor());
        userDao.insert();
        userDao.query();
    }

    public UserDao getProxyInstance(MethodInterceptor methodInterceptor){  
        Enhancer en = new Enhancer();
        //进行代理 
        en.setSuperclass(UserDao.class);  
        en.setCallbacks(new Callback[]{methodInterceptor, NoOp.INSTANCE});  
        en.setCallbackFilter(new MyCallbackFilter());  
        //生成代理实例 
        return (UserDao)en.create();  
    }  

}

其中NoOp.INSTANCE是CGlib提供的一个没有任何操作的拦截器。我们在MyCallbackFilter的accept方法返回1的时候其实就是指定由它来拦截query方法。

运行一下,看看结果

before method execute
insert() is executing !
after method execute
query() is executing !

发现insert方法被拦截了,而query方法则没有被拦截。

最后附上示例代码:https://github.com/FBing/cglib-demo

你可能感兴趣的:(动态代理,cglib)