实战CGLib

316 阅读6分钟

最近在自己做一个网关的项目,平时做一些业务开发,对代理模式的认知不够深刻,在此对cglib的代理用法做一些总结,方便项目的进行

CGLIB是一个功能强大,高性能的代码生成包。它为没有实现接口的类提供代理,为JDK的动态代理提供了很好的补充。通常可以使用Java的动态代理创建代理,但当要代理的类没有实现接口或者为了更好的性能,CGLIB是一个好的选择。

CGLIB原理:动态生成一个要代理类的子类,子类重写要代理的类的所有不是final的方法。在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。它比使用java反射的JDK动态代理要快。'

如果没有代理模式,代码就会缺少灵魂 如果没有代理模式,可能就看不到Spring中的AOP切面编程了,看不到mybatis这样的ORM框架了,各种基于代理实现的各种技术中间件

CGLIB缺点:对于final方法,无法进行代理。

1. 方法拦截MethodInterceptor

引入依赖

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>
/**
 * 
 * 被代理的类,不需要实现接口,这也是相比于InvocatinHandler的优势
 * @Author: dingyawu
 * @Description: TODO
 * @Date: Created in 21:38 2023/2/11
 */
public class ConcreteClassNoInterface {
    public String getConcreteMethodA(String str){
        System.out.println("ConcreteMethod A ... "+str);
        return str;
    }
    public int getConcreteMethodB(int n){
        System.out.println("ConcreteMethod B ... "+n);
        return n+10;
    }
}



/**
 *
 * 拦截器, CGLib会回调MethodInterceptor接口方法拦截,来实现你自己的代理逻辑
 * @Author: dingyawu
 * @Description: TODO
 * @Date: Created in 21:39 2023/2/11
 */
public class ConcreteClassInterceptor implements MethodInterceptor {
    /**
     * Object为由CGLib动态生成的代理类实例
     * Method为上文中实体类所调用的被代理的方法引用
     * Object[]为参数值列表
     * MethodProxy为生成的代理类对方法的代理引用
     */
    public Object intercept(Object obj, Method method, Object[] arg, MethodProxy proxy) throws Throwable {
        System.out.println("Before:"+method);
        Object object=proxy.invokeSuper(obj, arg);
        System.out.println("After:"+method);
        return object;
    }

    @Test
    public void test(){
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(ConcreteClassNoInterface.class);
        enhancer.setCallback(new ConcreteClassInterceptor());
        ConcreteClassNoInterface ccni=(ConcreteClassNoInterface)enhancer.create();
        ccni.getConcreteMethodA("shensy");
        ccni.getConcreteMethodB(0);
    }
}

2. 接口生成器InterfaceMaker

/**
 * InterfaceMaker会动态生成一个接口,该接口包含指定类定义的所有方法。
 * 此处让Object生成的代理类实现了由InterfaceMaker生成的接口,但是由于Object类并没有覆写其中的方法,
 * 因此,每当对生成接口内方法进行MethodInterceptor方法拦截时,都返回一个字符串,并在最后打印出来。
 * @Author: dingyawu
 * @Description: TODO
 * @Date: Created in 21:31 2023/2/11
 */
public class CreateTest {

    @Test
    public void test() throws Exception {
        InterfaceMaker im=new InterfaceMaker();
        im.add(ConcreteClassNoInterface.class);
        Class interfaceOjb=im.create();
        System.out.println(interfaceOjb.isInterface());
        System.out.println(interfaceOjb.getName());

        Method[] methods = interfaceOjb.getMethods();
        for(Method method:methods){
            System.out.println(method.getName());
        }

        Object obj = Enhancer.create(Object.class, new Class[]{ interfaceOjb },
                new MethodInterceptor() {
                    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
                        return "intercept!";
                    }
                });

        Method method = obj.getClass().getMethod("getConcreteMethodA", new Class[]{String.class});
        System.out.println(method.invoke(obj, new Object[]{"12345"}));

    }
}

3. 设置多个callback,但生不生效由ConcreteClassCallbackFilter决定索引

public class ConcreteClassNoInterface1 {
    public String getConcreteMethodA(String str){
        System.out.println("ConcreteMethod A ... "+str);
        return str;
    }
    public int getConcreteMethodB(int n){
        System.out.println("ConcreteMethod B ... "+n);
        return n+10;
    }
    public int getConcreteMethodFixedValue(int n){
        System.out.println("getConcreteMethodFixedValue..."+n);
        return n+10;
    }
}


/**
 * 回调过滤器CallbackFilter
 * 在CGLib回调时可以设置对不同方法执行不同的回调逻辑,或者根本不执行回调。
 * 其中return值为被代理类的各个方法在回调数组Callback[]中的位置索引
 * @Author: dingyawu
 * @Description: TODO
 * @Date: Created in 22:09 2023/2/11
 */
public class ConcreteClassCallbackFilter implements CallbackFilter {

    public int accept(Method method) {
        if("getConcreteMethodB".equals(method.getName())){
            //Callback callbacks[0]
            return 0;
        }else if("getConcreteMethodA".equals(method.getName())){
            //Callback callbacks[1]
            return 1;
        }else if("getConcreteMethodFixedValue".equals(method.getName())){
            //Callback callbacks[2]
            return 2;
        }
        return 1;
    }
}



/**
 * @Author: dingyawu
 * @Description: TODO
 * @Date: Created in 22:11 2023/2/11
 */
public class ConcreteClassFixedValue implements FixedValue {
    public Object loadObject() throws Exception {
        System.out.println("ConcreteClassFixedValue loadObject ...");
        Object object=999;
        return object;
    }
}



/**
 * 回调过滤CallbackFilter
 * MethodInterceptor:方法拦截器
 * NoOp.INSTANCE:这个NoOp表示no operator,即什么操作也不做,代理类直接调用被代理的方法不进行拦截
 * FixedValue:表示锁定方法返回值,无论被代理类的方法返回什么值,回调方法都返回固定值。
 *
 * @Author: dingyawu
 * @Description: TODO
 * @Date: Created in 22:10 2023/2/11
 */
public class CreateProxyTest {

    @Test
    public void test(){
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(ConcreteClassNoInterface1.class);
        CallbackFilter filter = new ConcreteClassCallbackFilter();
        enhancer.setCallbackFilter(filter);
        Callback interceptor = new ConcreteClassInterceptor();
        Callback noOp = NoOp.INSTANCE;
        Callback fixedValue = new ConcreteClassFixedValue();
        Callback[] callbacks = new Callback[]{ interceptor, noOp, fixedValue };
        enhancer.setCallbacks(callbacks);
        ConcreteClassNoInterface1 proxyObject=(ConcreteClassNoInterface1)enhancer.create();


        //接上文...
        System.out.println("*** NoOp Callback ***");
        proxyObject.getConcreteMethodA("abcde");

        System.out.println("*** MethodInterceptor Callback ***");
        proxyObject.getConcreteMethodB(1);

        System.out.println("*** FixedValue Callback ***");
        int fixed1=proxyObject.getConcreteMethodFixedValue(128);
        System.out.println("fixedValue1:"+fixed1);
        int fixed2=proxyObject.getConcreteMethodFixedValue(256);
        System.out.println("fixedValue2:"+fixed2);
    }
}

4. 延迟加载LazyLoader

/**
 * 首先定义一个实体类LoaderBean,该Bean内有一个需要延迟加载的属性PropertyBean。
 * @Author: dingyawu
 * @Description: TODO
 * @Date: Created in 00:07 2023/2/12
 */
public class LoaderBean {

    private String loaderName;
    private int loaderValue;
    private PropertyBean propertyBean;


    public LoaderBean(){
        this.loaderName="loaderNameA";
        this.loaderValue=123;
        this.propertyBean = createPropertyBean();
    }
    protected PropertyBean createPropertyBean(){
        Enhancer enhancer=new Enhancer();
        //设置要被代理的类
        enhancer.setSuperclass(PropertyBean.class);
        //第一个参数就是被代理的类的class, 第二个参数是callback的实现类
        return (PropertyBean)enhancer.create(PropertyBean.class,new ConcreteClassLazyLoader());
    }

    public String getLoaderName() {
        return loaderName;
    }

    public void setLoaderName(String loaderName) {
        this.loaderName = loaderName;
    }

    public int getLoaderValue() {
        return loaderValue;
    }

    public void setLoaderValue(int loaderValue) {
        this.loaderValue = loaderValue;
    }

    public PropertyBean getPropertyBean() {
        return propertyBean;
    }

    public void setPropertyBean(PropertyBean propertyBean) {
        this.propertyBean = propertyBean;
    }
}



/**
 * @Author: dingyawu
 * @Description: TODO
 * @Date: Created in 00:08 2023/2/12
 */
public class PropertyBean {
    private String propertyName;
    private int propertyValue;

    public String getPropertyName() {
        return propertyName;
    }

    public void setPropertyName(String propertyName) {
        this.propertyName = propertyName;
    }

    public int getPropertyValue() {
        return propertyValue;
    }

    public void setPropertyValue(int propertyValue) {
        this.propertyValue = propertyValue;
    }
}



/**
 * @Author: dingyawu
 * @Description: TODO
 * @Date: Created in 00:09 2023/2/12
 */
public class ConcreteClassLazyLoader implements LazyLoader {
    public Object loadObject()  {
        System.out.println("LazyLoader loadObject() ...");
        PropertyBean bean = new PropertyBean();
        bean.setPropertyName("lazy-load object propertyName!");
        bean.setPropertyValue(11);
        return bean;
    }

    @Test
    public void test(){
        LoaderBean loader=new LoaderBean();
        System.out.println(loader.getLoaderName());
        System.out.println(loader.getLoaderValue());
        PropertyBean propertyBean=loader.getPropertyBean();//访问延迟加载对象
        System.out.println(propertyBean.getPropertyName());
        System.out.println(propertyBean.getPropertyValue());
        System.out.println("after...");
        //当再次访问延迟加载对象时,就不会再执行回调了
        System.out.println(propertyBean.getPropertyName());
    }
}



/**
 * 第一次获取property bean的属性时,会触发代理类回调方法。
 * 第二次再获取property bean的属性时,就直接返回属性值而不会再次触发代理类回调方法了
 * @Author: dingyawu
 * @Description: TODO
 * @Date: Created in 01:03 2023/2/12
 */
public class ProxyTest {

    @Test
    public void test(){
        LoaderBean loader = new LoaderBean();
        System.out.println(loader.getLoaderName());
        System.out.println(loader.getLoaderValue());
        //访问延迟加载对象
        PropertyBean propertyBean=loader.getPropertyBean();
        System.out.println(propertyBean.getPropertyName());
        System.out.println(propertyBean.getPropertyValue());
        System.out.println("after...");
        //当再次访问延迟加载对象时,就不会再执行回调了
        System.out.println(propertyBean.getPropertyName());
    }
}

5. 延迟加载Dispatcher

/**
 * @Author: dingyawu
 * @Description: TODO
 * @Date: Created in 01:11 2023/2/12
 */
public class ConcreteClassDispatcher implements Dispatcher {
    public Object loadObject() throws Exception {
        System.out.println("Dispatcher loadObject ...");
        PropertyBean object = new PropertyBean();
        object.setPropertyName("PropertyBeanName!");
        object.setPropertyValue(1);
        return object;
    }
}


/**
 * @Author: dingyawu
 * @Description: TODO
 * @Date: Created in 01:11 2023/2/12
 */
public class DispatcherBean {
    private String name;
    private String value;
    private PropertyBean propertyBean;
    public DispatcherBean(){
        this.name="DispatcherBean";
        this.value="abc";
        this.propertyBean=createDispatcherBean();
    }
    protected PropertyBean createDispatcherBean(){
        Enhancer enhancer=new Enhancer();
        enhancer.setSuperclass(PropertyBean.class);
        return (PropertyBean)enhancer.create(PropertyBean.class,new ConcreteClassDispatcher());
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }

    public PropertyBean getPropertyBean() {
        return propertyBean;
    }

    public void setPropertyBean(PropertyBean propertyBean) {
        this.propertyBean = propertyBean;
    }
}



/**
 * 每次获取property bean的属性都会自动触发回调方法加载对象
 * @Author: dingyawu
 * @Description: TODO
 * @Date: Created in 01:22 2023/2/12
 */
public class ProxyTest {

    public void test(){
        DispatcherBean dispatcherBean=new DispatcherBean();
        System.out.println(dispatcherBean.getName());
        System.out.println(dispatcherBean.getValue());

        PropertyBean pb=dispatcherBean.getPropertyBean();
        System.out.println(pb.getPropertyName());
        //在每次访问时都要进行回调
        System.out.println(pb.getPropertyValue());
    }
}

6. 自己产生接口,自己创建代理

/**
 * 自己通过InterfaceMaker 产生一个接口
 * 然后通过enhancer产生一个代理类,被代理的类实现了产生的接口
 */
public class CglibTest implements MethodInterceptor {

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        return JSON.toJSONString(objects);
    }

    @Test
    public void test_cglib() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, NoSuchFieldException {
        // 定义接口
        InterfaceMaker interfaceMaker = new InterfaceMaker();
        interfaceMaker.add(new Signature("sayHi", Type.getType(String.class), new Type[]{Type.getType(String.class)}), null);
        Class<?> interfaceClass = interfaceMaker.create();
        Method[] methods = interfaceClass.getMethods();
        Arrays.stream(methods).forEach(System.out::println);
        // 创建代理
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Object.class);
        enhancer.setInterfaces(new Class[]{interfaceClass});
        enhancer.setCallback(this);
        Object obj = enhancer.create();

        // 调用方法, String.class是表示区分重构
        Method method = obj.getClass().getMethod("sayHi", String.class);
        Object result = method.invoke(obj,"hi yawu");

        System.out.println(result);
    }

}