专栏首页cmazxiaoma的架构师之路浅谈CGLIB动态代理和JDK动态代理 学习笔记

浅谈CGLIB动态代理和JDK动态代理 学习笔记

前言

前几天,写一个case,做单元的测试。抛出了依赖注入失败的异常,然后发现是没有配置CGLIB动态代理的原因,默认的JDK动态代理只能基于接口去代理,被代理的类必须要实现一个接口。而CGLIB动态代理可以基于类。

JDK动态代理实现AOP

  • 定义统一的接口类IUserService
public interface IUserService {

    void login();
}
  • 定义UserService去实现这个接口,加入自己的逻辑。
public class UserService implements IUserService {

    @Override
    public void login() {
        System.out.println("login success");
    }
}
  • 定义动态代理类UserDynamicProxy去实现InvocationHandler接口。invoke()方法中可以加入自定义的切面逻辑。被代理的类中方法的执行是由method.invoke(target, args)完成的。
public class UserDynamicProxy implements InvocationHandler {
    private Object target;

    public UserDynamicProxy(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("login before");
        Object result = method.invoke(target, args);
        System.out.println("login after");
        return result;
    }
}
  • 接下来我们写一下测试类,运行一下程序。
public class DymaticProxyDemo {

    public static void main(String[] args) {
        IUserService userService = new UserService();
        UserDynamicProxy handler = new UserDynamicProxy(userService);
        IUserService serviceProxy = (IUserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(),
                userService.getClass().getInterfaces(), handler);

        serviceProxy.login();
    }
}

image.png

  • Proxy.newProxyInstance()方法可以返回一个指定接口的代理类的实例。我们可以到Proxy类源码是这样讲到。
>    /**
     * Returns an instance of a proxy class for the specified interfaces
     * that dispatches method invocations to the specified invocation
     * handler.
     *
     * <p>{@code Proxy.newProxyInstance} throws
     * {@code IllegalArgumentException} for the same reasons that
     * {@code Proxy.getProxyClass} does.
     *
     * @param   loader the class loader to define the proxy class
     * @param   interfaces the list of interfaces for the proxy class
     *          to implement
     * @param   h the invocation handler to dispatch method invocations to
     * @return  a proxy instance with the specified invocation handler of a
     *          proxy class that is defined by the specified class loader
     *          and that implements the specified interfaces

Proxy.newProxyInstance()返回的的是一个指定接口的代理类的实例,其调用方法可以调用到指定的调用程序。也就是说serviceProxy.login()这个方法,其实是调用到UserDynamicProxy实例中的invoke()方法。

newProxyInstance()方法中,第一个参数是加载类加载器去定义代理类,第二个参数是接口代理类的接口列表,第三个参数是InvocationHandler接口的实现类实例去调用处理程序来进行相关的 处理。


CGLIB动态代理实现AOP

  • 定义一个Dog类,CGLIB动态代理不需要去定义目标类的同一接口。
public class Dog {

    public void call() {
        System.out.println("wang wang wang");
    }
}
  • 定义CglibProxy类,它需要去实现MethodInterceptor这个接口。我们可以在intercept()方法中,去加入自己的切面逻辑。目标类方法的调用是 proxy.invokeSuper(object, args)触发的。
public class CglibProxy implements MethodInterceptor {

    @Override
    public Object intercept(Object object, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("before");
        proxy.invokeSuper(object, args);
        System.out.println("after");
        return null;
    }
}
  • 接下来我们定义一个工厂类,去获得代理类的实例。
public class Factory {

    public static Dog getInstance(CglibProxy proxy) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Dog.class);
        enhancer.setCallback(proxy);
        Dog rayDog = (Dog) enhancer.create();
        return rayDog;
    }
}

首先去获取Enhancer类的实例,调用enhancer.setSuperclass()设置目标类class对象。观看setSuperClass(),会去判断这个class对象是否为空,或者是否为接口,是否为Objectclass对象。

    public void setSuperclass(Class superclass) {
        if(superclass != null && superclass.isInterface()) {
            this.setInterfaces(new Class[]{superclass});
        } else if(superclass != null && superclass.equals(Object.class)) {
            this.superclass = null;
        } else {
            this.superclass = superclass;
        }

    }

enhancer.setCallback()这个方法,我们需要传入CglibProxy的实例作为回调的对象,并且把它放入到一个Callback类型的数组里面。然后去判断这个数组是否合法。

    public void setCallback(Callback callback) {
        this.setCallbacks(new Callback[]{callback});
    }

    public void setCallbacks(Callback[] callbacks) {
        if(callbacks != null && callbacks.length == 0) {
            throw new IllegalArgumentException("Array cannot be empty");
        } else {
            this.callbacks = callbacks;
        }
    }

最后一步enhancer.create()返回的是一个增强的目标类的实例,此时的rayDog对象已经不是以前那个Dog类的, 而是一个增强后的Dog类的实例了。dog究极进化成了镭射狗(X-ray Dog的歌很好听哟,有一首叫Panorama)。我们可以看到create()方法是这样实现的。

    public Object create() {
        this.classOnly = false;
        this.argumentTypes = null;
        return this.createHelper();
    }

    private Object createHelper() {
        this.preValidate();
        Object key = KEY_FACTORY.newInstance(this.superclass != null?this.superclass.getName():null, ReflectUtils.getNames(this.interfaces), this.filter == ALL_ZERO?null:new WeakCacheKey(this.filter), this.callbackTypes, this.useFactory, this.interceptDuringConstruction, this.serialVersionUID);
        this.currentKey = key;
        Object result = super.create(key);
        return result;
    }

    private void preValidate() {
        if(this.callbackTypes == null) {
            this.callbackTypes = CallbackInfo.determineTypes(this.callbacks, false);
            this.validateCallbackTypes = true;
        }

        if(this.filter == null) {
            if(this.callbackTypes.length > 1) {
                throw new IllegalStateException("Multiple callback types possible but no filter specified");
            }

            this.filter = ALL_ZERO;
        }

    }

create()方法里面调用了createHelper()createHelper()里面又去调用了preValidate()。 我们进行DEBUG调试的时候,可以进行完preValidate()这个方法后,会发现this.callbackTypes就是MethodInterceptor类型的TYPE

this.callbackTypes = CallbackInfo.determineTypes(this.callbacks, false)会去解析传入的Callback数组的类型,并且把他们放入到一个Type类型的数组里。相关必要的操作做完,就传入Enhancer.EnhancerKey的实例,调用create(Object key)去生成增强后的目标类实例。

image.png

  • 最后编写测试类,启动程序。
public class CglibDemo {

    public static void main(String[] args) {
        Dog rayDog = Factory.getInstance(new CglibProxy());
        rayDog.call();
    }
}

image.png


尾言

勿以善小而不为

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Ribbon源码分析

    对于Spring中的AnnotationMetadata不太熟悉的同学,可以跑一下下面的CASE

    用户2032165
  • SpringBoot之路(二)之Web进阶

    用户2032165
  • 为什么我的HibernateDaoSupport没有注入SessionFactory

    1.按理来说Spring应该会通过setSessionFactory方法将SessionFactory注入进来,可是并没有。

    用户2032165
  • 七夕最污代码,单身慎入

    2.找不到对象说爱(Fatal error: Call to a member function on a non-object), 他怎么才能说出爱?

    后端技术探索
  • Spring中@Import的各种用法以及ImportAware接口

    @Import注解提供了和XML中<import/>元素等价的功能,实现导入的一个或多个配置类。@Import即可以在类上使用,也可以作为元注解使用。

    Coder小黑
  • Spring中@Import的各种用法以及ImportAware接口

    @Import注解提供了和XML中<import/>元素等价的功能,实现导入的一个或多个配置类。@Import即可以在类上使用,也可以作为元注解使用。

    用户1516716
  • Java编程思想第五版(On Java8)(十一)-内部类

    内部类是一种非常有用的特性,因为它允许你把一些逻辑相关的类组织在一起,并控制位于内部的类的可见性。然而必须要了解,内部类与组合是完全不同的概念,这一点很重要。在...

    JavaEdge
  • Mybatis源码简易导读

    TransactionFactory 事务工厂接口,源码如下,主要的作用构造事务的配置信息

    用户2603479
  • 你应该了解的5种TypeScript设计模式

    本文最初发布于 Medium 网站,经原作者授权由 InfoQ 中文站翻译并分享。

    深度学习与Python
  • SpringMVC 中的Annotated Controllers

    上面透漏一下信息:是一个GET响应 查找web下的index模版,通过model将数据传递给模版引擎渲染

    大话swift

扫码关注云+社区

领取腾讯云代金券