mybatis提供了一種非常簡便的方式去訪問數(shù)據(jù)庫,定義接口和sql之后,就能自動幫你完成jdbc操作。這得益于它的mapper機(jī)制,本篇文章在于分析mapper的流程。
一、生成MapperProxyFactory對象
DataSource dataSource = BlogDataSourceFactory.getBlogDataSource();
TransactionFactory transactionFactory = new JdbcTransactionFactory();
Environment environment = new Environment("development", transactionFactory, dataSource);
Configuration configuration = new Configuration(environment);
configuration.addMapper(BlogMapper.class);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
從mybatis的官方示例代碼可以看到,mapper接口的加載是通過Configuration類的addMapper方法去實(shí)現(xiàn)的。
public <T> void addMapper(Class<T> type) {
this.knownMappers.put(type, new MapperProxyFactory(type));
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(this.config, type);
parser.parse();
}
可以看到,這里創(chuàng)建了一個(gè)MapperProxyFactory對象,當(dāng)用戶調(diào)用getMapper方法時(shí),則會調(diào)用該factory生成一個(gè)基于jdk的代理
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
最終調(diào)用方法時(shí),則是通過MapperProxy的invoke方法完成邏輯調(diào)用。
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}
MapperProxy內(nèi)部由MapperMethod的execute完成方法調(diào)用,該方法調(diào)用sqlSession完成操作,即完成了mapper的封裝。
if (SqlCommandType.INSERT == command.getType()) {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
...
二、通過mapperProxyFactory生成Mapper接口的代理對象
mybatis提供了一個(gè)MapperProxyFactory類,通過調(diào)用該類的工廠方法newInstance就可以生成目標(biāo)mapper接口的代理對象。但是想使用spring自動注入的功能,還需要針對每個(gè)mapper接口創(chuàng)建一個(gè)FactoryBean添加到spring的factory中?;貞浺幌挛覀兪褂胢ybatis自動為mapper生成代理的使用方法:
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="orj.worf.mybatis.mapper"/>
<property name="annotationClass" value="org.springframework.stereotype.Repository"/>
</bean>
通過MapperScannerConfigurer配置了要掃描的包,以及一些注解過濾器(選配),來為mapper接口生成對應(yīng)的factoryBean,這個(gè)類實(shí)現(xiàn)了BeanDefinitionRegistryPostProcessor,在spring容器啟動的時(shí)候,該類掃描到符合條件的mapper接口,生成對應(yīng)的FactoryBean:MapperFactoryBean。詳細(xì)的流程如下:

之后將mapper注入到對應(yīng)的業(yè)務(wù)類時(shí),spring會調(diào)用FactoryBean的getObject方法,該方法利用MapperProxyFactory生成mapper的代理對象,。
public T getObject() throws Exception {
return getSqlSession().getMapper(this.mapperInterface);
}
...
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
return mapperProxyFactory.newInstance(sqlSession);
三、MapperProxyFactory的生成策略
第一小節(jié)中我們了解到了調(diào)用Configuration的getMapper方法來為mapper接口生成代理工廠對象MapperProxyFactory這一硬編碼方式。在實(shí)際使用mybatis mapper時(shí),它是怎么自動化給每個(gè)mapper調(diào)用getMapper方法的呢?這一切要從SqlSessionFactoryBean上得到答案。

SqlSessionFactoryBean的afterPropertiesSet掃描mapperLocations下所有的xml映射,最后把xml中mapper標(biāo)簽定義的namespace即mapper接口,利用Configuration的getMapper方法為期生成MapperProxyFactory對象。