mybatis接口没有实现类,实现动态代理的过程
mapper接口定义方法
public interface StudentMapper {
Student getStudent(Integer id);
}
mapper.xml文件编写sql语句
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ojbk.mapper.StudentMapper">
<!-- 联合查询-->
<select id="getStudent" resultMap="StudentClass">
select s.* ,c.* from student s left join class c on s.cid=c.cid where s.sid=#{id}
</select>
<resultMap id="StudentClass" type="student">
<id property="sid" column="sid"/>
<result property="sname" column="sname"/>
<association property="classroom" javaType="class">
<id property="cid" column="cid"/>
<result property="cname" column="cname"/>
</association>
</resultMap>
</mapper>
mybatis核心配置文件及数据库设置省略
测试
public class myTest throws IOException {
//1.读取核心配置文件
InputStream in = Resources.getResourceAsStream("mybatisConfig.xml");
//2.创建工厂对象
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
//3.取出sqlSession
SqlSession sqlSession = factory.openSession(true);//自动提交事务
//4.取出动态代理的对象,完成接口中方法的调用,实则是调用xml文件中相的标签的功能
StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
//5.调用接口中的方法
Student student = studentMapper.getStudent(1);
System.out.println(student);
//6.关闭sqlSession
sqlSession.close();
}
代码分析
1.读取核心配置文件
InputStream in = Resources.getResourceAsStream("mybatisConfig.xml");
主要是读取核心配置文件mybatisConfig.xml并将其放入bufferedInputStream。
2.创建工厂对象,完成数据库连接池与数据库间的连接。
(1)首先调用SqlSessionFactory工厂的builder方法
(2)调用 XMLConfigBuilder
1)首先调用父类BaseBuilder方法new一个Configuration对象
主要包含 :这三个类的初始化及赋值
(1)configuration 配置类
(2)typeAliasRegistry 类型别名注册器
(3)typeHandlerRegistry 类型处理器注册器
关于该父类的详解超链接
BaseBuilder解析
2)ReflectorFactory
点进去看的话,是一个判断和设置Mybatis反射类Reflector对象缓存的东西
3) ErrorContext
这个方法是mybatis用于相关的异常日志输出,可以得到异常是由谁在做什么的时候在哪个资源文件中发生的,执行的 SQL 是哪个,以及 java 详细的异常信息。
详解超链接
ErrorContext
4) 其他
下面几个就是一些属性设置,根据这个parsed的布尔值为false执行下面步骤 (3) 的解析获取。
(3) XNode对象 root解析
主要包含了数据库连接的相关信息
并最后将其返回给configuration对象
(4)根据configuration中内容new一个默认的sqlsessionFactory工厂对象
3.从factory工厂中取出一个sqlSession连接对象
可以设置事务的自动提交
4.mybatis动态代理
可以看到读取了StudentMapper接口类对象后,调用configuration对象的getMapper方法,再调用MapperRegistry注册器的getMapper方法,最终创建了一个MapperProxyFactory对象,即mapper类的动态代理工厂。
可以发现mybatis这里使用的动态代理和jdk动态代理类proxy一样
区别在于:
(1)jdk动态代理的ClassLoader传入的是接口实现类
mybatis传入的是接口
mapperInterface
mybatis使用接口全限定名通过class#forName生成class对象,而这个对象类型就是接口
(2)jdk动态代理的Class中传入的是接口实现类对象的class,读取所有接口
mybatis的Class传入的是接口本身。
MapperProxy
实现了invocationHandler类和Serializable类
说明该类首先可以被缓存(Mybatis的缓存)
在我们利用Mapper进行数据库的相关操作时会调用MapperProxy的invoke()方法,该方法先判断接口方法是否有实现类,如果有的话调用实现类的方法,如果没有的话,调用cachedInvoker的方法
区别(3)jdk动态代理调用的都是实现类的方法,返回的是实现类的值
mybatis没有实现类,因此返回的是invoke方法的返回值。
即mybatis实现接口的方法都在MapperProxy这invocationHandler类中实现。
sqlsession对象
可以看到MapperProxy实现接口中的方法又主要依靠封装再sqlsession对象中的MapperStatement对象实现。
总结:
JDK动态代理和mybatis动态代理的区别
(1)jdk动态代理的ClassLoader传入的是接口实现类。
mybatis传入的是接口。
(2)jdk动态代理的Class中传入的是接口实现类对象的class,读取所有接口
mybatis的Class传入的是接口本身。
(3)jdk动态代理调用的都是实现类的方法,返回的是实现类的值
mybatis没有实现类,因此返回的是invoke方法的返回值。即mybatis实现接口的方法都在MapperProxy这invocationHandler类中实现。