什么是Mybatis
MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。
Mybatis优势
- 与JDBC相比,减少了50%以上的代码量。
- MyBatis实现了接口绑定,无需自己写DAO实现类.
- MyBatis是最简单的持久化框架,小巧并且简单易学。
- MyBatis相当灵活,不会对应用程序或者数据库的现有设计强加任何影响,SQL写在XML里,从程序代码中彻底分离,降低耦合度,便于统一管理和优化,并可重用
Mybatis实现原理
我们知道,在Mybatis中声明一个interface接口,没有编写任何实现类,Mybatis就能返回接口实例,并调用接口方法返回数据库中的数据。实现这个功能的底层原理主要是底层采用了jdk动态代理。
动态代理
动态代理的主要功能是:通过拦截器方法回调,对目标target方法进行增强。言外之意就是为了增强目标target方法,这句话确实是讲的没有错误,但是在动态代理的过程中我们也可以做到连目标tagert都不要的霸权。
自定义自动映射器Mapper
首先我们先不研究源码,通过仿照源码的格式来自定义我们的自动映射器。
其中我们的domain类还是采用Student类,注意加上一个带参构造器。
public class Student {
private Integer id;
private String name;
public Student(Integer id,String name) {
this.id=id;
this.name=name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "[" + "id:" + id + " name:" + name + "]";
}
}
dao层接口中的方法:getStudentById()。
public interface StudentMapper {
public Student getStudentById(Integer id);
}
实现InvocationHandler
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class MapperProxy implements InvocationHandler {
public <T> T newInstance(Class<T> clz) {
return (T) Proxy.newProxyInstance(clz.getClassLoader(), new Class[] { clz }, this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (Object.class.equals(method.getDeclaringClass())) {
try {
// 诸如hashCode()、toString()、equals()等方法,将target指向当前对象this
return method.invoke(this, args);
} catch (Throwable t) {
}
}
return new Student((Integer) args[0], "hongchen.zhx");
}
}
在MapperProxy类中,我们可以看到在执行Object对象的方法时,target被指向了this,此时target已经可以说是成为一个傀儡了。已经没有target什么事了。
编写测试代码进行测试。
public class Demo {
public static void main(String[] args) {
MapperProxy proxy = new MapperProxy();
StudentMapper mapper = proxy.newInstance(StudentMapper.class);
Student student= mapper.getStudentById(1001);
System.out.println("ID:" + student.getId());
System.out.println("Name:" + student.getName());
System.out.println(mapper.toString());
}
}
那么测试代码运行后的结果为:
真正的MapperProxy
通过以上自定义的Mapper映射器再来看相对应的源码就会轻松很多。
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (Object.class.equals(method.getDeclaringClass())) {
try {
//在执行诸如Object的方式时,target指向当前对象this。
return method.invoke(this, args);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
//从缓存中获取MapperMethod对象,如果缓存中没有,则创建新的MapperMethod对象并添加到缓存中。
final MapperMethod mapperMethod = cachedMapperMethod(method);
//调用MapperMethod.execute()方法执行sql语句。
return mapperMethod.execute(sqlSession, args);
}