zoukankan      html  css  js  c++  java
  • cglib之Enhancer

    1. 背景

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

    2. Enhancer是啥

    Enhancer是cglib中使用频率很高的一个类,它是一个字节码增强器,可以用来为无接口的类创建代理。它的功能与java自带的Proxy类挺相似的。它会根据某个给定的类创建子类,并且所有非final的方法都带有回调钩子。

    2.1 Callback

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

    2.1.1 Callback-MethodInterceptor

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

    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方法。

    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");
            }
        }
    
    }
    

    上面的程序会打印:
    before
    I am a car
    after

    2.1.2 Callback-NoOp

    这个回调相当简单,就是啥都不干的意思。

    Callback-LazyLoader

    LazyLoader是cglib用于实现懒加载的callback。当被增强bean的方法初次被调用时,会触发回调,之后每次再进行方法调用都是对LazyLoader第一次返回的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 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

  • 相关阅读:
    k8s 资源管理
    Kubernetes核心组件
    python复习
    项目发布
    tornado
    斯巴达系统(一)
    Tornado-第三篇-tornado支持websocket协议
    Tornado-第二篇-异步非阻塞
    Tornado-第一篇-搭建网页
    python--面向对象的特殊方法(反射,内置方法)
  • 原文地址:https://www.cnblogs.com/micrari/p/7565632.html
Copyright © 2011-2022 走看看