博主大二计科学生一枚,正在学习MyBatis框架,在动态代理用Java动态代理机制简单实现MyBatis框架底层时遇到小困难,回去复习Java动态代理机制后,结合自己的理解和ai辅助,写下了这篇笔记方便大家复习后端开发技术,祝大家都能拿高薪、进大厂!

本篇笔记重点Java动态代理机制的详细讲解,已经Github上上传了用Java动态代理机制简单实现MyBatis框架底层

文中的图一、图三引用自黑马程序员的网课,图二引用彩虹糖广告图,如有侵权,本人立即删除

1.动态代理概念

  • 为什么要有动态代理?

    • 假如我们写了一个方法A,后续业务要求我们向这个方法中添加几个新功能B,C,之前我们往往采取的方法是侵入式修改,即在方法外部写好新功能B、C的方法,然后在再要添加功能的方法A中调用新功能方法B、C。

      如:

    • 这种侵入式修改代码的方法在体量小的项目中,可能无伤大雅,没有什么影响,但是如果是大项目,侵入式修改很可能造成修改一处整个项目崩溃的场景,就如下图这个经典广告

    • 此时我们如何无侵入式的给代码添加新的功能呢?

      • 这就要用到我们今天所学的动态代理

  • 动态代理基础概念

    • 定义Java 中的动态代理(Dynamic Proxy)是一种在运行时动态创建代理类和代理对象的机制,常用于 AOP(面向切面编程)、日志记录、权限检查等场景。

    • 什么是代理

      代理是一种设计模式,代理对象代替目标对象对外提供服务,控制对目标对象的访问。

      • 静态代理:代理类在编译时就已经确定了(写死了)。

      • 动态代理:代理类在运行时生成,目标对象也可以动态设置。

    • 特点

      • 无侵入式的给代码添加新功能

      • 通过创建的代理对象添加新功能,代理对象添加新功能之后,接着调用原有方法

    • 动态代理的实现分为两种

      1. 基于JDK动态代理

      2. 基于GGLIB动态代理

2.JDK实现动态代理的思想

  • 基于JDK的动态代理的五个核心

    三类:被代理类、新增功能工具类、代理工具类proxy

    两接口:代理方法接口、调用处理器接口(InvocationHandler

    一对象:由代理工具类proxy创建出的代理对象

    详解既应用动态代理核心过程

    • 要为方法新增功能的类是被代理类,我们将其中所有要新增功能的方法抽象的一个接口中,这个接口便是代理方法接口(有时没有被代理类,直接为代理方法接口新增功能,如下文mybatis框架中实现增删改查的底层原理)

    • 既然要为被代理类(或代理方法接口)中的方法新增功能,就需要一个工具类负责调用原方法和新增功能,这个工具类便是新增功能工具类,新增功能工具类实现调用处理器接口InvocationHandler),并重写其中的invoke方法来完成新增功能、调用原方法等功能

    • 实现完无侵入式方法功能新增后,需要以一个对象来调用新增功能后的方法,这个对象便是由代理类工具类proxy创建出的代理对象(对象类型是被代理类),要调用新增功能的方法,就要知道这个方法的原方法从哪里来(传参被代理类或代理方法接口的类加载器),要知道这个方法约束(传参代理方法接口数组),要知道新增功能后的方法从哪里来(传参代理工具类对象)

3.基于JDK的动态代理实现步骤

步骤一:被代理类将要添加新功能的方法,抽象出来,写到一个代理方法接口中,并且被代理类自己要继承。

  • 注意:代理接口的访问权限,要使代理类能访问

  • //被代理类将要添加新功能的方法,抽象出来
    public interface UserService {
       void login(String username, String password);
       void register(String username);
    }

  • //被代理类自己要继承
    public class UserServiceImpl implements UserService {
       public void login(String username, String password) {
           System.out.println(username + " 登录成功");
      }
    ​
       public void register(String username) {
           System.out.println(username + " 注册成功");
      }
    }

步骤二:创建新增功能工具类实现调用处理器接口,完成invoke方法重写

  • invoke方法参数解释

    • invoke方法参数一:被代理的对象,一般不会使用,按默认的就行

    • invoke方法参数二:被代理类中要被调用的未新增功能的原有方法

    • invoke方法参数三:调用原方法时传递的实参数组

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/*创建新增功能工具类实现调用处理器接口,完成invoke方法重写,实现新增功能、调用原方法*/
public class MyInvocationHandler implements InvocationHandler {
   private Object target;
​
   public MyInvocationHandler(Object target) {
       this.target = target;
  }
​
   // 所有方法调用都会进入 invoke 方法
   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
       System.out.println("方法调用前:" + method.getName());
       Object result = method.invoke(target, args);  // 调用目标方法
       System.out.println("方法调用后:" + method.getName());
       return result;
  }
}

步骤三:创建代理对象调用新增功能的方法

  • 调用位于java.lang.reflect包下的Proxy类的静态方法newProxyInstance

    • newProxyInstance方法参数一:代理工具类的类加载器

    • newProxyInstance方法参数二:代理方法接口的Class对象数组(代理方法接口的字节码文件数组)

    • newProxyInstance方法参数三:新增方法接口InvocationHandler的实现类对象

    • 注意:创建代理对象方法的返回值Object是代理对象

      import java.lang.reflect.Proxy;
      ​
      public class ProxyTest {
         public static void main(String[] args) {
             UserService realService = new UserServiceImpl();
      ​
             UserService proxy = (UserService) Proxy.newProxyInstance(
                 realService.getClass().getClassLoader(),
                 realService.getClass().getInterfaces(),
                 new MyInvocationHandler(realService)
            );
      ​
             proxy.login("张三", "123456");
             proxy.register("张三");
        }
      }
      ​

4用动态代理手动编写MyBatis增删改查底层

该篇幅需要有一定maven项目管理、MyBatis框架、Java反射、泛型的基础,如果没有请跳过

该篇幅只介绍各个文件对应动态代理部分,

详细代码见Github--https://github.com/Simonchu-73/mybatis_learn/tree/master/mybatis_day03_custom

注:代码只用动态代理完成了MyBatis中查询SElECT * FROM student的底层,欢迎各位同僚继续补充

其他部分若有不懂地方可私信问

其中com.xzit.mapper路径下StudentMapper是要代理方法接口

其中com.xzit.util路径下ProxyImp是要新增功能工具类

其中com.xzit.util路径下CustomSqlSession是要中由代理工具类创建了代理对象

其中com.xzit.test路径下Test是要完成了功能测试

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐