1.SqlSessionFactoryBean
<!-- spring和MyBatis完美整合,不需要mybatis的配置映射文件 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="typeAliasesPackage" value="com.enjoylearning.mybatis.entity" />
<property name="mapperLocations" value="classpath:sqlmapper/*.xml" />
</bean>
public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
實(shí)現(xiàn)FactoryBean接口的getObject方法:
/**
*
* 將SqlSessionFactory對(duì)象注入spring容器
* {@inheritDoc}
*/
@Override
public SqlSessionFactory getObject() throws Exception {
if (this.sqlSessionFactory == null) {
afterPropertiesSet();
}
return this.sqlSessionFactory;
}
SqlSessionFactoryBean實(shí)現(xiàn)InitializingBean接口砌创,需要實(shí)現(xiàn)其afterPropertiesSet():
@Override
//在spring容器中創(chuàng)建全局唯一的sqlSessionFactory
public void afterPropertiesSet() throws Exception {
notNull(dataSource, "Property 'dataSource' is required");
notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null),
"Property 'configuration' and 'configLocation' can not specified with together");
this.sqlSessionFactory = buildSqlSessionFactory();
}
核心是buildSqlSessionFactory:
protected SqlSessionFactory buildSqlSessionFactory() throws IOException {
Configuration configuration;
XMLConfigBuilder xmlConfigBuilder = null;
if (this.configuration != null) {//如果configuration不為空事甜,則使用該對(duì)象碑韵,并對(duì)其進(jìn)行配置
configuration = this.configuration;
if (configuration.getVariables() == null) {
configuration.setVariables(this.configurationProperties);
} else if (this.configurationProperties != null) {
configuration.getVariables().putAll(this.configurationProperties);
}
} else if (this.configLocation != null) {//創(chuàng)建xmlConfigBuilder留晚,讀取mybatis的核心配置文件
xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
configuration = xmlConfigBuilder.getConfiguration();
} else {//如果configuration為空,實(shí)例化一個(gè)configuration對(duì)象
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Property 'configuration' or 'configLocation' not specified, using default MyBatis Configuration");
}
configuration = new Configuration();
if (this.configurationProperties != null) {
configuration.setVariables(this.configurationProperties);
}
}
//設(shè)置objectFactory
if (this.objectFactory != null) {
configuration.setObjectFactory(this.objectFactory);
}
//設(shè)置objectWrapperFactory
if (this.objectWrapperFactory != null) {
configuration.setObjectWrapperFactory(this.objectWrapperFactory);
}
//設(shè)置vfs
if (this.vfs != null) {
configuration.setVfsImpl(this.vfs);
}
//掃描指定的包typeAliasesPackage,注冊(cè)別名
if (hasLength(this.typeAliasesPackage)) {
String[] typeAliasPackageArray = tokenizeToStringArray(this.typeAliasesPackage,
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
for (String packageToScan : typeAliasPackageArray) {
configuration.getTypeAliasRegistry().registerAliases(packageToScan,
typeAliasesSuperType == null ? Object.class : typeAliasesSuperType);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Scanned package: '" + packageToScan + "' for aliases");
}
}
}
//為typeAliases指定的類注冊(cè)別名
if (!isEmpty(this.typeAliases)) {
for (Class<?> typeAlias : this.typeAliases) {
configuration.getTypeAliasRegistry().registerAlias(typeAlias);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Registered type alias: '" + typeAlias + "'");
}
}
}
//注冊(cè)插件
if (!isEmpty(this.plugins)) {
for (Interceptor plugin : this.plugins) {
configuration.addInterceptor(plugin);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Registered plugin: '" + plugin + "'");
}
}
}
//掃描指定的包typeHandlersPackage箍镜,注冊(cè)類型解析器
if (hasLength(this.typeHandlersPackage)) {
String[] typeHandlersPackageArray = tokenizeToStringArray(this.typeHandlersPackage,
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
for (String packageToScan : typeHandlersPackageArray) {
configuration.getTypeHandlerRegistry().register(packageToScan);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Scanned package: '" + packageToScan + "' for type handlers");
}
}
}
//為typeHandlers指定的類注冊(cè)類型解析器
if (!isEmpty(this.typeHandlers)) {
for (TypeHandler<?> typeHandler : this.typeHandlers) {
configuration.getTypeHandlerRegistry().register(typeHandler);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Registered type handler: '" + typeHandler + "'");
}
}
}
//配置databaseIdProvider
if (this.databaseIdProvider != null) {//fix #64 set databaseId before parse mapper xmls
try {
configuration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
} catch (SQLException e) {
throw new NestedIOException("Failed getting a databaseId", e);
}
}
//配置緩存
if (this.cache != null) {
configuration.addCache(this.cache);
}
//使用xmlConfigBuilder讀取mybatis的核心配置文件
if (xmlConfigBuilder != null) {
try {
xmlConfigBuilder.parse();
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Parsed configuration file: '" + this.configLocation + "'");
}
} catch (Exception ex) {
throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex);
} finally {
ErrorContext.instance().reset();
}
}
//默認(rèn)使用SpringManagedTransactionFactory作為事務(wù)管理器
if (this.transactionFactory == null) {
this.transactionFactory = new SpringManagedTransactionFactory();
}
//設(shè)置Environment
configuration.setEnvironment(new Environment(this.environment, this.transactionFactory, this.dataSource));
//根據(jù)mapperLocations的配置,處理映射配置文件以及相應(yīng)的mapper接口
if (!isEmpty(this.mapperLocations)) {
for (Resource mapperLocation : this.mapperLocations) {
if (mapperLocation == null) {
continue;
}
try {
XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
configuration, mapperLocation.toString(), configuration.getSqlFragments());
xmlMapperBuilder.parse();
} catch (Exception e) {
throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
} finally {
ErrorContext.instance().reset();
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Parsed mapper file: '" + mapperLocation + "'");
}
}
} else {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Property 'mapperLocations' was not specified or no matching resources found");
}
}
//最終使用sqlSessionFactoryBuilder創(chuàng)建sqlSessionFactory
return this.sqlSessionFactoryBuilder.build(configuration);
}
可知SqlSessionFactoryBean主要通過對(duì)applicationContext.xml解析完成時(shí)Configuration的實(shí)例化以及對(duì)完成對(duì)映射配置文件mapper*.xml的解析渔欢。
因?yàn)檫@個(gè)方法內(nèi)有兩個(gè)關(guān)鍵類:
*XMLConfigBuilder:在mybatis中主要負(fù)責(zé)解釋mybatis-config.xml
XMLMapperBuilder:負(fù)責(zé)解析映射配置文件
2.MapperFactoryBean
2.1 MapperFactoryBean測(cè)試
applicationContext.xml配置墓塌,注入MapperFactoryBean:
<bean id="tUserMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="com.enjoylearning.mybatis.mapper.TUserMapper"></property>
<property name="sqlSessionFactory" ref="sqlSessionFactory"></property>
</bean>
UserServiceImpl.java中按名字注入tUserMapper:
@Service
public class UserServiceImpl implements UserService{
@Resource(name="tUserMapper")
private TUserMapper userMapper;
@Override
public TUser getUserById(Integer id) {
return userMapper.selectByPrimaryKey(id);
}
}
測(cè)試代碼:
public class MybatisSpringTest {
@Resource
private UserService us;
@Test
public void TestSpringMyBatis(){
System.out.println(us.getUserById(1).toString());
}
測(cè)試結(jié)果:
TUser [id=1, userName=lison, realName=李小宇, sex=1, mobile=186995587422, email=lison@qq.com, note=lison的備注, positionId=1]
2.2 MapperFactoryBean源碼解析
實(shí)現(xiàn)FactoryBean接口的getObject方法:
/**
* 通過在容器中的mapperRegistry,返回當(dāng)前mapper接口的動(dòng)態(tài)代理
*
* {@inheritDoc}
*/
@Override
public T getObject() throws Exception {
return getSqlSession().getMapper(this.mapperInterface);
}
調(diào)用SqlSessionTemplate的getMapper:
@Override
public <T> T getMapper(Class<T> type) {
return getConfiguration().getMapper(type, this);
}
調(diào)用Configuration的getMapper:
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
調(diào)用MapperRegistry的getMapper:
@SuppressWarnings("unchecked")
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);
}
}
調(diào)用MapperProxyFactory的newInstance:
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
2.2.1 這里解決了一個(gè)重要問題
@Test
// 測(cè)試自動(dòng)映射以及下劃線自動(dòng)轉(zhuǎn)化駝峰
public void quickStart() throws IOException {
// 2.獲取sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 3.獲取對(duì)應(yīng)mapper
TUserMapper mapper = sqlSession.getMapper(TUserMapper.class);
// 4.執(zhí)行查詢語句并返回結(jié)果
TUser user = mapper.selectByPrimaryKey(1);
System.out.println(user.toString());
}
TUserMapper是方法級(jí)別的,而注入到Spring的容器中顯然不是苫幢。
@Service
public class UserServiceImpl implements UserService{
@Resource(name="tUserMapper")
private TUserMapper userMapper;
@Override
public TUser getUserById(Integer id) {
return userMapper.selectByPrimaryKey(id);
}
}
MyBatis-Spring是通過FactoryBean來解決的访诱,F(xiàn)actoryBean每次通過getObject來生成新的動(dòng)態(tài)代理對(duì)象來解決這個(gè)問題,本質(zhì)上與MyBatis中沒有區(qū)別韩肝!
3.一種更方便的方法——MapperScannerConfigurer
MapperScannerConfigurer會(huì)將指定basePackage下面的所有接口都以MapperFactoryBean形式注冊(cè)到Spring容器中触菜。
<!-- DAO接口所在包名,Spring會(huì)自動(dòng)查找其下的類 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.enjoylearning.mybatis.mapper" />
</bean>
public class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {
核心實(shí)現(xiàn)了BeanDefinitionRegistry后置處理器BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry哀峻,主要是對(duì)bean的定義信息進(jìn)行修改增強(qiáng)涡相,例如對(duì)bean的類型進(jìn)行轉(zhuǎn)換:
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
if (this.processPropertyPlaceHolders) {//占位符處理
processPropertyPlaceHolders();
}
//實(shí)例化ClassPathMapperScanner,并對(duì)scanner相關(guān)屬性進(jìn)行配置
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
scanner.setAddToConfig(this.addToConfig);
scanner.setAnnotationClass(this.annotationClass);
scanner.setMarkerInterface(this.markerInterface);
scanner.setSqlSessionFactory(this.sqlSessionFactory);
scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
scanner.setResourceLoader(this.applicationContext);
scanner.setBeanNameGenerator(this.nameGenerator);
scanner.registerFilters();//根據(jù)上述配置剩蟀,生成過濾器催蝗,只掃描合條件的class
//掃描指定的包以及其子包
scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}
調(diào)用ClassPathBeanDefinitionScanner:
public int scan(String... basePackages) {
int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
doScan(basePackages);
// Register annotation config processors, if necessary.
if (this.includeAnnotationConfig) {
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}
return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
}
調(diào)用ClassPathMapperScanner的doScan:
@Override
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
//通過父類的掃描,獲取所有復(fù)合條件的BeanDefinitionHolder對(duì)象
Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
if (beanDefinitions.isEmpty()) {
logger.warn("No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
} else {
//處理掃描得到的BeanDefinitionHolder集合育特,將集合中的每一個(gè)mapper接口轉(zhuǎn)換成MapperFactoryBean后丙号,注冊(cè)至spring容器
processBeanDefinitions(beanDefinitions);
}
return beanDefinitions;
}
轉(zhuǎn)換成MapperFactoryBean:
//處理掃描得到的BeanDefinitionHolder集合,將集合中的每一個(gè)mapper接口轉(zhuǎn)換成MapperFactoryBean后缰冤,注冊(cè)至spring容器
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
GenericBeanDefinition definition;
//遍歷集合
for (BeanDefinitionHolder holder : beanDefinitions) {
definition = (GenericBeanDefinition) holder.getBeanDefinition();
if (logger.isDebugEnabled()) {
logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName()
+ "' and '" + definition.getBeanClassName() + "' mapperInterface");
}
// the mapper interface is the original class of the bean
// but, the actual class of the bean is MapperFactoryBean
//將添加掃描到的接口類型作為構(gòu)造函數(shù)的入?yún)? definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName()); // issue #59
//講bean的類型轉(zhuǎn)換成mapperFactoryBean
definition.setBeanClass(this.mapperFactoryBean.getClass());
//增加addToConfig屬性
definition.getPropertyValues().add("addToConfig", this.addToConfig);
boolean explicitFactoryUsed = false;
//增加sqlSessionFactory屬性
if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
explicitFactoryUsed = true;
} else if (this.sqlSessionFactory != null) {
definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
explicitFactoryUsed = true;
}
//增加sqlSessionTemplate屬性
if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
if (explicitFactoryUsed) {
logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
}
definition.getPropertyValues().add("sqlSessionTemplate", new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
explicitFactoryUsed = true;
} else if (this.sqlSessionTemplate != null) {
if (explicitFactoryUsed) {
logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
}
definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
explicitFactoryUsed = true;
}
//修改自動(dòng)注入的方式 bytype
if (!explicitFactoryUsed) {
if (logger.isDebugEnabled()) {
logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
}
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
}
}
}
4.總結(jié)
MyBatis集成到Spring后犬缨,只有兩件事稍微有不同:
- 1)Cofiguration的實(shí)例化是讀取applicationContext.xml,而不是mybatis-config.xml
- 2)mapper對(duì)象是方法級(jí)別的棉浸,Spring通過FactoryBean進(jìn)行增強(qiáng)巧妙地解決了這一問題
參考
- 1)享學(xué)課堂Lison老師筆記