1、spring-boot项目中启动mybatis
mybatis在提供了mybatis-spring-boot-autoconfigure,用于spring-boot项目中自动加载注入mybatis类,这里采用了springboot指定SPI规范,SPI规范可参考
在mybatis-spring-boot-autoconfigure包的META-INF目录的spring-factories文件中,配置了mybatis的自动配置的类,boot项目在启动的时候会扫描所有jar包下的/META-INF/spring-factories文件,所以可以扫描到该文件资源。
key值为org.springframework.boot.autoconfigure.EnableAutoConfiguration,下图是spring-boot jar包下的spring-factories文件,可以看到该文件中也有一个key为org.springframework.boot.autoconfigure.EnableAutoConfiguration的键值对(其中值有很多类名以逗号隔开)。也就是说配置了该key,项目在启动的时候,boot就会进行自动配置
在MybatisAutoConfiguration配置类中有mybatis的核心Bean如SqlSessionFactory等,整个串起来就可以知道,为什么spring-boot项目启动的时候就会注入mybatis各种bean了。
2、mybatis启动过程
以下是MybatisAutoConfiguration配置类中sqlSessionFactory对象的创建过程
@Bean
@ConditionalOnMissingBean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
factory.setDataSource(dataSource);
factory.setVfs(SpringBootVFS.class);
if (StringUtils.hasText(this.properties.getConfigLocation())) {
factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));
}
org.apache.ibatis.session.Configuration configuration = this.properties.getConfiguration();
if (configuration == null && !StringUtils.hasText(this.properties.getConfigLocation())) {
configuration = new org.apache.ibatis.session.Configuration();
}
if (configuration != null && !CollectionUtils.isEmpty(this.configurationCustomizers)) {
Iterator var4 = this.configurationCustomizers.iterator();
while(var4.hasNext()) {
ConfigurationCustomizer customizer = (ConfigurationCustomizer)var4.next();
customizer.customize(configuration);
}
}
factory.setConfiguration(configuration);
if (this.properties.getConfigurationProperties() != null) {
factory.setConfigurationProperties(this.properties.getConfigurationProperties());
}
if (!ObjectUtils.isEmpty(this.interceptors)) {
factory.setPlugins(this.interceptors);
}
if (this.databaseIdProvider != null) {
factory.setDatabaseIdProvider(this.databaseIdProvider);
}
if (StringUtils.hasLength(this.properties.getTypeAliasesPackage())) {
factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage());
}
if (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) {
factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());
}
if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) {
factory.setMapperLocations(this.properties.resolveMapperLocations());
}
return factory.getObject();
}
可以看到,先new出了SqlSessionFactoryBean,该类实现了FactoryBean,继承关系结构图如下:最后是调用了factory.getObject()
public SqlSessionFactory getObject() throws Exception {
if (this.sqlSessionFactory == null) {
this.afterPropertiesSet();
}
return this.sqlSessionFactory;
}
getObject方法中,先判断sqlSessionFactory是否为null,如果为null,会调用afterPropertiesSet创建sqlSessionFactory.
sqlSessionFactory方法总结如下:
- dataSource 注入SqlSessionFactoryBean
- configLocation注入SqlSessionFactoryBean
- 创建configuration, 并注入SqlSessionFactoryBean
- MybatisProperties中的configurationProperties注入SqlSessionFactoryBean
- 拦截器注入SqlSessionFactoryBean
- 解析出properties的mapperLocations注入SqlSessionFactoryBean
- buildSqlSessionFactory
- xmlConfigBuilder 解析 mybatis-config.xml 配置
- xmlMapperBuilder解析mapper配置
- 前两步解析的数据都放在了configuration对象中
每个mapper接口会构建成一个个BeanDefinition对象,以备后续spring创建对象使用,我们知道mapper接口在项目启动后都会有代理对象,而代理对象就是通过BeanDefinition生成的。mapper的BeanDefinition创建过程可参考
3 mapperInterface的代理过程分析
每一个mapper接口对应着一个BeanDefinition,BeanDefinition中的class是MapperFactoryBean
在spring创建对象的时候,遇到BeanDefinition的class是FactoryBean的实现的情况下,就会调用实现类的getObject(),这里针对mapper的话,就是调用MapperFactoryBean的getObject()获取mapper对象。
public T getObject() throws Exception {
return getSqlSession().getMapper(this.mapperInterface);
}
在MapperFactoryBean中有一个属性sqlSession,sqlSession对象实际是SqlSessionTemplate
public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
if (!this.externalSqlSession) {
this.sqlSession = new SqlSessionTemplate(sqlSessionFactory);
}
}
而在SqlSessionTemplate类中有一个属性sqlSessionProxy
public class SqlSessionTemplate implements SqlSession, DisposableBean {
private final SqlSessionFactory sqlSessionFactory;
private final ExecutorType executorType;
private final SqlSession sqlSessionProxy;
private final PersistenceExceptionTranslator exceptionTranslator;
在SqlSessionTemplate的构造函数中,对sqlSessionProxy进行了初始化,而sqlSessionProxy是通过动态代理实现的初始化,对应的InvocationHandler实现类是SqlSessionInterceptor
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;
this.sqlSessionProxy = (SqlSession) newProxyInstance(
SqlSessionFactory.class.getClassLoader(),
new Class[] { SqlSession.class },
new SqlSessionInterceptor());
}
而SqlSessionTemplate的getMapper方法实现了mapperInterface的代理对象的实现过程
public <T> T getMapper(Class<T> type) {
return getConfiguration().getMapper(type, this);
}
这里我们可以看到,调用了configure的getMapper,并将SqlSessionTemplate自己的引用传了进去
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
} else {
try {
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception var5) {
throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);
}
}
}
再进去之后,是MapperRegistry的getMapper接口,这里获得了该mapperInterface对应的mapperProxyFactory,并调用了mapperProxyFactory的newInstance方法
public T newInstance(SqlSession sqlSession) {
MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
return this.newInstance(mapperProxy);
}
protected T newInstance(MapperProxy<T> mapperProxy) {
return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
}
在mapperProxyFactory的newInstance方法中,创建了mapperProxy对象,并通过动态代理创建了该mapperInterface的代理对象。
这里不难猜到,MapperProxy是InvocationHandler的实现类,而在调用mapperInterface方法时,都会调用MapperProxy的invoke方法(动态代理的规范),那么我们来看MapperProxy的invoke方法的代码逻辑
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
//若是Object的方法,那么直接调用该方法执行即可
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
}
if (this.isDefaultMethod(method)) {
return this.invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable var5) {
throw ExceptionUtil.unwrapThrowable(var5);
}
//这里说明了,mapper的每一个自定义函数都对应一个MapperMethod,调用该方法时,就是调用该方法对应的MapperMethod的execute方法
MapperMethod mapperMethod = this.cachedMapperMethod(method);
return mapperMethod.execute(this.sqlSession, args);
}
调用mapperInterface的方法时,会调用mapperMethod.execute(this.sqlSession, args); 这里的sqlSession就是我们上面分析的SqlSessionTemplate,我们看execute内部流程
public Object execute(SqlSession sqlSession, Object[] args) {
Object param;
Object result;
switch(this.command.getType()) {
case INSERT:
param = this.method.convertArgsToSqlCommandParam(args);
result = this.rowCountResult(sqlSession.insert(this.command.getName(), param));
break;
case UPDATE:
param = this.method.convertArgsToSqlCommandParam(args);
result = this.rowCountResult(sqlSession.update(this.command.getName(), param));
break;
case DELETE:
param = this.method.convertArgsToSqlCommandParam(args);
result = this.rowCountResult(sqlSession.delete(this.command.getName(), param));
break;
case SELECT:
if (this.method.returnsVoid() && this.method.hasResultHandler()) {
this.executeWithResultHandler(sqlSession, args);
result = null;
} else if (this.method.returnsMany()) {
result = this.executeForMany(sqlSession, args);
} else if (this.method.returnsMap()) {
result = this.executeForMap(sqlSession, args);
} else if (this.method.returnsCursor()) {
result = this.executeForCursor(sqlSession, args);
} else {
param = this.method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(this.command.getName(), param);
}
break;
case FLUSH:
result = sqlSession.flushStatements();
break;
default:
throw new BindingException("Unknown execution method for: " + this.command.getName());
}
if (result == null && this.method.getReturnType().isPrimitive() && !this.method.returnsVoid()) {
throw new BindingException("Mapper method '" + this.command.getName() + " attempted to return null from a method with a primitive return type (" + this.method.getReturnType() + ").");
} else {
return result;
}
}
在该方法中,我们看到了分INSERT、UPDATE、DELETE、SELECT等不清的情况进行不同的调用,这里我们以INSERT为例分析一下,在INSERT分支中,其实是通过sqlSession的insert实现的,我们再看sqlSession的insert实现(这里其实就是SqlSessionTemplate类的insert方法)
public int insert(String statement, Object parameter) {
return this.sqlSessionProxy.insert(statement, parameter);
}
这里,sqlSession的insert其实是委托给了sqlSessionProxy对象的insert方法,前面我们分析过,sqlSessionProxy对象的创建其实是通过动态代理生成的,其InvocationHandler实现类是SqlSessionInterceptor,所以在调用sqlSessionProxy都会走到SqlSessionInterceptor的invoke方法中。
private class SqlSessionInterceptor implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
SqlSession sqlSession = getSqlSession(
SqlSessionTemplate.this.sqlSessionFactory,
SqlSessionTemplate.this.executorType,
SqlSessionTemplate.this.exceptionTranslator);
try {
Object result = method.invoke(sqlSession, args);
if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
// force commit even on non-dirty sessions because some databases require
// a commit/rollback before calling close()
sqlSession.commit(true);
}
return result;
} catch (Throwable t) {
Throwable unwrapped = unwrapThrowable(t);
if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
// release the connection to avoid a deadlock if the translator is no loaded. See issue #22
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
sqlSession = null;
Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException) unwrapped);
if (translated != null) {
unwrapped = translated;
}
}
throw unwrapped;
} finally {
if (sqlSession != null) {
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
}
}
}
}
在SqlSessionInterceptor的invoke方法中,又重新生成了一个sqlSession,通过这个新生成的sqlSession进行了数据库的操作。
从上面的分析,我们可以总结出,
各对象的引用关系如下(实线箭头代表引用的关系,虚线箭头代表的代理的关系)
service中调用某个mapper接口中的方法(如insert),其内部走的大概流程如下: