为什么去学习MyBatis
优点:
- MyBatis简单易学,只需要简单的几个xml文件配置,即可
- 灵活:因为mybatis是sql是放在xml文件中代码和sql分离,可以做统一优化和管理,提供动态标签更灵活编写sql
缺点:
-
移植性差,以为sql是依赖数据库的
-
编写sql量大
代码
@Service
public class UserServiceImpl implements UserService{
@Autowired
UserMapper userMapper;
@Override
public User findUserById(Integer id) {
//为什么接口却可以直接执行?
User user = userMapper.selectOne(id);
return user;
}
}
我们并没有写userMapper的实现类,那Mybatis怎么调用的?想知道这个我们首先要了解下动态代理,动态代理有cglib和JDK实现的动态代理
JDK动态代理
首先代理类要实现 InvocationHandler 这个接口
public class ProxyExample implements InvocationHandler{
/**
*
* @param proxy 代理类
* @param method 执行的方法
* @param args 方法参数
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return "Hello Proxy ";
}
}
我们要代理的接口创建如下:
public interface TargetProxy {
public String getProxyHello();
}
下面补全代理的代码,让代理类代理TargetProxy接口
public class ProxyExample implements InvocationHandler{
/**
*
* @param proxy 代理类
* @param method 执行的方法
* @param args 方法参数
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return "Hello Proxy ";
}
/**
* 拿到需要代理的类
* @return
*/
public Object setTargetProxy(){
/**
* TargetProxy.class.getClassLoader() 目标类的类加载器
* new Class[]{TargetProxy.class} 目标类的接口class对象
* this 代理类 ,因为代理类是本类, 调用目标类时会执行代理类的 invoke 方法
*/
return Proxy.newProxyInstance(TargetProxy.class.getClassLoader() ,new Class[]{TargetProxy.class} ,this);
}
public static void main(String[] args) {
//创建代理类
ProxyExample proxyExample = new ProxyExample();
//获取目标对象
TargetProxy targetProxy = (TargetProxy)proxyExample.setTargetProxy();
//执行目标对象方法
String proxyHello = targetProxy.getProxyHello();
System.out.println(proxyHello);
}
}
执行该main方法:
Hello Proxy
那我们来看代理模式加深下印象:
到此我们看到了,接口直接执行方法却得到了我们想要的结果, 同样MyBatis也是这样做的,下面我们看看mybatis的调用。
@Service
public class UserServiceImpl implements UserService {
@Autowired
SqlSessionFactory sqlSessionFactory;
@Override
public void removeById(Integer id) {
//获取sqlsession
SqlSession sqlSession = sqlSessionFactory.openSession();
//通过sqlsession获取mapper
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//执行删除
mapper.deleteOne(id);
}
}
我们可以看到获取的顺序 : sqlSessionFactory->SqlSession->Mapper
那我们先看SqlSessionFactory它是一个接口:
有两个实现类:
defaultSqlSessionFactory:
public class DefaultSqlSessionFactory implements SqlSessionFactory {
private final Configuration configuration;
public DefaultSqlSessionFactory(Configuration configuration) {
this.configuration = configuration;
}
@Override
public SqlSession openSession() {
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
......
}
可以看到获取sqlSession的方法有很多我们看其中一个:
//通过执行类型和事务获取执行器 ,然后创建默认defaultsqlsession
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
//获取环境变量
final Environment environment = configuration.getEnvironment();
// 事务工厂
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
//创建事务
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
//创建执行器
final Executor executor = configuration.newExecutor(tx, execType);
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
closeTransaction(tx); // may have fetched a connection so lets call close()
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
可以看到defaultSqlSessionFactory中的SqlSession是直接new DefaultSqlSession创建的接下来看下 SqlSessionManage
public class SqlSessionManager implements SqlSessionFactory, SqlSession {
private final SqlSessionFactory sqlSessionFactory;
private final SqlSession sqlSessionProxy;
private final ThreadLocal<SqlSession> localSqlSession = new ThreadLocal<SqlSession>();
private SqlSessionManager(SqlSessionFactory sqlSessionFactory) {
this.sqlSessionFactory = sqlSessionFactory;
this.sqlSessionProxy = (SqlSession) Proxy.newProxyInstance(
SqlSessionFactory.class.getClassLoader(),
new Class[]{SqlSession.class},
new SqlSessionInterceptor());
}
}
看他构造函数,是不是很眼熟,使用了我们上面说的代理模式 ,被代理的对象是SqlSessionFactory,代理对象是 SqlSessionInterceptor,等执行SqlsessionFactory的时候会执行代理类,看下代理类的实现:
private class SqlSessionInterceptor implements InvocationHandler {
public SqlSessionInterceptor() {
// Prevent Synthetic Access
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//从当前线程去拿sqlsession
final SqlSession sqlSession = SqlSessionManager.this.localSqlSession.get();
if (sqlSession != null) {
try {
return method.invoke(sqlSession, args);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
} else {
//否则就从新打开一个session连接
final SqlSession autoSqlSession = openSession();
try {
final Object result = method.invoke(autoSqlSession, args);
autoSqlSession.commit();
return result;
} catch (Throwable t) {
autoSqlSession.rollback();
throw ExceptionUtil.unwrapThrowable(t);
} finally {
autoSqlSession.close();
}
}
}
}
这个执行的代理方法是在ThreadLocal中存放一个sqlsession,如果同一个线程,他会直接去ThreadLocal中去拿取SqlSession,如果没有get到,则直接重新重SqlSessionFactory中创建一个新的连接。可以避免连接的频繁创建销毁带来的性能损耗。到此为止我们基本上可以看到创建SqlSession的流程了 :
下面就要看怎么获取的Mapper接口了
public interface SqlSession extends Closeable {
......
<T> T getMapper(Class<T> type);
......
}
最终调用的是DefaultSqlSession->Configuration->MapperRegister中的getMapper方法,如果是idea可以一直点Ctrl+左键到最终执行的实现
public class MapperRegistry {
private final Configuration config;
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<Class<?>, MapperProxyFactory<?>>();
public MapperRegistry(Configuration config) {
this.config = config;
}
@SuppressWarnings("unchecked")
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
//mapper的class对象
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
return mapperProxyFactory.newInstance(sqlSession);//记住这一句下面会用到,回头来看看
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
public <T> boolean hasMapper(Class<T> type) {
return knownMappers.containsKey(type);
}
......
}
可以仔细看下getMapper方法他是从一个Map中去出来的,那我们就要去看看这个map是怎么添加进去的,Key和Value都是什么,下面也是这个mapperRegister类中的方法addMapper
public class MapperRegistry {
private final Configuration config;
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<Class<?>, MapperProxyFactory<?>>();
public MapperRegistry(Configuration config) {
this.config = config;
}
public <T> void addMapper(Class<T> type) {
if (type.isInterface()) {
if (hasMapper(type)) {//校验有重复的mapper
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
knownMappers.put(type, new MapperProxyFactory<T>(type));//创建一个对应的mapper 的mapperproxyfactory
// It's important that the type is added before the parser is run
// otherwise the binding may automatically be attempted by the
// mapper parser. If the type is already known, it won't try.
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
}
可以看出添加的时候是Key是接口class对象,value是MapperProxyFactory对象,那我们去看看这个对象
public class MapperProxyFactory<T> {
private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>();
public MapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
......
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
//返回代理的mapper接口,会执行invoke
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
//初始化mapperproxy类
final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
}
看newInstance(SqlSession sqlSession)这个方法,前面特别提醒的代码执行的是这个方法,可以看到首先创建了一个MapperProxy对象,我们点进去会发现又是我们前面学习的动态代理模式。之后调用newInstance(MapperProxy mapperProxy)方法里面是一个绑定代理对象和目标对象的方法,仔细看下不难发现,被代理类mapperInterface是我们创建MapperProxyFactory的时候传入进来的,mapperProxy是我们刚刚创建的。那现在我们知道了执行mapper的方法,调用的是代理类invoke的方法:
public class MapperProxy<T> implements InvocationHandler, Serializable {
private static final long serialVersionUID = -6424540398559729838L;
private final SqlSession sqlSession;
private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache;
public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
this.sqlSession = sqlSession;
this.mapperInterface = mapperInterface;
this.methodCache = methodCache;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {//如果是object类的方法直接执行
return method.invoke(this, args);
} else if (isDefaultMethod(method)) {//如果是java编译器使用下面方法调用
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
final MapperMethod mapperMethod = cachedMapperMethod(method);
//执行方法
return mapperMethod.execute(sqlSession, args);
}
private MapperMethod cachedMapperMethod(Method method) {
MapperMethod mapperMethod = methodCache.get(method);
if (mapperMethod == null) {
mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
//缓存方法的解析结果,不用每次调用都进行解析
methodCache.put(method, mapperMethod);
}
return mapperMethod;
}
......
}
重点看invoke方法,最后执行的是executor方法。分析到现在我们大概对我们为什么mapper接口可以直接执行方法有了个大致了解。但是我们还是不知道,为什么执行executor方法就能执行我们所写的sql语句,下面章介绍这个