用大白话理解Spring框架中的动态代理

本文深入解析动态代理的概念,对比静态代理,阐述动态代理如何在运行时动态获取并增强被代理类中的方法,通过具体代码示例展示实现过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

动态代理

  1. 何为动态代理?

答: 可以简单理解为在不改变原本类中代码的情况下,为类中的方法(可以为一个或者多个)提供更多的功能,美滋滋。

  1. 为什么要使用动态代理而不使用静态代理?

先定义两个说法:1.哪个类中的方法想要被加强,我称这个类为被代理类 2.使拥某个类来加强被代理类中的方法,这个类我称为代理类(很容易理解,别被绕晕了)

答:使用动态代理可以使程序员在运行期动态地获取到被代理类中的所有方法(注意,是可以获得被代理类中的所有方法!),获得方法之后便能在一个代理类中对这些方法进行加强

实现动态代理

实现动态代理,我们做好三步,一步一步套着来,就能完成

  • 代理类要实现一个InvocationHandle接口
  • 使用Proxy类的newProxyInstance()方法创建一个动态代理对象
    这里有点小讲究 newProxyInstance()方法需要三个参数,
    newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
    //loader - 定义被代理类的类加载器
    //interfaces - 代理类要实现的接口列表
    //h - 指派方法调用的调用处理程序
    

这里不知道填什么没关系,我们先往后看

在这里插入图片描述
这里是我的代码文件结构:
damain 为JavaBean包, proxy包为代理类包,service为实现功能的包(在本例中即为被代理类包)

User类就是一个基本的JavaBean
在这里插入图片描述
UserService是一个功能接口
在这里插入图片描述

UserServiceImpl实现了这个功能接口
在这里插入图片描述
准备好这些之后,重点就是我们的代理类了

public class UserProxy implements InvocationHandler {

    private UserService userService;

    public UserProxy(UserService userService) {
        this.userService = userService;
    }


    //当login方法执行的时候会回调该方法,所以实际增强的效果是在这里
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        Object obj = null;

        if("login".equals(method.getName())) {
            System.out.println("登录功能执行前的记录日志....");

            obj = method.invoke(userService, args);

            if(obj != null) {
                System.out.println("恭喜您登录成功");
            }
        }
        return obj;
    }

    public UserService loginPlus() {
        //这里先准备好Proxy.newProxyInstance()方法所需要的三个参数

        //loader - 定义被代理类的类加载器(哪个类要被增强,就是哪个类的类加载器)
        ClassLoader loader = userService.getClass().getClassLoader();

        //classes - 代理类要实现的接口列表
        Class[] classes = new Class[]{UserService.class};

        Object obj = Proxy.newProxyInstance(loader, classes, this);

        UserService userService = (UserService) obj;

        return userService;
    }
}

代码很多,不要急,一个一个来解释

这一步是把被代理类对象放进来,为什么要放进来呢?因为就是要对被代理类中的方法进行增强嘛,当然要把方法的类对象引进来,不然怎么获得到类中的方法呢?

在这里插入图片描述
这个方法,是在获取到加强后的UserService类对象,这里最重要也是最复杂的一个地方,就是
Proxy.newProxyInstance(loader, classes, this);
this参数传的就是UserProxy这个类对象,因为这个参数需要一个InvocationHandle的类对象,而正好UserProxy实现了这个接口,因此可以直接将本类的对象传过去

调用这个方法会返回一个Object类型的值,其实,Proxy.newProxyInstance(loader, classes, this);这个方法底层就是调用了上边的invoke方法,invoke方法如下图
在这里插入图片描述
这个方法中有参数method,就是值被代理类中的方法。Object[]数组里边装的是被代理类方法的所需要的参数,method.invoke()方法所需要的参数为(传进此代理类的被代理类对象,要被加强的方法所需的参数,直接填args即可),这样一来就可以获得一个Object对象,将此对象 return给对象它的Proxy.newProxyInstance(),因此Proxy.newProxyInstance()可以获得一个Object类对象,而这个Object类对象,实际上就是被增强后的被代理类对象。

在这里插入图片描述

最后,在测试类中完成测试,先实例化一个UserProxy对象,再用这个对象去调用loginPlus()来获得被加强后的被代理对象,此时再用被代理对象去调用login登录方法,即完成了方法的增强
在这里插入图片描述
而下面两句代码的输出结果可以看出来,被代理类对象传进代理类的加强方法中之后,返回的对象已经发生了改变(这种查物理地址的方法还是阳哥教我的/—\)。

 System.out.println(System.identityHashCode(service));
 System.out.println(System.identityHashCode(userService));
### C++ 指针概念的大白话解释 #### 什么是指针? 指针是一个特殊的变量,它的值是另一个变量的地址。换句话说,指针存储的是内存位置而不是具体的数值[^1]。 例如,在C++中声明一个整数类型的指针: ```cpp int *ptr; ``` 这里的`*`表示这是一个指向整数类型数据的指针。如果有一个整数变量`num`: ```cpp int num = 10; ``` 那么可以通过取址运算符`&`获取这个变量的地址并赋给指针: ```cpp ptr = &num; ``` 此时`ptr`保存的就是`num`所在的内存地址。 #### 如何理解指针的作用? 想象一下图书馆里的书架编号系统。假设有一本书放在某个特定的位置上(比如第7排第3层),而你只需要记住这本书所在的具体坐标就能快速找到它。同样地,在计算机程序里,当创建了一个变量之后,操作系统会给其分配一段独一无二的记忆空间;如果我们想操作该变量的内容而不直接提及名称的话,则需要用到能够记录下这段记忆区域起始处的信息——这就是所谓的“指针”。 另外值得注意的一点是在定义多个同种类型的指针时,把星号紧挨着类型名可以帮助程序员更好地理解和维护代码逻辑[^2]: ```cpp // 这样写更清晰明了 int* p1, p2; // 只有p1是指向int的指针,p2不是 int *q1,*q2; // q1,q2都是指向int的指针 ``` #### 动态内存管理中的应用 除了作为普通变量地址外,指针还广泛应用于动态内存分配场景之中。例如使用`new`关键字可以在堆区申请一块新的未被使用的连续字节序列来构建对象实例,并返回这块新辟出来的首部位置供后续调用者存入实际的数据结构体成员或者数组元素等等[^3]。 ```cpp MyClass* obj = new MyClass(); delete obj; ``` 以上就是关于C++指针的一些基础介绍以及大白话理解方式。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值