java CGLib代理

转载自   cglib之Enhancerhtml

 

1. 背景

cglib库的Enhancer在Spring AOP中做为一种生成代理的方式被普遍使用。本文针对Enhancer的用法以实际代码为例做一些介绍。java

2. Enhancer是啥

Enhancer是cglib中使用频率很高的一个类,它是一个字节码加强器,能够用来为无接口的类建立代理。它的功能与java自带的Proxy类挺类似的。它会根据某个给定的类建立子类,而且全部非final的方法都带有回调钩子。数组

2.1 Callback

那么Enhancer使用的Callback具体有哪些呢?下面介绍如下这几种Callback。在cglib中Callback是一个标记接口,Enhancer使用的回调就是cglib中Callback接口的子接口。ide

2.1.1 Callback-MethodInterceptor

方法拦截器。这个东西和JDK自带的InvocationHandler很相似oop

Object intercept(Object obj, java.lang.reflect.Method method, Object[] args, MethodProxy proxy) throws Throwable

这其中MethodProxy proxy参数通常是用来调用原来的对应方法的。好比能够proxy.invokeSuper(obj, args)。那么为何不能像InvocationHandler那样用method来调用呢?由于若是用method调用会再次进入拦截器。为了不这种状况,应该使用接口方法中第四个参数methodProxy调用invokeSuper方法。ui

public class EnhancerTest {

    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Car.class);
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy)
                    throws Throwable {
                System.out.println("before");
                Object res = methodProxy.invokeSuper(obj, args);
                System.out.println("after");
                return res;
            }
        });
        Car car = (Car) enhancer.create();

        car.print();
    }

    static class Car {
        void print() {
            System.out.println("I am a car");
        }
    }

}

上面的程序会打印:this

before
I am a car
afterspa

2.1.2 Callback-NoOp

这个回调至关简单,就是啥都不干的意思。代理

Callback-LazyLoader

LazyLoader是cglib用于实现懒加载的callback。当被加强bean的方法初次被调用时,会触发回调,以后每次再进行方法调用都是对LazyLoader第一次返回的bean调用。code

public class EnhancerTest { public static void main(String[] args) { CarFactory factory = new CarFactory(); System.out.println("factory built"); System.out.println(factory.car.getName()); System.out.println(factory.car.getName()); } static class Car { String name; Car() { } String getName() { return name; }
    }

    static class CarFactory {
        Car car;

        CarFactory() {
            car = carLazyProxy();
        }

        Car carLazyProxy() {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(Car.class);
            enhancer.setCallback(new LazyLoader() {
                @Override
                public Object loadObject() throws Exception {
                    System.out.println("prepare loading");
                    Car car = new Car();
                    car.name = "this is a car";
                    System.out.println("after loading");
                    return car;
                }
            });
            return ((Car) enhancer.create());
        }
    }
}
上面的程序打印状况以下:

factory built
prepare loading
after loading
this is a car
this is a car

2.1.3 Callback-Dispatcher

Dispatcher和LazyLoader做用很类似,区别是用Dispatcher的话每次对加强bean进行方法调用都会触发回调。

public class EnhancerTest { public static void main(String[] args) { CarFactory factory = new CarFactory(); System.out.println("factory built"); System.out.println(factory.car.getName()); System.out.println(factory.car.getName()); } static class Car { String name; Car() { } String getName() { return name; }
    }

    static class CarFactory {
        Car car;

        CarFactory() {
            car = carLazyProxy();
        }

        Car carLazyProxy() {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(Car.class);
            enhancer.setCallback(new Dispatcher() {
                @Override
                public Object loadObject() throws Exception {
                    System.out.println("prepare loading");
                    Car car = new Car();
                    car.name = "this is a car";
                    System.out.println("after loading");
                    return car;
                }
            });
            return ((Car) enhancer.create());
        }
    }
}
程序会打印:

factory built
prepare loading
after loading
this is a car
prepare loading
after loading
this is a car

2.1.4 Callback-InvocationHandler

cglib的InvocationHandler和JDK自带的InvocationHandler做用基本相同。使用的时候要注意,若是对参数中的method再次调用,会重复进入InvocationHandler。

public class EnhancerTest {

    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Car.class);
        enhancer.setCallback(new InvocationHandler() {
            @Override
            public Object invoke(Object obj, Method method, Object[] args) throws Throwable {
                if (method.getReturnType() == void.class) {
                    System.out.println("hack");
                }
                return null;
            }
        });
        Car car = (Car) enhancer.create();
        car.print();
    }

    static class Car {
        void print() {
            System.out.println("I am a car");
        }
    }
}

上面的程序会打印:

hack

2.1.5 Callback-FixedValue

FixedValue通常用于替换方法的返回值为回调方法的返回值,但必须保证返回类型是兼容的,不然会出转换异常。

public class EnhancerTest {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Car.class);
        enhancer.setCallback(new FixedValue() {
            @Override
            public Object loadObject() throws Exception {
                return "hack!";
            }
        });

        Car car = (Car) enhancer.create();
        System.out.println(car.print1());
        System.out.println(car.print2());
    }

    static class Car {
        String print1() {
            return "car1";
        }
        String print2() {
            return "car2";
        }
    }
}

上面的代码会打印:

hack!
hack!

2.2 CallbackFilter

上面已经介绍了Enhancer的几种常见callback,这里再介绍一下CallbackFilter。
上面都是为加强bean配置了一种代理callback,可是当须要做一些定制化的时候,CallbackFilter就派上用处了。
当经过设置CallbackFilter加强bean以后,bean中原方法都会根据设置的filter与一个特定的callback映射。咱们一般会使用cglib中CallbackFilter的默认实现CallbackHelper,它的getCallbacks方法能够返回生成的callback数组。

下面是CallbackFilter的demo程序。

public class EnhancerTest {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Car.class);
        CallbackHelper helper = new CallbackHelper(Car.class,new Class[0]) {
            @Override
            protected Object getCallback(Method method) {
                if (method.getReturnType() == void.class) {
                    return new MethodInterceptor() {
                        @Override
                        public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy)
                                throws Throwable {
                            System.out.println("before invocation");
                            Object res = methodProxy.invokeSuper(obj, args);
                            System.out.println("after invocation");
                            return res;
                        }
                    };
                } else if (method.getReturnType() == String.class) {
                    return new FixedValue() {
                        @Override
                        public Object loadObject() throws Exception {
                            return "a hacked car";
                        }
                    };
                } else return NoOp.INSTANCE;
            }
        };

        enhancer.setCallbacks(helper.getCallbacks());
        enhancer.setCallbackFilter(helper);
        
        Car car = (Car) enhancer.create();
        car.print();
        System.out.println(car.getId());
        System.out.println(car.getName());
    }

    static class Car {
        static int index = 0;

        int id;

        Car() {
            id = index++;
        }

        String getName() {
            return "car";
        }

        int getId() {
            return id;
        }

        void print() {
            System.out.println("I am a car");
        }

    }
}
程序将打印:

before invocation
I am a car
after invocation
0
a hacked car

咱们能够看看CallbackHelper的源码在作什么事情:

public CallbackHelper(Class superclass, Class[] interfaces)
{
    List methods = new ArrayList();
    Enhancer.getMethods(superclass, interfaces, methods);
    Map indexes = new HashMap();
    for (int i = 0, size = methods.size(); i < size; i++) {
        Method method = (Method)methods.get(i);

        // getCallback就是咱们编写的根据method返回callback的策略方法。
        Object callback = getCallback(method);
        if (callback == null)
            throw new IllegalStateException("getCallback cannot return null");
        boolean isCallback = callback instanceof Callback;
        if (!(isCallback || (callback instanceof Class)))
            throw new IllegalStateException("getCallback must return a Callback or a Class");
        if (i > 0 && ((callbacks.get(i - 1) instanceof Callback) ^ isCallback))
            throw new IllegalStateException("getCallback must return a Callback or a Class consistently for every Method");

        // 从callback与编号的map中获取编号。
        Integer index = (Integer)indexes.get(callback);
        // 若是map中没有对应callback,则插入到map中。
        if (index == null) {
            index = new Integer(callbacks.size());
            indexes.put(callback, index);
        }
        // 维护bean的method与callback编号的映射。
        methodMap.put(method, index);
        // 维护callback列表。
        callbacks.add(callback);
    }
}

 

能够看到在CallbackHelper源码中也是维护了一个methodMap用于保存method和callback编号的映射,一个callbacks用于保存callback集合(方便getCallbacks方法导出)。

3. 参考

CGLib: The Missing Manual

相关文章
相关标签/搜索