MyBatis和Spring结合时为什么只写Dao层不写其实现类

前言:

了需要提前了解的知识点:

  1. JDK动态代理:https://mp.csdn.net/mdeditor/90598309#
  2. Mybatis简单Demo学习: https://www.mybatis.org/mybatis-3/zh/getting-started.html
  3. Sprng和Mybatis简单Demo构建: http://www.mybatis.org/spring/zh/getting-started.html

代码构建:

  1. Dao、Mapper、spring-mybatis.xml 文件的编写 省略:
  2. 创建测试类 MybatisSpringTest:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath*:/spring-mybatis.xml"})
@Slf4j
public class MybatisSpringTest {

    @Autowired
    private ClassInfoDao classInfoDao;

    @Test
    public void testSrpingMybatis() throws IOException {
        ClassInfo classInfo = classInfoDao.selectClassByClassId(1);
        System.err.println("classInfo : " + JSONObject.toJSONString(classInfo));
        
        // -------- 将代理类反编译到文件中  ($Proxy17 需要根据Debug中显示定义,可能会不同)----------------
        byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy17", new Class[]{ClassInfoDao.class});
        String path = "F:/mycode/Git/spring-mvc/learn/mybatis/mybatis-learn/src/test/java/com/lot/Proxy17.class";
        try {
            FileOutputStream fos = new FileOutputStream(path);
            fos.write(classFile);
            fos.flush();
            System.out.println("代理类写入成功");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

代码流程分析:

  1. 当我们Debug 运行时,可以 classInfoDao 的定义,如下图所示:
    MyBatis和Spring结合时为什么只写Dao层不写其实现类_第1张图片
    当我们在调用ClassInfoDao 实际上是 $ Proxy17(JDK 动态代理生成的对象),其中代理类 h 是 MapperProxy,通过 ProxyGenerator.generateProxyClass 将$Proxy17 生成class文件,class内容如下:
public final class $Proxy17 extends Proxy implements ClassInfoDao {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m4;
    private static Method m0;

    public $Proxy17(InvocationHandler var1) throws  {
        super(var1);
    }

    static {
        try {
        m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
        m2 = Class.forName("java.lang.Object").getMethod("toString");
        m3 = Class.forName("com.lot.dao.ClassInfoDao").getMethod("selectClassByClassId", Class.forName("java.lang.Integer"));
        m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
        throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
        throw new NoClassDefFoundError(var3.getMessage());
        }
        }

    public final ClassInfo selectClassByClassId(Integer var1) throws  {
        try {
            return (ClassInfo)super.h.invoke(this, m3, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }
    // 省略  hashCode  toString  equals方法
    
}
  1. 哦吼,很显然当我们代码执行 classInfoDao.selectClassByClassId 时 实际调用的是 JDK动态代理生成的对象 $Proxy17. selectClassByClassId 方法,而 super.h.invoke() 执行的是 org.apache.ibatis.binding.MapperProxy#invoke 方法,这一步已经由我们自己的代码跳转到 Spring中了。
  2. 最后当我们一步步Debug 时会发现最终调用的是Mybatis的 sqlSession.selectOne(),最终执行sql。 自此,Spring的 工作就已经完成了,后面就要交给Mybatis 来处理。

总结:

  1. Mybatis 为什么Dao不用实现?
    简: 当我们启动项目时,Mybatis 会将我们的Mapper文件(或者注解)配置的 Sql 以及其类和方法组成唯一的 Key 保存起来, 并且Spring 通过JDK动态代理的方式和 MapperProxy 帮我们生成代理类,当我们供我们调用。极大的简化我们开发流程。

你可能感兴趣的:(#,Mybatis)