在前面的MyBatis中Mapper接口的扫描过程一文中介绍到,MyBatis将扫描到的mapper接口对应的bean定义的类型设置为了MapperFactoryBean。这是一个FactoryBean,Spring会调用它的getObject方法来创建mapper对象。本文就来分析一下这是怎样一个过程。
Mapper对象的创建过程会经过很多个类,下面一个个来分析。
MapperFactoryBean
构造方法
private Class<T> mapperInterface;
private boolean addToConfig = true;
public MapperFactoryBean() {
// intentionally empty
}
public MapperFactoryBean(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
在MyBatis中Mapper接口的扫描过程一文中介绍到,在处理bean定义时,会将mapper接口的类型设置为bean的构造器参数。所以实际执行的时候执行的是有参构造方法。另外也会设置addToConfig属性的值,只不过两边的默认值都是true。
另外MapperFactoryBean继承了父类SqlSessionDaoSupport。
private SqlSessionTemplate sqlSessionTemplate;
// 会被setter注入,MyBatisAutoConfiguration中会注册SqlSessionFactory
public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
if (this.sqlSessionTemplate == null || sqlSessionFactory != this.sqlSessionTemplate.getSqlSessionFactory()) {
/*
* 从SqlSessionFactory创建SqlSessionTemplate,但是在Spring Boot中,
* 因为会注入一个SqlSessionTemplate(在下面的setter方法上打个断点就知道了),所以这里设置的其实无效。
*/
this.sqlSessionTemplate = createSqlSessionTemplate(sqlSessionFactory);
}
}
public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
this.sqlSessionTemplate = sqlSessionTemplate;
}
注意,因为在MybatisAutoConfigurataion中会注册一个SqlSessionTemplate的方法,上面的setter方法会被调用,而且会覆盖构造方法中创建的。
@Bean
@ConditionalOnMissingBean
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
ExecutorType executorType = this.properties.getExecutorType();
/*
* 创建SqlSessionTemplate对象,后续创建Mapper代理对象会被用到。
*/
if (executorType != null) {
return new SqlSessionTemplate(sqlSessionFactory, executorType);
} else {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
private final SqlSessionFactory sqlSessionFactory;
private final ExecutorType executorType;
private final SqlSession sqlSessionProxy;
private final PersistenceExceptionTranslator exceptionTranslator;
public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
this(sqlSessionFactory, sqlSessionFactory.getConfiguration().getDefaultExecutorType());
}
public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType) {
this(sqlSessionFactory, executorType,
new MyBatisExceptionTranslator(sqlSessionFactory.getConfiguration().getEnvironment().getDataSource(), true));
}
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;
// 创建SQlSession的代理对象
this.sqlSessionProxy = (SqlSession) newProxyInstance(SqlSessionFactory.class.getClassLoader(),
new Class[] { SqlSession.class }, new SqlSessionInterceptor());
}
创建bean对象
@Override
public T getObject() throws Exception {
// 通过sqlSession来创建mapper代理对象
return getSqlSession().getMapper(this.mapperInterface);
}
public SqlSession getSqlSession() {
return this.sqlSessionTemplate;
}
SqlSessionTemplate
@Override
public <T> T getMapper(Class<T> type) {
// 通过Configuration对象来获取Mapper对象
return getConfiguration().getMapper(type, this);
}
@Override
public Configuration getConfiguration() {
return this.sqlSessionFactory.getConfiguration();
}
直接调用了Configuration对象的getMapper方法。
Configuration
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
这里又直接调用了MapperRegistry的getMapper方法。
MapperRegistry
// 记录mapper文件中的namespace配置的类
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();
@SuppressWarnings("unchecked")
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
// 获取创建mapper对象的工厂
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
// 如果没有与该类型相关的mapper配置文件
if (mapperProxyFactory == null) {
// 则抛出异常
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
/*
* 创建Mapper代理对象
*/
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
现在只需要知道这里的knownMappers是在MyBatis解析了mapper文件中就会往里面添加元素即可。继续看创建mapper对象的逻辑。
MapperFactory
MapperFacotry类非常简单,整个类的代码都不长,该类的目的就是为了创建mapper对象。
public class MapperProxyFactory<T> {
private final Class<T> mapperInterface;
private final Map<Method, MapperMethodInvoker> methodCache = new ConcurrentHashMap<>();
public MapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
public Class<T> getMapperInterface() {
return mapperInterface;
}
public Map<Method, MapperMethodInvoker> getMethodCache() {
return methodCache;
}
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
// 通过JDK动态代理创建代理对象
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
// 创建执行处理器对象,mapper接口中的方法会被该处理器中的invoke方法拦截
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
// 对象mapper对象
return newInstance(mapperProxy);
}
}
重点关注上面的两个newInstance方法,可以发现,MyBatis使用了Java的动态代理功能来为接口生成实现代理类,并创建其实例,即为本文所分析的目标:Mapper对象。
这里创建代理时传入的InvocationHandler是MapperProxy类型,其invoke方法实现了增强逻辑。所以要分析mapper方法,就从该方法入手。
总结
本文分析了MyBatis中mapper对象的创建过程,最终发现了MyBatis是通过Java动态代理来为mapper接口生成代理实现类,并创建代理对象。