mybatis 源碼分析(一) Xml解析,mapper bean初始化
mybatis
mybatis 作爲一個輕量級的orm 框架 具有減少sql編寫,提高開發效率的能力.而且在插件化這塊 做到了可插拔功能.
實現了 簡單 易用
使用mybaits maven座標
groupid | artifactId | version |
---|---|---|
org.mybatis | mybatis | 3.2.8 |
org.mybatis | mybatis-spring | 1.2.3 |
tk.mybatis | mapper | 3.3.0 |
mybatis 配置文件
申明mybatis mapper.xml 路徑 插件 數據源配置等
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd">
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="driverClassName" value="xxx" />
<property name="url" value="xxx" />
<property name="username" value="xxx" />
<property name="password" value="xxx" />
<property name="maxActive" value="xxx" />
<property name="initialSize" value="xxx" />
<property name="minIdle" value="xxx" />
<property name="maxWait" value="xxx" />
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="mapperLocations">
<list>
<value>classpath:mapper/*.xml</value>
</list>
</property>
<property name="typeAliasesPackage" value="com.sun.model"/>
<!-- 配置插件 -->
<property name="plugins">
<array>
<bean class="com.sun.plugins.DataSourceSharePlugin"/>
</array>
</property>
</bean>
<bean class="tk.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.sun.mapper.**"/>
<property name="markerInterface" value="com.sun.mapper.base.BaseBillMapper"/>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
</bean>
</beans>
這裏面最重要的2個類 就是
org.mybatis.spring.SqlSessionFactoryBean: 字面意思 sqlSession的生產工廠
tk.mybatis.spring.mapper.MapperScannerConfigurer: 掃描mapper接口 生成一個代理類 並注入到spring 容器中
SqlSessionFactoryBean 怎麼來完成初始化的
主要完成:mybatis mapper.xml的映射 插件初始化 sqlSessiong構建
這個類 實現了InitializingBean 接口 ,這個接口的只提供了一個afterPropertiesSet 方法,spring 容器在初始化bean之後就會執行該方法
public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
/**
* {@inheritDoc}
*/
@Override
public void afterPropertiesSet() throws Exception {
notNull(dataSource, "Property 'dataSource' is required");
notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
/** 構造factory */
this.sqlSessionFactory = buildSqlSessionFactory();
}
protected SqlSessionFactory buildSqlSessionFactory() throws IOException {
/** 配置類 */
Configuration configuration;
XMLConfigBuilder xmlConfigBuilder = null;
if (this.configLocation != null) {
xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
configuration = xmlConfigBuilder.getConfiguration();
} else {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Property 'configLocation' not specified, using default MyBatis Configuration");
}
configuration = new Configuration();
configuration.setVariables(this.configurationProperties);
}
if (this.objectFactory != null) {
configuration.setObjectFactory(this.objectFactory);
}
if (this.objectWrapperFactory != null) {
configuration.setObjectWrapperFactory(this.objectWrapperFactory);
}
/** 設置類型別名的package */
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");
}
}
}
if (!isEmpty(this.typeAliases)) {
for (Class<?> typeAlias : this.typeAliases) {
configuration.getTypeAliasRegistry().registerAlias(typeAlias);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Registered type alias: '" + typeAlias + "'");
}
}
}
/** 設置插件 */
if (!isEmpty(this.plugins)) {
for (Interceptor plugin : this.plugins) {
configuration.addInterceptor(plugin);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Registered plugin: '" + plugin + "'");
}
}
}
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");
}
}
}
if (!isEmpty(this.typeHandlers)) {
for (TypeHandler<?> typeHandler : this.typeHandlers) {
configuration.getTypeHandlerRegistry().register(typeHandler);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Registered type handler: '" + typeHandler + "'");
}
}
}
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();
}
}
if (this.transactionFactory == null) {
this.transactionFactory = new SpringManagedTransactionFactory();
}
/** 設置 事務 數據源 */
configuration.setEnvironment(new Environment(this.environment, this.transactionFactory, this.dataSource));
if (this.databaseIdProvider != null) {
try {
configuration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
} catch (SQLException e) {
throw new NestedIOException("Failed getting a databaseId", e);
}
}
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());
/** 實現mapper xml解析 */
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");
}
}
/** 構造sqlSessionFactory */
return this.sqlSessionFactoryBuilder.build(configuration);
}
SqlSessionFactoryBuilder
public class SqlSessionFactoryBuilder {
//省略其他方法
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
}
DefaultSqlSessionFactory
public class DefaultSqlSessionFactory implements SqlSessionFactory {
/** 其他方法省略 */
public DefaultSqlSessionFactory(Configuration configuration) {
this.configuration = configuration;
}
/**
* sqlSession 是mybatis 的sql執行器 默認有 DefaultSqlSession
* SqlSessionManager SqlSessionTemplate 3個實現類
* 默認使用 DefaultSqlSession 實現
*/
public SqlSession openSession(boolean autoCommit) {
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, autoCommit);
}
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
/** 最終sql的執行器 默認使用 SimpleExecutor */
final Executor executor = configuration.newExecutor(tx, execType);
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
closeTransaction(tx); // may have fetched a connection so lets call close()
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
}
接下來 看另外一個重要的類
tk.mybatis.spring.mapper.MapperScannerConfigurer
主要功能:完成mapper interface 接口生成代理類 並注入到spring的容器中
/** 繼承了mybatis 的 MapperScannerConfigurer 由於 父類 實現了
* BeanDefinitionRegistryPostProcessor 接口
* 所以初始化會調用 postProcessBeanDefinitionRegistry 方法.
* */
public class MapperScannerConfigurer extends org.mybatis.spring.mapper.MapperScannerConfigurer {
/**
* 註冊完成後,對MapperFactoryBean的類進行特殊處理
*
* @param registry
*/
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
super.postProcessBeanDefinitionRegistry(registry);
//如果沒有註冊過接口,就註冊默認的Mapper接口
this.mapperHelper.ifEmptyRegisterDefaultInterface();
String[] names = registry.getBeanDefinitionNames();
GenericBeanDefinition definition;
for (String name : names) {
BeanDefinition beanDefinition = registry.getBeanDefinition(name);
if (beanDefinition instanceof GenericBeanDefinition) {
definition = (GenericBeanDefinition) beanDefinition;
if (StringUtil.isNotEmpty(definition.getBeanClassName())
&& definition.getBeanClassName().equals("org.mybatis.spring.mapper.MapperFactoryBean")) {
/** mapper 代理類生產工廠 */
definition.setBeanClass(MapperFactoryBean.class);
definition.getPropertyValues().add("mapperHelper", this.mapperHelper);
}
}
}
}
}
public class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {
/**
* {@inheritDoc}
* 掃描mapper inteface
* @since 1.0.2
*/
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
if (this.processPropertyPlaceHolders) {
processPropertyPlaceHolders();
}
/** 掃描並注入spring bean容器 */
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();
scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}
}
public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner {
/** 省略部分代碼 */
private Class<?> markerInterface;
private MapperFactoryBean mapperFactoryBean = new MapperFactoryBean();
/** 生成mybatis mapper對應的 MapperFactoryBean */
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");
}
//通過反射設置屬性值
definition.getPropertyValues().add("mapperInterface", definition.getBeanClassName());
definition.setBeanClass(this.mapperFactoryBean.getClass());
definition.getPropertyValues().add("addToConfig", this.addToConfig);
boolean explicitFactoryUsed = false;
//MapperFactoryBean 中 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;
}
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;
}
if (!explicitFactoryUsed) {
if (logger.isDebugEnabled()) {
logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
}
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
}
}
}
}
MapperFactoryBean
主要功能:mapper代理類生產工廠
FactoryBean 官方文檔
/** 該類實現了 FactoryBean 這個接口乾嘛的呢
* 請看 [官方文檔]
*/
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
/** 最重要的是這個方法 生成接口的代理類
* {@inheritDoc}
*/
@Override
public T getObject() throws Exception {
/** getSqlSession獲取的是父類生成的SqlSessionTemplate
*/
return getSqlSession().getMapper(this.mapperInterface);
}
}
SqlSessionDaoSupport
public abstract class SqlSessionDaoSupport extends DaoSupport {
private SqlSession sqlSession;
private boolean externalSqlSession;
/** sqlSession 的實例對象實際上是使用了SqlSessionTemplate */
public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
if (!this.externalSqlSession) {
this.sqlSession = new SqlSessionTemplate(sqlSessionFactory);
}
}
public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
this.sqlSession = sqlSessionTemplate;
this.externalSqlSession = true;
}
}
最後一直往下跟 就會發現MapperProxy 這個類(沒錯 這就是我們最終的代理類了 spring 注入的就是這個玩意)
/** 實現了 InvocationHandler 的invoke方法*/
public class MapperProxy<T> implements InvocationHandler, Serializable {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (Object.class.equals(method.getDeclaringClass())) {
try {
return method.invoke(this, args);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
final MapperMethod mapperMethod = cachedMapperMethod(method);
/** 執行的sql的構建 結果的處理 都是在這裏完成的
(有興趣的小夥伴可以繼續往下跟) */
return mapperMethod.execute(sqlSession, args);
}
}