我们以MyBatis-Spring-Boot-Starter:2.1.2为例,首先打开mybatis-spring-boot-autoconfigure:2.1.2,找到spring.factories:
# Auto Configureorg.springframework.boot.autoconfigure.EnableAutoConfiguration=\org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration,\org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
这里定义了mybatis的配置类,我们主要看下MybatisAutoConfiguration。
首先看下构造函数:
public MybatisAutoConfiguration(MybatisProperties properties, ObjectProvider<Interceptor[]> interceptorsProvider,ObjectProvider<TypeHandler[]> typeHandlersProvider, ObjectProvider<LanguageDriver[]> languageDriversProvider,ResourceLoader resourceLoader, ObjectProvider<DatabaseIdProvider> databaseIdProvider,ObjectProvider<List<ConfigurationCustomizer>> configurationCustomizersProvider) {this.properties = properties;this.interceptors = interceptorsProvider.getIfAvailable();this.typeHandlers = typeHandlersProvider.getIfAvailable();this.languageDrivers = languageDriversProvider.getIfAvailable();this.resourceLoader = resourceLoader;this.databaseIdProvider = databaseIdProvider.getIfAvailable();this.configurationCustomizers = configurationCustomizersProvider.getIfAvailable();}
构造函数注入了四个组件:Interceptor、TypeHandler、LanguageDriver、DatabaseIdProvider,同时注入了ConfigurationCustomizer用来自定义配置,还注入了MybatisProperties。可以结合前一篇文章一块来看。
继续往下看,里面定义了两个Bean:
@Bean@ConditionalOnMissingBeanpublic SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {SqlSessionFactoryBean factory = new SqlSessionFactoryBean();//中间就是向factory中填充各种属性...//最终生成出SqlSessionFactoryreturn factory.getObject();}
这是定义SqlSessionFactory。
@Bean@ConditionalOnMissingBeanpublic SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {//利用SqlSessionFactory构建出来SqlSessionTemplate}
这是定义SqlSessionTemplate,Mybatis-Spring的核心就是SqlSessionTemplate
继续往下面看,下面还定义了一个内嵌的配置类:
@org.springframework.context.annotation.Configuration@Import(AutoConfiguredMapperScannerRegistrar.class)@ConditionalOnMissingBean({ MapperFactoryBean.class, MapperScannerConfigurer.class })public static class MapperScannerRegistrarNotFoundConfiguration implements InitializingBean{}
这个配置类主要用来做Mapper的扫描的,但是要注意@ConditionalOnMissingBean({ MapperFactoryBean.class, MapperScannerConfigurer.class }),只有在没有定义MapperFactoryBean和MapperScannerConfigurer的情况下,才会启用默认的Mapper扫描。
我们来看下它是如何Mapper扫描的:
public static class AutoConfiguredMapperScannerRegistrar implements BeanFactoryAware, ImportBeanDefinitionRegistrar {@Overridepublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {。。。BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);builder.addPropertyValue("processPropertyPlaceHolders", true);builder.addPropertyValue("annotationClass", Mapper.class);builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(packages));BeanWrapper beanWrapper = new BeanWrapperImpl(MapperScannerConfigurer.class);Stream.of(beanWrapper.getPropertyDescriptors())// Need to mybatis-spring 2.0.2+.filter(x -> x.getName().equals("lazyInitialization")).findAny().ifPresent(x -> builder.addPropertyValue("lazyInitialization", "${mybatis.lazy-initialization:false}"));registry.registerBeanDefinition(MapperScannerConfigurer.class.getName(), builder.getBeanDefinition());}}
这里是生成了扫描注解的类,这个类的名字是MapperScannerConfigurer,类型是MapperScannerConfigurer,给它设置了几个属性:processPropertyPlaceHolders、annotationClass和basePackage,annotationClass就是要扫描的Mapper的默认的注解类,basePackage就是要扫描的包的跟路径,默认就是SpringBoot的启动类的包路径。
继续往下看MapperScannerConfigurer:
public class MapperScannerConfigurerimplements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {}
它实现了BeanDefinitionRegistryPostProcessor,因此Spring容器会回调postProcessBeanDefinitionRegistry方法:
@Overridepublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {if (this.processPropertyPlaceHolders) {processPropertyPlaceHolders();}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.setMapperFactoryBeanClass(this.mapperFactoryBeanClass);if (StringUtils.hasText(lazyInitialization)) {scanner.setLazyInitialization(Boolean.valueOf(lazyInitialization));}scanner.registerFilters();scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));}
它里面会创建一个ClassPathMapperScanner的类去做Mapper的扫描,看下具体的doScan()方法:
@Overridepublic Set<BeanDefinitionHolder> doScan(String... basePackages) {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 {processBeanDefinitions(beanDefinitions);}return beanDefinitions;}
继续看下processBeanDefinitions:
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {。。。for (BeanDefinitionHolder holder : beanDefinitions) {definition = (GenericBeanDefinition) holder.getBeanDefinition();// 这里就设置了生成的Mappe的类是mapperFactoryBeanClass = MapperFactoryBean.class;definition.setBeanClass(this.mapperFactoryBeanClass);//给Bean设置了sqlSessionFactorydefinition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);// 给Bean设置了sqlSessionFactorydefinition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);}}
继续看下MapperFactoryBean:
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {}public abstract class SqlSessionDaoSupport extends DaoSupport {}public abstract class DaoSupport implements InitializingBean{}
MapperFactoryBean是一个FactoryBean,所以容器会调用getObject()来生成Mapper的具体的类:
@Overridepublic T getObject() throws Exception {return getSqlSession().getMapper(this.mapperInterface);}
同时它也实现了InitializingBean,因此容器会回调afterPropertiesSet(),最终会回调到checkDaoConfig():
@Overrideprotected void checkDaoConfig() {super.checkDaoConfig();Configuration configuration = getSqlSession().getConfiguration();if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {try {//这里就把Mapper接口添加到了configuratiuon中configuration.addMapper(this.mapperInterface);} catch (Exception e) {logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", e);throw new IllegalArgumentException(e);} finally {ErrorContext.instance().reset();}}}
继续跟一下configuration.addMapper,一直到MapperRegistry.addMapper():
public <T> void addMapper(Class<T> type) {if (type.isInterface()) {if (hasMapper(type)) {throw new BindingException("Type " + type + " is already known to the MapperRegistry.");}boolean loadCompleted = false;try {knownMappers.put(type, new MapperProxyFactory<>(type));// It's important that the type is added before the parser is run// otherwise the binding may automatically be attempted by the// mapper parser. If the type is already known, it won't try.MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);parser.parse();loadCompleted = true;} finally {if (!loadCompleted) {knownMappers.remove(type);}}}}
看一下parser.parse():
public void parse() {String resource = type.toString();if (!configuration.isResourceLoaded(resource)) {//这里面就会去解析与Mapper接口相同包名下面的xml文件loadXmlResource();configuration.addLoadedResource(resource);assistant.setCurrentNamespace(type.getName());parseCache();parseCacheRef();Method[] methods = type.getMethods();for (Method method : methods) {try {// issue #237if (!method.isBridge()) {//这里回去解析接口方法上的SQL注解parseStatement(method);}} catch (IncompleteElementException e) {configuration.addIncompleteMethod(new MethodResolver(this, method));}}}parsePendingMethods();}
至此,Mapper解析加载完成。
此外,如果不使用默认的Mapper扫描机制,也可以使用@MapperScan注解,看下它的源码:
@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)@Documented@Import(MapperScannerRegistrar.class)@Repeatable(MapperScans.class)public @interface MapperScan {}
看下MapperScannerRegistrar:
public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {}
容器会回调registerBeanDefinitions():
void registerBeanDefinitions(AnnotationMetadata annoMeta, AnnotationAttributes annoAttrs,BeanDefinitionRegistry registry, String beanName) {//这里其实也是注册了一个MapperScannerConfigurer的Bean,后面的处理则与默认相同BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);builder.addPropertyValue("processPropertyPlaceHolders", true);Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass");if (!Annotation.class.equals(annotationClass)) {builder.addPropertyValue("annotationClass", annotationClass);}Class<?> markerInterface = annoAttrs.getClass("markerInterface");if (!Class.class.equals(markerInterface)) {builder.addPropertyValue("markerInterface", markerInterface);}Class<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator");if (!BeanNameGenerator.class.equals(generatorClass)) {builder.addPropertyValue("nameGenerator", BeanUtils.instantiateClass(generatorClass));}Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {builder.addPropertyValue("mapperFactoryBeanClass", mapperFactoryBeanClass);}String sqlSessionTemplateRef = annoAttrs.getString("sqlSessionTemplateRef");if (StringUtils.hasText(sqlSessionTemplateRef)) {builder.addPropertyValue("sqlSessionTemplateBeanName", annoAttrs.getString("sqlSessionTemplateRef"));}String sqlSessionFactoryRef = annoAttrs.getString("sqlSessionFactoryRef");if (StringUtils.hasText(sqlSessionFactoryRef)) {builder.addPropertyValue("sqlSessionFactoryBeanName", annoAttrs.getString("sqlSessionFactoryRef"));}List<String> basePackages = new ArrayList<>();basePackages.addAll(Arrays.stream(annoAttrs.getStringArray("value")).filter(StringUtils::hasText).collect(Collectors.toList()));basePackages.addAll(Arrays.stream(annoAttrs.getStringArray("basePackages")).filter(StringUtils::hasText).collect(Collectors.toList()));basePackages.addAll(Arrays.stream(annoAttrs.getClassArray("basePackageClasses")).map(ClassUtils::getPackageName).collect(Collectors.toList()));if (basePackages.isEmpty()) {basePackages.add(getDefaultBasePackage(annoMeta));}String lazyInitialization = annoAttrs.getString("lazyInitialization");if (StringUtils.hasText(lazyInitialization)) {builder.addPropertyValue("lazyInitialization", lazyInitialization);}builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(basePackages));registry.registerBeanDefinition(beanName, builder.getBeanDefinition());}
跟上面就一样了。
整个流程还是很清晰的,需要注意下starter首先是注册了BeanDefinitionRegistryPostProcessor,然后在它里面又去做的Mapper的扫描,下一篇我们将讲解下如何来自定义组件扫描,欢迎关注。




