cglib动态代理学习

CGLIB是一个强大的高性能的代码生成包。它广泛的被许多AOP的框架使用,例如Spring AOP和dynaop,为他们提供方法的interception(拦截)。最流行的OR Mapping工具hibernate也使用CGLIB来代理单端single-ended(多对一和一对一)关联(对集合的延迟抓取,是采用其他机制实现的)。EasyMock和jMock是通过使用模仿(mock)对象来测试java代码的包。它们都通过使用CGLIB来为那些没有接口的类创建模仿(mock)对象。
CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。除了CGLIB包,脚本语言例如Groovy和BeanShell,也是使用ASM来生成java的字节码。当然不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉

cglib实现代理时使用Enhancer的setCallback设置回调方法,对方法进行拦截,从而实现拦截,下面的例子是使用MethodInterceptor拦截器

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

class Base {
    /**
     * 一个模拟的add方法
     */
    public void add() {
        System.out.println("add ------------");
    }
}
class Factory {
    /**
     * 获得增强之后的目标类,即添加了切入逻辑advice之后的目标类
     *
     * @param proxy
     * @return
     */
    public static Base getInstance(CglibProxy proxy) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Base.class);
        //回调方法的参数为代理类对象CglibProxy,最后增强目标类调用的是代理类对象CglibProxy中的intercept方法
        enhancer.setCallback(proxy);
        // 此刻,base不是单纯的目标类,而是增强过的目标类
        Base base = (Base) enhancer.create();
        return base;
    }
}
class CglibProxy implements MethodInterceptor{

    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("before...");
        methodProxy.invokeSuper(o,objects);
        System.out.println("after....");
        return null;
    }
}
public class CgProxy {

    public static void main(String[] args) {

        CglibProxy cgProxy = new CglibProxy();
        Base base = Factory.getInstance(cgProxy);
        base.add();
    }
}

需要cglib和asm的包
maven pom.xml

  <dependency>
      <groupId>cglib</groupId>
      <artifactId>cglib</artifactId>
      <version>3.0</version>
    </dependency>
  <dependency>
      <groupId>org.ow2.asm</groupId>
      <artifactId>asm-all</artifactId>
      <version>5.0.4</version>
  </dependency>

cglic的拦截器种类

这里写图片描述
Callback有6个子接口,意味着回调方法中可以配置6中拦截器

1. FixedValue

定义一个Person类

class Person{
    public String sayHello(String name){

        System.out.println("say hello...");
        return  "hello "+ name;
    }
}

FixedValue拦截器会将所有的方法的返回值替换为固定了一个值,包括从Object类的方法
自定一个FixdValue,需要实现FixedValue,下面的MyFixedValue实现了FixedValue,并实现了loadObject,返回”who are you?”,这样当掉用代理类的所有方法的时候,都会返回”who are you ?”

class MyFixedValue implements FixedValue{

    @Override
    public Object loadObject() throws Exception {
        return "who are you ? ";
    }
}

调用sayHello方法

public class CglibStudy {


    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Person.class);
        enhancer.setCallback(new MyFixValue());
        Person person  = (Person)enhancer.create();
        String hello = person.sayHello("xiaoming");
        System.out.println(hello);  //hello=who are you ?
        System.out.println(person.toString());//也打印出who are you ?
    }
}

输出结果
这里写图片描述
如果调用person.hashCode(), 会抛出异常 ClassCastException ,因为FixedValue永远返回String 类型
只能调用Person的非final方法和非static方法

2. InvocationHandler
class MyInvocationHandler implements InvocationHandler{

    @Override
    public Object invoke(Object o, Method method, Object[] objects) throws Throwable {

        return "Hello pig";
    }
}

此时所有的方法调用时也会返回固定的值Hello Pig,在invoke中,不能使用method.invoke,因为调用的时候又会被拦截,导致死循环

class MyInvocationHandler implements InvocationHandler{

    @Override
    public Object invoke(Object o, Method method, Object[] args) throws Throwable {

      method.invoke(o,args); //调用会导致死循环
    }
}

这里写图片描述

输出结果
这里写图片描述
可以看出invoke中的method方法正是Person类的sayHello方法,如果使用method.invoke(o,args),将会被MyInvocationHandler拦截,从而导致死循环

3. MethodInterceptor

MethodInterceptor允许完全控制被截取的方法,并提供一些实用程序来调用增强类处于原始状态的方法。 但是为什么会想使用其他方法呢? 因为其他方法更有效率,而且cglib经常用于效率起重要作用的边缘案例框架中。 MethodInterceptor的创建和链接需要例如生成不同类型的字节代码和创建InvocationHandler不需要的一些运行时对象。 因此,还有其他类可以与Enhancer一起使用

class MyMethodInterceptor implements MethodInterceptor{

     @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        if(method.getDeclaringClass() != Object.class && method.getReturnType() == String.class) {
            return "Hello cglib!";
        } else {
            return methodProxy.invokeSuper(o, args);
        }
    }
}

methodProxy.invokeSuper 方法将会调用被拦截的方法,这样我们就可以在调用之前和之后做一些其他的事情,实现代理的目的,这个用法类似于JDK动态代理,上面的intercept方法中,先判断被拦截的方法不是Object类,并且返回类型是String的话,就直接返回Hello cglib,否则执行methodProxy.invokeSuper(o,args),这句将会执行被代理类的方法,而return "Hello cglib",并没有调用Person的被拦截的方法,直接就终止了

4. LazyLoader

lazyLoader 可以用来做一些延迟加载,对象并没有被加载,而是在使用的时候才创建,像Herbernate就是用cglib来做延迟加载,这个loadObject只会调用一次
这里写图片描述
定义一个LazyLoader的实现类

class MyLazyLoader implements  LazyLoader{
    @Override
    public Object loadObject() throws Exception {
        System.out.println("创建Person对象");
        Person person = new Person();
        return person;
    }
}

调用的时候才会调用loadObject创建对象

public class CglibStudy {

    public static void main(String[] args) throws ClassNotFoundException {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Person.class);
        Person person  = (Person)enhancer.create(Person.class,new MyLazyLoader());
        System.out.println(person);
        String hello = person.sayHello("xiaoming");
        System.out.println(hello);
        hello = person.sayHello("xiaowang");
        System.out.println(person);
        System.out.println(hello);
    }
}

输出,看到两次打印person对象是一样的,因此loadObject只会被调用一次
这里写图片描述

5. Dispatcher
class MyDispatcher implements Dispatcher{

    @Override
    public Object loadObject() throws Exception {
        System.out.println("创建Person对象");
        Person person = new Person();
        return person;
    }
}

调用

public class CglibStudy {


    public static void main(String[] args) throws ClassNotFoundException {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Person.class);
        Person person  = (Person)enhancer.create(Person.class,new MyDispatcher());
        System.out.println(person);
        String hello = person.sayHello("xiaoming");
        System.out.println(hello);
        hello = person.sayHello("xiaowang");//这个peson对象是被第二次创建的
        System.out.println(person);
        System.out.println(hello);
    }
}

输出结果,两次打印的person对象不一样,说明loadObject会每次都调用
这里写图片描述

6. ProxyRefDispatcher

ProxyRefDispatcher用来和setCallbackFilter使用

7. NoOp

NoOp.INSTANCE返回的没有任何操作的拦截器

public class CglibStudy {

    public static void main(String[] args) throws ClassNotFoundException {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Person.class);
        CallbackHelper callbackHelper = new CallbackHelper(Person.class,new Class[0]) {
            @Override
            protected Object getCallback(Method method) {
                if(method.getReturnType() == String.class) {
                    return new FixedValue() {
                        @Override
                        public Object loadObject() throws Exception {
                            return "hello cglib";
                        }
                    };
                }else{
                    return NoOp.INSTANCE;
                }
            }
        };
        enhancer.setCallbackFilter(callbackHelper);
        enhancer.setCallbacks(callbackHelper.getCallbacks());
        Person person  = (Person)enhancer.create();
        System.out.println(person);
        String hello = person.sayHello("xiaoming");
        System.out.println(hello);
        hello = person.sayHello("xiaowang");
        System.out.println(person);
        System.out.println(hello);

    }
}

cglib也是数据代理模式,关于代理模式,可以看我的另外一篇博客

http://blog.csdn.net/daiyutage/article/details/69788030

cglib的github地址,点击打开
cglib的API

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值