Mybatis动态代理源码

目录

1.简单动态代理

2.动态代理为什么不能对类进行代理

3.无实现类完成动态代理

4.mybatis根据接口生成代理类

5.jdk动态代理能不能对类或者抽象类代理

6.总结

7.捋一下源码过程 


1.简单动态代理

Mybatis 底层封装使用的 JDK 动态代理,一般来说定义的JDK 动态代理分为三个步骤

  1. 定义代理接口

  2. 定义代理接口实现类

  3. 定义动态代理调用处理器

  4. 生成代理类调用

public interface Subject { // 定义代理接口
    String sayHello();
}
 
public class SubjectImpl implements Subject {  // 定义代理接口实现类
    @Override
    public String sayHello() {
        System.out.println(" Hello World");
        return "success";
    }
}
 
public class ProxyInvocationHandler implements InvocationHandler {  // 定义动态代理调用处理器
    private Object target;
 
    public ProxyInvocationHandler(Object target) {
        this.target = target;
    }
 
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println(" ???? ???? ???? 进入代理调用处理器 ");
        return method.invoke(target, args);
    }
}
public class ProxyTest {
    public static void main(String[] args) {
        Subject subject = new SubjectImpl();
        Subject proxy = (Subject) Proxy
                .newProxyInstance(
                        subject.getClass().getClassLoader(),
                        subject.getClass().getInterfaces(),
                        new ProxyInvocationHandler(subject));
 
        proxy.sayHello();
        /**
         * 打印输出如下
         * 调用处理器:???? ???? ???? 进入代理调用处理器
         * 被代理实现类:Hello World
         */
    }
}

2.动态代理为什么不能对类进行代理

之前我们有分析过动态代理的源码

生成的代理类继承proxy,又实现了Subject,如果对类进行代理的话,不可能同时继承两个类

我们demo中被代理的接口是有实现类的,而Mybatis中的Mapper接口是没有实现类的,那mybatis是如何实现的呢 

3.无实现类完成动态代理

我们先来看下普通动态代理有没有可能不用实现类,仅靠接口完成 

public interface Subject {
    String sayHello();
}
 
public class ProxyInvocationHandler implements InvocationHandler {
 
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println(" ???? ???? ???? 进入代理调用处理器 ");
        return "success";
    }
}

根据代码可以看到,我们并没有实现接口 Subject,继续看一下怎么实现动态代理

public class ProxyTest {
    public static void main(String[] args) {
        Subject proxy = (Subject) Proxy
                .newProxyInstance(
                        Subject.getClass().getClassLoader(),
                        new Class[]{Subject.class},
                        new ProxyInvocationHandler());
 
        proxy.sayHello();
        /**
         * 打印输出如下
         * 调用处理器:???? ???? ???? 进入代理调用处理器
         */
    }
}

原本我们是通过实现类来获得类加载器,以及接口本身数组,和把实现类当做参数传入到调用处理器中

有实现类接口和无实现类接口产生的动态代理类区别:

  • 有实现类接口是对 InvocationHandler#invoke 方法调用,然后在invoke方法里面对实现类进行反射调用
  • 无实现类接口则是仅对 InvocationHandler#invoke 产生调用,这个invoke方法里返回啥,就返回啥。

InvocationHandler#invoke 方法返回值是 success 字符串,定义个字符串变量,是否能成功返回 

重点就是:Mapper 没有实现类,所有调用 JDBC 等操作都是在 Mybatis InvocationHandler 实现的

4.mybatis根据接口生成代理类

demo中接口固定,mapper可不固定  

看看 Mybatis 底层它怎么实现的动态接口代理,小伙伴只需要关注标记处的代码即可

和我们的 Demo 代码很像,核心点在于 mapperInterface 它是怎么赋值的

先来说一下 Mybatis 代理工厂中具体生成动态代理类具体逻辑:

  1. 根据xml上关联的namespace,通过 Class#forName 反射的方式返回 Class 对象(不止 .xml namespace 一种方式)
  2. 将得到的 Class 对象(实际就是接口对象)传递给 Mybatis 代理工厂生成代理对象,生成的对象即mapperInterface 属性

重点:Mybatis 使用接口全限定名通过 Class#forName 生成 Class 对象,这个 Class 对象类型就是接口 

代码举例:

假设已有接口 AutoConstructorMapper 以及对应的 .xml 如下 

  1. 首先第一步获取 .xml 上 mapper 标签 namespace 属性,得到 mapper 接口全限定信息

  2. 根据 mapper 全限定信息获取 Class 对象

  3. 添加到对应的映射器容器中,等待生成动态代理对象

 如果此时调用生成动态代理对象,代理工厂 newInstance 方法如下:

5.jdk动态代理能不能对类或者抽象类代理

public abstract class AbstractProxy {
    abstract void sayHello();
}
 
AbstractProxy proxyInterface = (AbstractProxy) Proxy
        .newProxyInstance(
                ProxyTest.class.getClassLoader(),
                new Class[]{AbstractProxy.class},
                new ProxyInvocationHandler());
proxyInterface.sayHello();

毫无疑问,报错是必然的,JDK 是不能对类进行代理的 

带着小疑惑我们看一下 Proxy 源码报错位置,JDK 动态代理在生成代理类的过程代码中,会有是否接口验证

底层就判断了是不是接口,是类的话就不行

6.总结

Q:JDK 动态代理能否对类代理?

因为 JDK 动态代理生成的代理类,会继承 Proxy 类,由于 Java 无法多继承,所以无法对类进行代理

Q:抽象类是否可以 JDK 动态代理?

不可以,抽象类本质上也是类,Proxy 生成代理类过程中,会校验传入 Class 是否接口 

Q:Mybatis Mapper 接口没有实现类,怎么实现的动态代理? 

Mybatis 会通过 Class#forname 得到 Mapper 接口 Class 对象,生成对应的动态代理对象,核心业务处理都会在 InvocationHandler#invoke 进行处理,应该是在invoke里面获取的sql语句,然后进行jdbc的相关操作得到最后的结果返回过来

7.捋一下源码过程 

  • 14
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值