一,SqlSessionFactory 初始化
在基本的 MyBatis 中,sessionFactory可以使用 SqlSessionFactoryBuilder 来创建。而在 MyBatis-spring 中,则使用 SqlSessionFactoryBean 来替代,在SqlSessionFactoryBean 其实还是调用了sqlSessionFactoryBuilder.build(configuration)来创建sessionFactory。
相当于spring的SqlSessionFactoryBean 做了一层封装,通过实现spring的接口这读取配置,填充configuration,然后再创建sessionFactory。
定义SqlSessionFactoryBean:
<bean id="session_cmail_mail_web" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="ds_cmail_web"/>
<property name="configLocation">
<value>classpath:xml/sqlmap/mysql-mail-base-map-config.xml</value>
</property>
</bean>
查看SqlSessionFactoryBean
public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
实现了FactoryBean和InitializingBean,所以在bean初始化时,会执行afterPropertiesSet(),查看代码:
/**
* {@inheritDoc}
*/
public void afterPropertiesSet() throws Exception {
Assert.notNull(dataSource, "Property 'dataSource' is required");
Assert.notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
this.sqlSessionFactory = buildSqlSessionFactory();
}
在buildSqlSessionFactory() 这个方法中,读取,创建sessionFactory .
截取主要方法:
//读取<configuration> 配置
if (this.configLocation != null) {
xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(),
null, this.configurationProperties);
configuration = xmlConfigBuilder.getConfiguration();
} else {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Property 'configLocation' not specified, using default
MyBatis Configuration");
}
configuration = new Configuration();
}
//如果SqlSessionFactoryBean的property有配置typeAliasesPackage,则写入configuration
if (StringUtils.hasLength(this.typeAliasesPackage)) {
String[] typeAliasPackageArray =
StringUtils.tokenizeToStringArray(this.typeAliasesPackage,
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
for (String packageToScan : typeAliasPackageArray) {
configuration.getTypeAliasRegistry().registerAliases(packageToScan);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Scanned package: '" + packageToScan + "' for
aliases");
}
}
}
//如果SqlSessionFactoryBean的property有配置typeAliases,则写入configuration
if (!ObjectUtils.isEmpty(this.typeAliases)) {
for (Class<?> typeAlias : this.typeAliases) {
configuration.getTypeAliasRegistry().registerAlias(typeAlias);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Registered type alias: '" + typeAlias + "'");
}
}
}
//如果SqlSessionFactoryBean的property有配置plugins,则写入configuration
if (!ObjectUtils.isEmpty(this.plugins)) {
for (Interceptor plugin : this.plugins) {
configuration.addInterceptor(plugin);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Registered plugin: '" + plugin + "'");
}
}
}
if (StringUtils.hasLength(this.typeHandlersPackage)) {
String[] typeHandlersPackageArray = StringUtils.tokenizeToStringArray(this.typeHandlersPackage,
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
for (String packageToScan : typeHandlersPackageArray ) {
configuration.getTypeHandlerRegistry().register(packageToScan);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Scanned package: '" + packageToScan + "' for type handlers");
}
}
}
if (!ObjectUtils.isEmpty(this.typeHandlers)) {
for (TypeHandler typeHandler : this.typeHandlers) {
configuration.getTypeHandlerRegistry().register(typeHandler);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Registered type handler: '" + typeHandler + "'");
}
}
}
//在这个方法中,会读取<configuration> 中的所有配置包括typeAliases,plugins,mappers...
if (xmlConfigBuilder != null) {
try {
xmlConfigBuilder.parse();
if (this.logger.isDebugEnabled()) {
this.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();
}
}
//使用spring事务管理
if (this.transactionFactory == null) {
this.transactionFactory = new
SpringManagedTransactionFactory(this.dataSource);
}
Environment environment = new Environment(this.environment,
this.transactionFactory, this.dataSource);
configuration.setEnvironment(environment);
//如果有sqlSessionFactoryBean有配置mappers,则读取
if (!ObjectUtils.isEmpty(this.mapperLocations)) {
for (Resource mapperLocation : this.mapperLocations) {
if (mapperLocation == null) {
continue;
}
// this block is a workaround for issue
http://code.google.com/p/mybatis/issues/detail?id=235
// when running MyBatis 3.0.4. But not always works.
// Not needed in 3.0.5 and above.
String path;
if (mapperLocation instanceof ClassPathResource) {
path = ((ClassPathResource) mapperLocation).getPath();
} else {
path = mapperLocation.toString();
}
try {
XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
configuration, path, configuration.getSqlFragments());
xmlMapperBuilder.parse();
} catch (Exception e) {
throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
} finally {
ErrorContext.instance().reset();
}
if (this.logger.isDebugEnabled()) {
this.logger.debug("Parsed mapper file: '" + mapperLocation + "'");
}
}
} else {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Property 'mapperLocations' was not specified or no matching resources found");
}
}
return this.sqlSessionFactoryBuilder.build(configuration);
二.MapperFactoryBean的作用
定义MapperFactoryBean,主要是生成sqlSession以及把Mapper接口添加到Configuration对象中。MapperFactoryBean实现了FactoryBean接口,所以会使用getObject(),
public T getObject() throws Exception {
return getSqlSession().getMapper(this.mapperInterface);
}
2.1生成sqlSession时使用动态代理:
this.sqlSessionProxy = (SqlSession) Proxy.newProxyInstance(
SqlSessionFactory.class.getClassLoader(),
new Class[] { SqlSession.class },
new SqlSessionInterceptor());
2.2 把Mapper接口添加到Configuration对象
@Override
protected void checkDaoConfig() {
super.checkDaoConfig();
Assert.notNull(this.mapperInterface, "Property 'mapperInterface' is required");
Configuration configuration = getSqlSession().getConfiguration();
if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
try {
configuration.addMapper(this.mapperInterface);
} catch (Throwable t) {
logger.error("Error while adding the mapper '" + this.mapperInterface + "'
to configuration.", t);
throw new IllegalArgumentException(t);
} finally {
ErrorContext.instance().reset();
}
}
}