pom引入依賴
<!--mybatis與spring整合-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.1</version>
</dependency>
<!--mybatis與spring-boot整合-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
springboot啓動配置bean
@Configuration
@ImportResource(locations = { "classpath:spring/*" })
@PropertySource("classpath:config/datasource.properties")
@ComponentScan(basePackages = "org.gallant.spring")
@MapperScan("org.gallant.spring.transaction.mapper")
@EnableTransactionManagement
public class AppContext {
}
mybatis爲了整合springboot開發了starter橋接包與實現包。橋接包中指定自動配置的實現提供者:provides: mybatis-spring-boot-autoconfigure,mybatis,mybatis-spring。查看mybatis自動配置類實現源碼如下
@org.springframework.context.annotation.Configuration
@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })
@ConditionalOnBean(DataSource.class)
@EnableConfigurationProperties(MybatisProperties.class)
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
public class MybatisAutoConfiguration {
...//暫時僅關注註解部分
}
註冊properties配置bean
EnableConfigurationProperties註解指定EnableConfigurationPropertiesImportSelector導入選擇器導入ConfigurationPropertiesBeanRegistrar配置bean定義並註冊至上下文、ConfigurationPropertiesBindingPostProcessorRegistrar將ConfigurationPropertiesBindingPostProcessor註冊至上下文,ConfigurationPropertiesBindingPostProcessor將在配置bean初始化前綁定配置屬性postProcessBeforeInitialization。配置bean類型爲MybatisProperties,名稱爲mybatis-org.mybatis.spring.boot.autoconfigure.MybatisProperties(如果有前綴),配置MybatisProperties類的ConfigurationProperties註解指定配置的前綴爲mybatis。
註冊sqlSessionFactory bean
創建SqlSessionFactoryBean,根據properties配置factory,獲取SqlSessionFactory bean
@Override
public SqlSessionFactory getObject() throws Exception {
if (this.sqlSessionFactory == null) {
afterPropertiesSet();
}
return this.sqlSessionFactory;
}
@Override
public void afterPropertiesSet() throws Exception {
...
// 1.根據配置初始化Configuration
// 1.x 根據mapperLocations解析mapper xml文件至configuration.getSqlFragments
// 2. 根據Configuration配置構建SqlSessionFactory實例
// return this.sqlSessionFactoryBuilder.build(configuration);
this.sqlSessionFactory = buildSqlSessionFactory();
}
註冊sqlSessionTemplate bean
註冊Mapper bean
Mapper掃描
定義Mapper掃描註冊類實現
// class : MybatisAutoConfiguration 的內部類
public static class AutoConfiguredMapperScannerRegistrar
implements BeanFactoryAware, ImportBeanDefinitionRegistrar, ResourceLoaderAware {
private BeanFactory beanFactory;
private ResourceLoader resourceLoader;
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
logger.debug("Searching for mappers annotated with @Mapper");
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
try {
if (this.resourceLoader != null) {
scanner.setResourceLoader(this.resourceLoader);
}
List<String> packages = AutoConfigurationPackages.get(this.beanFactory);
if (logger.isDebugEnabled()) {
for (String pkg : packages) {
logger.debug("Using auto-configuration base package '{}'", pkg);
}
}
scanner.setAnnotationClass(Mapper.class);
scanner.registerFilters();
scanner.doScan(StringUtils.toStringArray(packages));
} catch (IllegalStateException ex) {
logger.debug("Could not determine auto-configuration package, automatic mapper scanning disabled.", ex);
}
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
}
導入Mapper掃描註冊bean
MapperScan註解指定導入MapperScannerRegistrar。import註解在spring上下文刷新時導入指定的bean。具體邏輯可以查看ConfigurationClassParser.processImports方法。
MapperScannerRegistrar源碼分析
- 獲取MapperScan註解元數據信息
- 創建ClassPathMapperScanner實例
- 根據註解註冊過濾器scanner.registerFilters(),annotationClass、markerInterface可以指定需要掃描的類型,如果未指定默認接收所有接口acceptAllInterfaces;默認會增加排除exclude package-info.java
- 根據basePackage掃描doScan
執行掃描
- 調用父類掃描ClassPathBeanDefinitionScanner.doScan。
- 遍歷basePackages目錄,將匹配的源文件封裝爲ScannedGenericBeanDefinition類型,返回所有匹配的bean定義。spring掃描bean的目錄順序:Application所在目錄、@ComponentScan註解配置目錄、@MapperScan註解配置目錄
- 將掃描結果註冊至上下文等待初始化
初始化Mapper bean
AbstractApplicationContext.finishBeanFactoryInitialization執行上下文中非懶惰加載bean的初始化
ConstructorResolver.autowireConstructor構造Mapper實例,Mapper bean定義類型爲org.mybatis.spring.mapper.MapperFactoryBean,候選的構造器列表如下
按照選中的構造器(有class爲入參的構造器),使用CglibSubclassingInstantiationStrategy父類SimpleInstantiationStrategy.instantiate創建動態代理對象,如果getMethodOverrides爲空則調用BeanUtils._instantiateClass,否則調用子類重寫的_instantiateWithMethodInjection方法
constructorToUse
argsToUse
構建後的beanWrapper
注入屬性populateBean
- 各種InstantiationAwareBeanPostProcessor後置處理動作
- autowire注入模式默認爲類型getResolvedAutowireMode=AUTOWIRE_BY_TYPE(2)
- 待注入的屬性列表addToConfig、sqlSessionFactory、sqlSessionTemplate,從工廠中解析依賴resolveDependency,獲取依賴bean緩存至MutablePropertyValues待注入
- 解析sqlSessionFactory,從工廠獲取bean
- 執行依賴注入,即@Autowired或者@Resource MyMapper mapper
- 從工廠中獲取依賴類型的bean
- mapper依賴描述
- 待注入的屬性列表
- 注入applyPropertyValues
初始化對象initializeBean
返回bean對象
MapperFactoryBean獲取Mapper
MapperFactoryBean實現了spring的FactoryBean接口,通過spring容器回調(AbstractBeanFactory.getObjectForBeanInstance)getObject構建Mapper返回
@Override
public T getObject() throws Exception {
return getSqlSession().getMapper(this.mapperInterface);
}
SqlSessionTemplate會話模板獲取Mapper
@Override
public <T> T getMapper(Class<T> type) {
return getConfiguration().getMapper(type, this);
}
Configuration配置獲取Mapper
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
MapperRegistry註冊中心獲取Mapper
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
// 從已知Mapper類型緩存中獲取代理工廠,此處是一個map,key爲mapper類型,value爲代理工廠
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
// mapper代理工廠創建mapper實例
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
Mapper代理工廠創建Mapper實例,可以看到使用的是jdk自帶的動態代理
// 類:MapperProxyFactory
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
Mapper動態代理實現
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
...
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}
private MapperMethod cachedMapperMethod(Method method) {
MapperMethod mapperMethod = methodCache.get(method);
if (mapperMethod == null) {
// 根據配置文件、接口、方法創建Mapper方法命令
mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
methodCache.put(method, mapperMethod);
}
return mapperMethod;
}
MapperMethod實例
// 類:MapperMethod
public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
this.command = new SqlCommand(config, mapperInterface, method);
this.method = new MethodSignature(config, mapperInterface, method);
}
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
switch (command.getType()) {
case INSERT: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
break;
}
...
default:
throw new BindingException("Unknown execution method for: " + command.getName());
}
if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
throw new BindingException("Mapper method '" + command.getName()
+ " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
}
return result;
}
// MapperMethod的內部類SqlCommand
public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
final String methodName = method.getName();
final Class<?> declaringClass = method.getDeclaringClass();
// 根據配置文件解析Mapped語句
MappedStatement ms = resolveMappedStatement(mapperInterface, methodName, declaringClass,
configuration);
...
}
// MapperMethod的內部類SqlCommand
private MappedStatement resolveMappedStatement(Class<?> mapperInterface, String methodName,
Class<?> declaringClass, Configuration configuration) {
String statementId = mapperInterface.getName() + "." + methodName;
if (configuration.hasStatement(statementId)) {
return configuration.getMappedStatement(statementId);
} else if (mapperInterface.equals(declaringClass)) {
return null;
}
// 遞歸調用實現的父接口
for (Class<?> superInterface : mapperInterface.getInterfaces()) {
if (declaringClass.isAssignableFrom(superInterface)) {
MappedStatement ms = resolveMappedStatement(superInterface, methodName,
declaringClass, configuration);
...
}
}
return null;
}
配置文件緩存的語句MappedStatement