Spring 从入门到精通 (十六) AOP底层如何创建动态代理类

关键词:Spring | AOP | 创建代理类 | 底层

本专栏通过理论和实践相结合,系统学习框架核心思想及简单原理,原创不易,如果觉得文章对你有帮助,点赞收藏支持博主 ✨

在这里插入图片描述

目录

  • 一、创建对象三要素
  • 二、动态代理类的创建
    • 2.1、JDK 动态代理
      • 2.1.1、第三个参数,完成附加操作
      • 2.1.2、第二个参数,实现同一接口
      • 2.1.3、第一个参数,类加载器
      • 2.1.4、编码
    • 2.2、Cglib 动态代理
      • 2.2.1、编码
  • 三、总结
  • 四、写在最后

一、创建对象三要素

  • 原始对象
  • 额外功能
  • 原始对象要和代理对象实现同一个接口

二、动态代理类的创建

  • JDK 动态代理
  • cglib 动态代理

2.1、JDK 动态代理

public static void main(String[] args) {
    //1. 创建原始对象
    UserService userService = new UserServiceImpl();
    //2. 创建动态代理,使用JDK
    Proxy.newProxyInstance(loader, interfaces, InvocationHandler);
}

2.1.1、第三个参数,完成附加操作

JDK的动态代理是实现的第三个参数, InvocationHandler 接口中的 invoke()

UserService userService = new UserServiceImpl();
/**
 * 作用:用于编写额外功能,可以运行在原始方法前,后,前后,异常等情况
 * @param proxy 表示代理对象,可以忽略
 * @param method 额外功能加给哪个原始方法
 * @param args 原始方法参数
 * @return 原始方法的返回值
 */
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    //额外功能
    //反射调用,更灵活
    Object invoke = method.invoke(userService, args);
    return invoke;
}

接口实现和之前学习的 MethodInterceptor很像,其实方法拦截器就是由这个方法封装过的



2.1.2、第二个参数,实现同一接口

第二个参数是 Class[] interfaces ,原始对象所实现的接口,只有拿到相同的接口,才能根据接口进一步代理

UserService.getClass().getInterfaces()

2.1.3、第一个参数,类加载器

第一个参数是 ClassLoader loader,又叫类加载器,主要作用有两个:

  1. 可以通过类加载器把字节码文件加载到虚拟机
  2. 可以通过类加载器创建类的Class对象,进而创建类对象

如何获得类加载器?

每一个类的.class文件,都会被自动分配与之对应的类加载器(Class Loader)

但是动态代理类使用了动态字节码技术来创建字节码,没有源文件和字节码文件,直接将字节码写入虚拟机,没有加载的字节码的过程,那我们要获得代理对象,前面拿到原始类对象的字节码文件后,可以创建代理类对象了吗?

不可以,因为想要创建代理类对象,必须先获得代理类 Class 对象,就需要类加载器的帮助,但是动态代理没有 .clsss 文件,也就没有与之分配的类加载器

但是此时需要类加载器,怎么办?借一个,完成代理类 Class 对象的创建,进而完成代理类对象的创建

2.1.4、编码

TestJDKProxy

public class TestJDKProxy {
    public static void main(String[] args) {
        //1. 创建原始对象
        UserService userService = new UserServiceImpl();
        //2. 创建动态代理,使用JDK
        InvocationHandler handler = (proxy, method, args1) -> {
            System.out.println("--------Proxy log4--------");
            // 执行方法
            Object reg = method.invoke(userService, args1);
            return reg;
        };
        UserService useServiceProxy = (UserService) Proxy.newProxyInstance(TestJDKProxy.class.getClassLoader(), userService.getClass().getInterfaces(), handler);
        useServiceProxy.login("哈哈", "123");
    }
}

Result

--------Proxy log4--------
UserServiceImpl login + DAO

没有任何问题

2.2、Cglib 动态代理

Cglib是通过父子继承关系创建代理对象,原始类作为父类,代理类作为子类,这样既可以保证既可以保证方法一致的情况下,在代理类中提供新的实现(原始方法 + 额外功能)

2.2.1、编码

TestCglib

public class TestCglib {
    public static void main(String[] args) {
        //1.创建原始对象 UserService
        UserService userService = new UserService();
        //2.通过Cglib创建动态代理对象
        Enhancer enhancer = new Enhancer();
        enhancer.setClassLoader(TestCglib.class.getClassLoader());
        enhancer.setSuperclass(userService.getClass());
        MethodInterceptor methodInterceptor = new MethodInterceptor(){
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                System.out.println("---Cglib log---");
                Object ret = method.invoke(userService, objects);
                return ret;
            }
        };
        enhancer.setCallback(methodInterceptor);
        UserService userServiceProxy = (UserService) enhancer.create();
        userServiceProxy.login("哈哈", "12312312");
    }
}

Result

---Cglib log---
核心功能,登录操作

三、总结

JDK: 通过接口创建代理类对象
Cglib: 通过继承创建代理类对象

有接口使用 JDK ,没有就是用 Cglib

四、写在最后

座右铭:不要在乎别人如何看你,要在乎你自己如何看未来,看梦想,看世界…!

一起学习的可以私信博主或添加博主微信哦。

专栏:订阅专栏 ✅
关注:关注博主

你可能感兴趣的:(Spring框架从入门到精通,spring,java,后端,AOP,底层)