Mybatis中实现了为数据访问层接口动态创建代理,简化了代码书写方式。我们可以不必手动实现接口了,因为Mybatis在运行时帮我们实现了。
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;
}
public Class<T> getMapperInterface() {
return mapperInterface;
}
public Map<Method, MapperMethod> getMethodCache() {
return methodCache;
}
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
}
MapperProxy是实现了InvocationHandler接口的handler,在创建代理实例时Mapper接口类和MapperProxy作为参数传入,也就是创建了Mapper接口的代理甙类,当调用Mapper接口的方法时,实际上执行了invoke方法。
class MapperProxy implements InvocationHandler, Serializable {
...
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else if (isDefaultMethod(method)) {
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}
...
}
在MapperRegistry中注册动态代理类的工厂实例,mapper接口作为参数新建一个MapperProxyFactory实例
public <T> void addMapper(Class<T> type) {
if (type.isInterface()) {
if (hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
knownMappers.put(type, new MapperProxyFactory<T>(type));
// 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);
}
}
}
}
获取代理类实例,mapper接口和sqlSession做参数,在Spring中实际上sqlSession是SqlSessionTemplate实例,SqlSessionTemplate一个singleton
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
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);
}
}
```
此处可能有个疑惑,为什么SqlSessionTemplate作为一个singleton传入的?
Mybatis中的SqlSession是非线程安全的,每个线程应该有独立的SqlSession实例。所以SqlSession应该是限制在请求范围。使用完毕需要执行关闭动作。
```java
SqlSession session = sqlSessionFactory.openSession();
try {
// do work
} finally {
session.close();
}
<div class="se-preview-section-delimiter"></div>
解开SqlSessionTemplate的秘密
通过查看源码,SqlSessionTemplate实现了SqlSession,并创建了SqlSession的动态代理类。
public class SqlSessionTemplate implements SqlSession, DisposableBean {
...
public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
PersistenceExceptionTranslator exceptionTranslator) {
notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
notNull(executorType, "Property 'executorType' is required");
this.sqlSessionFactory = sqlSessionFactory;
this.executorType = executorType;
this.exceptionTranslator = exceptionTranslator;
this.sqlSessionProxy = (SqlSession) newProxyInstance(
SqlSessionFactory.class.getClassLoader(),
new Class[] { SqlSession.class },
new SqlSessionInterceptor());
}
...
<div class="se-preview-section-delimiter"></div>
SqlSessionTemplate实现SqlSession接口方法,并委托给动态代理类执行,即通过回调SqlSessionInterceptor的invoke方法,
private class SqlSessionInterceptor implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
SqlSession sqlSession = getSqlSession(
SqlSessionTemplate.this.sqlSessionFactory,
SqlSessionTemplate.this.executorType,
SqlSessionTemplate.this.exceptionTranslator);
try {
Object result = method.invoke(sqlSession, args);
if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
// force commit even on non-dirty sessions because some databases require
// a commit/rollback before calling close()
sqlSession.commit(true);
}
return result;
} catch (Throwable t) {
Throwable unwrapped = unwrapThrowable(t);
if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
// release the connection to avoid a deadlock if the translator is no loaded. See issue #22
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
sqlSession = null;
Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException) unwrapped);
if (translated != null) {
unwrapped = translated;
}
}
throw unwrapped;
} finally {
if (sqlSession != null) {
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
}
}
}
}
而在invoke方法中使用了SqlSession的另一个实现DefaultSqlSession来执行真正的数据库查询操作,此处也证明了JDK的动态代理是接口的代理而非类的代理。
SqlSession使用ThreadLocal<Map<Object, Object>> resources
保存,最终SqlSession是每线程一个实例。也消除了为什么SqlSessionTemplate是一个Singleton的疑惑。