SpringBoot整合MyBatis源码

SpringBoot如何整合MyBatis

导包

代码
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter</artifactId>
</dependency>

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
	<groupId>mysql</groupId>
	<artifactId>mysql-connector-java</artifactId>
</dependency>

<dependency>
	<groupId>org.mybatis.spring.boot</groupId>
	<artifactId>mybatis-spring-boot-starter</artifactId>
	<version>2.1.3</version>
</dependency>

<dependency>
	<groupId>org.projectlombok</groupId>
	<artifactId>lombok</artifactId>
	<version>1.18.24</version>
</dependency>

<dependency>
	<groupId>cn.hutool</groupId>
	<artifactId>hutool-all</artifactId>
	<version>5.8.3</version>
</dependency>

配置application.yml

代码
server:
  port: 9090
  servlet:
    encoding:
      charset: UTF-8
    context-path: /visual-web
spring:
  application:
    name: visual-web
  datasource:
    type: com.zaxxer.hikari.HikariDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://IP1:3360/db?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT&allowPublicKeyRetrieval=true
    username: root
    password: pwd
    hikari:
      connection-timeout: 10000
      validation-timeout: 3000
      idle-timeout: 60000
      login-timeout: 5
      max-lifetime: 60000
      maximum-pool-size: 10
      minimum-idle: 5
      read-only: false

mybatis:
  type-aliases-package: com.sunpeiyu.visualweb.entity
  mapper-locations: classpath:com/sunpeiyu/visualweb/mapper/*.xml

编写Mapper和XML

代码
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.sunpeiyu.visualweb.dao.MenuMapper">
    <select id="selectMenuList" resultType="com.sunpeiyu.visualweb.entity.MenuEntity">
        select menu_id as menuId, menu_name as menuName,
               menu_path as menuPath, menu_label as menuLabel,
               menu_url as menuUrl, menu_index as menuIndex, menu_icon as menuIcon
        from menu
    </select>
</mapper>

@Repository
@Mapper
public interface MenuMapper {
    /**
     * 查询所有的菜单
     * @return
     */
    List<MenuEntity> selectMenuList();
}

service层

代码
@Service
public class MenuServiceImpl {

    @Autowired
    private MenuMapper menuMapper;

    public List<MenuDTO> listMenu() {
        List<MenuEntity> menuEntityList = menuMapper.selectMenuList();
        return CopyUtils.copyListByShallow(menuEntityList, MenuDTO.class);
    }
}

controller层

代码
@RequestMapping("/menu")
@RestController
public class MenuController {

    @Autowired
    private MenuServiceImpl menuService;

    @GetMapping("/list")
    public List<MenuDTO> list() {
        return menuService.listMenu();
    }
}

流程图

image

mybatis源码阅读思考

  1. mybatis如何加载mapper中java接口和xml文件的。
  2. mybatis如何解析xml文件中的sql语句。
  3. mybatis的mapper中只有java接口,没有实现类,如何进行实现类调用。
  4. mybatis中的拦截器的源码如何实现的。
  5. mybatis中的xml文件和java接口如何做到一一对应的。

自动装配Spring.factories文件中EnableAutoConfiguration对应的值MybatisAutoConfiguration

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration,\
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
代码
@Configuration
@ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class})
@ConditionalOnSingleCandidate(DataSource.class)
@EnableConfigurationProperties({MybatisProperties.class})
@AutoConfigureAfter({DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class})
public class MybatisAutoConfiguration implements InitializingBean {
    private static final Logger logger = LoggerFactory.getLogger(MybatisAutoConfiguration.class);
    private final MybatisProperties properties;
    private final Interceptor[] interceptors;
    private final TypeHandler[] typeHandlers;
    private final LanguageDriver[] languageDrivers;
    private final ResourceLoader resourceLoader;
    private final DatabaseIdProvider databaseIdProvider;
    private final List<ConfigurationCustomizer> configurationCustomizers;

    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 = (Interceptor[])interceptorsProvider.getIfAvailable();
        this.typeHandlers = (TypeHandler[])typeHandlersProvider.getIfAvailable();
        this.languageDrivers = (LanguageDriver[])languageDriversProvider.getIfAvailable();
        this.resourceLoader = resourceLoader;
        this.databaseIdProvider = (DatabaseIdProvider)databaseIdProvider.getIfAvailable();
        this.configurationCustomizers = (List)configurationCustomizersProvider.getIfAvailable();
    }

    public void afterPropertiesSet() {
        this.checkConfigFileExists();
    }

    private void checkConfigFileExists() {
        if (this.properties.isCheckConfigLocation() && StringUtils.hasText(this.properties.getConfigLocation())) {
            Resource resource = this.resourceLoader.getResource(this.properties.getConfigLocation());
            Assert.state(resource.exists(), "Cannot find config location: " + resource + " (please add config file or check your Mybatis configuration)");
        }

    }

    @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()));
        }

        this.applyConfiguration(factory);
        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 (this.properties.getTypeAliasesSuperType() != null) {
            factory.setTypeAliasesSuperType(this.properties.getTypeAliasesSuperType());
        }

        if (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) {
            factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());
        }

        if (!ObjectUtils.isEmpty(this.typeHandlers)) {
            factory.setTypeHandlers(this.typeHandlers);
        }

        if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) {
            factory.setMapperLocations(this.properties.resolveMapperLocations());
        }

        Set<String> factoryPropertyNames = (Set)Stream.of((new BeanWrapperImpl(SqlSessionFactoryBean.class)).getPropertyDescriptors()).map(FeatureDescriptor::getName).collect(Collectors.toSet());
        Class<? extends LanguageDriver> defaultLanguageDriver = this.properties.getDefaultScriptingLanguageDriver();
        if (factoryPropertyNames.contains("scriptingLanguageDrivers") && !ObjectUtils.isEmpty(this.languageDrivers)) {
            factory.setScriptingLanguageDrivers(this.languageDrivers);
            if (defaultLanguageDriver == null && this.languageDrivers.length == 1) {
                defaultLanguageDriver = this.languageDrivers[0].getClass();
            }
        }

        if (factoryPropertyNames.contains("defaultScriptingLanguageDriver")) {
            factory.setDefaultScriptingLanguageDriver(defaultLanguageDriver);
        }

        return factory.getObject();
    }

    private void applyConfiguration(SqlSessionFactoryBean factory) {
        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 var3 = this.configurationCustomizers.iterator();

            while(var3.hasNext()) {
                ConfigurationCustomizer customizer = (ConfigurationCustomizer)var3.next();
                customizer.customize(configuration);
            }
        }

        factory.setConfiguration(configuration);
    }

    @Bean
    @ConditionalOnMissingBean
    public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
        ExecutorType executorType = this.properties.getExecutorType();
        return executorType != null ? new SqlSessionTemplate(sqlSessionFactory, executorType) : new SqlSessionTemplate(sqlSessionFactory);
    }

    @Configuration
    @Import({MybatisAutoConfiguration.AutoConfiguredMapperScannerRegistrar.class})
    @ConditionalOnMissingBean({MapperFactoryBean.class, MapperScannerConfigurer.class})
    public static class MapperScannerRegistrarNotFoundConfiguration implements InitializingBean {
        public MapperScannerRegistrarNotFoundConfiguration() {
        }

        public void afterPropertiesSet() {
            MybatisAutoConfiguration.logger.debug("Not found configuration for registering mapper bean using @MapperScan, MapperFactoryBean and MapperScannerConfigurer.");
        }
    }

    public static class AutoConfiguredMapperScannerRegistrar implements BeanFactoryAware, ImportBeanDefinitionRegistrar {
        private BeanFactory beanFactory;

        public AutoConfiguredMapperScannerRegistrar() {
        }

        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
            if (!AutoConfigurationPackages.has(this.beanFactory)) {
                MybatisAutoConfiguration.logger.debug("Could not determine auto-configuration package, automatic mapper scanning disabled.");
            } else {
                MybatisAutoConfiguration.logger.debug("Searching for mappers annotated with @Mapper");
                List<String> packages = AutoConfigurationPackages.get(this.beanFactory);
                if (MybatisAutoConfiguration.logger.isDebugEnabled()) {
                    packages.forEach((pkg) -> {
                        MybatisAutoConfiguration.logger.debug("Using auto-configuration base package '{}'", pkg);
                    });
                }

                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()).filter((x) -> {
                    return x.getName().equals("lazyInitialization");
                }).findAny().ifPresent((x) -> {
                    builder.addPropertyValue("lazyInitialization", "${mybatis.lazy-initialization:false}");
                });
                registry.registerBeanDefinition(MapperScannerConfigurer.class.getName(), builder.getBeanDefinition());
            }
        }

        public void setBeanFactory(BeanFactory beanFactory) {
            this.beanFactory = beanFactory;
        }
    }
}

Mybatis中的关键bean

关键1,SqlSessionFactory类,根据mapper配置文件解析出dao与具体jdbc操作、resultMap与实体类等的映射关系

image

关键2,SqlSessionTemplate类,实现了CRUD操作

image

关键3,AutoConfiguredMapperScannerRegistrar类,扫描@Mapper注解的类

image

注册SqlSessionFactory类,根据mapper配置文件解析出dao与具体jdbc操作、resultMap与实体类等的映射关系

@Bean
@ConditionalOnMissingBean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
	// 设置数据源DataSource
	SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
	factory.setDataSource(dataSource);
	factory.setVfs(SpringBootVFS.class);
	// 设置总配置文件,mybatis-config.xml
	if (StringUtils.hasText(this.properties.getConfigLocation())) {
	  factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));
	}
	applyConfiguration(factory);
	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);
	}
	// 设置mybatis自动扫描到自定义的POJO
	if (StringUtils.hasLength(this.properties.getTypeAliasesPackage())) {
	  factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage());
	}
	if (this.properties.getTypeAliasesSuperType() != null) {
	  factory.setTypeAliasesSuperType(this.properties.getTypeAliasesSuperType());
	}
	if (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) {
	  factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());
	}
	if (!ObjectUtils.isEmpty(this.typeHandlers)) {
	  factory.setTypeHandlers(this.typeHandlers);
	}
	// 设置mapper-locations,扫描mapper的xml文件
	if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) {
	  factory.setMapperLocations(this.properties.resolveMapperLocations());
	}
	Set<String> factoryPropertyNames = Stream
		.of(new BeanWrapperImpl(SqlSessionFactoryBean.class).getPropertyDescriptors()).map(PropertyDescriptor::getName)
		.collect(Collectors.toSet());
	Class<? extends LanguageDriver> defaultLanguageDriver = this.properties.getDefaultScriptingLanguageDriver();
	if (factoryPropertyNames.contains("scriptingLanguageDrivers") && !ObjectUtils.isEmpty(this.languageDrivers)) {
	  // Need to mybatis-spring 2.0.2+
	  factory.setScriptingLanguageDrivers(this.languageDrivers);
	  if (defaultLanguageDriver == null && this.languageDrivers.length == 1) {
		defaultLanguageDriver = this.languageDrivers[0].getClass();
	  }
	}
	if (factoryPropertyNames.contains("defaultScriptingLanguageDriver")) {
	  // Need to mybatis-spring 2.0.2+
	  factory.setDefaultScriptingLanguageDriver(defaultLanguageDriver);
	}
	// 创建SqlSessionFactory对象
	return factory.getObject();
}

factory.getObject();实例化SqlSessionFactory对象

@Override
public SqlSessionFactory getObject() throws Exception {
	if (this.sqlSessionFactory == null) {
	  afterPropertiesSet();
	}

	return this.sqlSessionFactory;
}

@Override
public void afterPropertiesSet() throws Exception {
	notNull(dataSource, "Property 'dataSource' is required");
	notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
	state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null),
		"Property 'configuration' and 'configLocation' can not specified with together");

	this.sqlSessionFactory = buildSqlSessionFactory();
}

委派给buildSqlSessionFactory()方法,构建SqlSessionFactory对象

  • 解析this.typeAliasesPackage,扫描到自定义的POJO,存放到this.typeAliases
  • 遍历mapper-locations,扫描mapper的所有xml文件,解析xml文件
protected SqlSessionFactory buildSqlSessionFactory() throws Exception {

    final Configuration targetConfiguration;

    XMLConfigBuilder xmlConfigBuilder = null;
    if (this.configuration != null) {
      targetConfiguration = this.configuration;
      if (targetConfiguration.getVariables() == null) {
        targetConfiguration.setVariables(this.configurationProperties);
      } else if (this.configurationProperties != null) {
        targetConfiguration.getVariables().putAll(this.configurationProperties);
      }
    } else if (this.configLocation != null) {
      xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
      targetConfiguration = xmlConfigBuilder.getConfiguration();
    } else {
      LOGGER.debug(
          () -> "Property 'configuration' or 'configLocation' not specified, using default MyBatis Configuration");
      targetConfiguration = new Configuration();
      Optional.ofNullable(this.configurationProperties).ifPresent(targetConfiguration::setVariables);
    }

    Optional.ofNullable(this.objectFactory).ifPresent(targetConfiguration::setObjectFactory);
    Optional.ofNullable(this.objectWrapperFactory).ifPresent(targetConfiguration::setObjectWrapperFactory);
    Optional.ofNullable(this.vfs).ifPresent(targetConfiguration::setVfsImpl);
	// 扫描到自定义的POJO,存放到this.typeAliases
    if (hasLength(this.typeAliasesPackage)) {
      scanClasses(this.typeAliasesPackage, this.typeAliasesSuperType).stream()
          .filter(clazz -> !clazz.isAnonymousClass()).filter(clazz -> !clazz.isInterface())
          .filter(clazz -> !clazz.isMemberClass()).forEach(targetConfiguration.getTypeAliasRegistry()::registerAlias);
    }

    if (!isEmpty(this.typeAliases)) {
      Stream.of(this.typeAliases).forEach(typeAlias -> {
        targetConfiguration.getTypeAliasRegistry().registerAlias(typeAlias);
        LOGGER.debug(() -> "Registered type alias: '" + typeAlias + "'");
      });
    }

    if (!isEmpty(this.plugins)) {
      Stream.of(this.plugins).forEach(plugin -> {
        targetConfiguration.addInterceptor(plugin);
        LOGGER.debug(() -> "Registered plugin: '" + plugin + "'");
      });
    }

    if (hasLength(this.typeHandlersPackage)) {
      scanClasses(this.typeHandlersPackage, TypeHandler.class).stream().filter(clazz -> !clazz.isAnonymousClass())
          .filter(clazz -> !clazz.isInterface()).filter(clazz -> !Modifier.isAbstract(clazz.getModifiers()))
          .forEach(targetConfiguration.getTypeHandlerRegistry()::register);
    }

    if (!isEmpty(this.typeHandlers)) {
      Stream.of(this.typeHandlers).forEach(typeHandler -> {
        targetConfiguration.getTypeHandlerRegistry().register(typeHandler);
        LOGGER.debug(() -> "Registered type handler: '" + typeHandler + "'");
      });
    }

    targetConfiguration.setDefaultEnumTypeHandler(defaultEnumTypeHandler);

    if (!isEmpty(this.scriptingLanguageDrivers)) {
      Stream.of(this.scriptingLanguageDrivers).forEach(languageDriver -> {
        targetConfiguration.getLanguageRegistry().register(languageDriver);
        LOGGER.debug(() -> "Registered scripting language driver: '" + languageDriver + "'");
      });
    }
    Optional.ofNullable(this.defaultScriptingLanguageDriver)
        .ifPresent(targetConfiguration::setDefaultScriptingLanguage);

    if (this.databaseIdProvider != null) {// fix #64 set databaseId before parse mapper xmls
      try {
        targetConfiguration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
      } catch (SQLException e) {
        throw new NestedIOException("Failed getting a databaseId", e);
      }
    }

    Optional.ofNullable(this.cache).ifPresent(targetConfiguration::addCache);

    if (xmlConfigBuilder != null) {
      try {
        xmlConfigBuilder.parse();
        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();
      }
    }

    targetConfiguration.setEnvironment(new Environment(this.environment,
        this.transactionFactory == null ? new SpringManagedTransactionFactory() : this.transactionFactory,
        this.dataSource));
	// 遍历mapper-locations,扫描mapper的所有xml文件
    if (this.mapperLocations != null) {
      if (this.mapperLocations.length == 0) {
        LOGGER.warn(() -> "Property 'mapperLocations' was specified but matching resources are not found.");
      } else {
        for (Resource mapperLocation : this.mapperLocations) {
          if (mapperLocation == null) {
            continue;
          }
          try {
            XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
                targetConfiguration, mapperLocation.toString(), targetConfiguration.getSqlFragments());
			// 解析mapper的xml文件
            xmlMapperBuilder.parse();
          } catch (Exception e) {
            throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
          } finally {
            ErrorContext.instance().reset();
          }
          LOGGER.debug(() -> "Parsed mapper file: '" + mapperLocation + "'");
        }
      }
    } else {
      LOGGER.debug(() -> "Property 'mapperLocations' was not specified.");
    }

    return this.sqlSessionFactoryBuilder.build(targetConfiguration);
}

注册SqlSessionFactory类逻辑

自动装配的逻辑中使用ConfigurationClassPostProcessor这个处理@Configuration注解的后置处理器,使用registerBeanDefinition方法,将当前的SqlSessionFactory类放入beanDefinitionMap中。

注册SqlSessionTemplate类,实现了CRUD操作

@Bean
@ConditionalOnMissingBean
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
	ExecutorType executorType = this.properties.getExecutorType();
	return executorType != null ? new SqlSessionTemplate(sqlSessionFactory, executorType) : new SqlSessionTemplate(sqlSessionFactory);
}

注册SqlSessionTemplate类逻辑

自动装配的逻辑中使用ConfigurationClassPostProcessor这个处理@Configuration注解的后置处理器,使用registerBeanDefinition方法,将当前的SqlSessionTemplate类放入beanDefinitionMap中。

注册AutoConfiguredMapperScannerRegistrar类来扫描被@Mapper标注的类

image

注入AutoConfiguredMapperScannerRegistrar类

在自动装配的源码中ConfigurationClassParser.processImports(configClass, sourceClass, getImports(sourceClass), filter, true),执行@Import注解的同时也会批量注册实现了ImportBeanDefinitionRegistrar接口的类

image

default方法,就是实现了ImportBeanDefinitionRegistra接口的类,只要实例化就是会执行registerBeanDefinitions方法

public interface ImportBeanDefinitionRegistrar {

	default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry,
			BeanNameGenerator importBeanNameGenerator) {

		registerBeanDefinitions(importingClassMetadata, registry);
	}

	default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
	}

}

AutoConfiguredMapperScannerRegistrar.registerBeanDefinitions方法
该方法的执行,找到并且解析注解@Mapper注解,扫描包路径basePackage配置下的bean,然后又注册了重要的类MapperScannerConfigurer。

@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
	//这个是判断beanFactory中是否存在AutoConfigurationPackages的bean,这里是存在的
	if (!AutoConfigurationPackages.has(this.beanFactory)) {
	logger.debug("Could not determine auto-configuration package, automatic mapper scanning disabled.");
	return;
	}

	logger.debug("Searching for mappers annotated with @Mapper");
	// 这里获取要扫描的包名,这里会是com.sunpeiyu.visualweb.mapper,其实也就是我们在哪里找mapper
	List<String> packages = AutoConfigurationPackages.get(this.beanFactory);
	if (logger.isDebugEnabled()) {
	packages.forEach(pkg -> logger.debug("Using auto-configuration base package '{}'", pkg));
	}
	//下面这些代码主要就是定义一个bean的定义,添加到BeanFactory中
	BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
	builder.addPropertyValue("processPropertyPlaceHolders", true);
	//这就是要扫描的注解类型,就是@Mapper
	builder.addPropertyValue("annotationClass", Mapper.class);
	//这里是要扫描的包的路径
	builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(packages));
	BeanWrapper beanWrapper = new BeanWrapperImpl(MapperScannerConfigurer.class);
	Set<String> propertyNames = Stream.of(beanWrapper.getPropertyDescriptors()).map(PropertyDescriptor::getName)
	  .collect(Collectors.toSet());
	if (propertyNames.contains("lazyInitialization")) {
	// Need to mybatis-spring 2.0.2+
	builder.addPropertyValue("lazyInitialization", "${mybatis.lazy-initialization:false}");
	}
	if (propertyNames.contains("defaultScope")) {
	// Need to mybatis-spring 2.0.6+
	builder.addPropertyValue("defaultScope", "${mybatis.mapper-default-scope:}");
	}
	// 注册MapperScannerConfigurer到this.beanDefinitionMap
	registry.registerBeanDefinition(MapperScannerConfigurer.class.getName(), builder.getBeanDefinition());
}

MapperScannerConfigurer扫描mapper,将mapper注册到beanDefinitionMap中

MapperScannerConfigurer定义

image

MapperScannerConfigurer执行增加bean的入口方法

image

执行postProcessBeanDefinitionRegistry方法:

public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
    if (this.processPropertyPlaceHolders) {
      //这个是主要设置一些属性,比如上面包名,要扫描的注解类名称等等
      processPropertyPlaceHolders();
    }
	//这个类看名字,大家都知道是干什么的了。主要就是扫描mapper注解的类
    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));
    }
    if (StringUtils.hasText(defaultScope)) {
      scanner.setDefaultScope(defaultScope);
    }
    //这里是设置要扫描的注解类,这里会设置@Mapper
    scanner.registerFilters();
    //这里就是要根据传入的包名去做扫描了,这里的this.basePackage就是上面说的com.example.springbootmybatis
    scanner.scan(
        StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}

包名遍历扫描,将包含的mapper转成BeanDefinition,并且将mapper的bean存放到beanDefinitionMap中

public int scan(String... basePackages) {
	int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
	// 在这里进行mapper的扫描
	doScan(basePackages);

	// Register annotation config processors, if necessary.
	if (this.includeAnnotationConfig) {
		AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
	}

	return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
}


@Override
public 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;
}

protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
	Assert.notEmpty(basePackages, "At least one base package must be specified");
	Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
	//根据传入的包名遍历
	for (String basePackage : basePackages) {
		//这里就是扫描类路径下的mapper注解类了。
		//比如我这里的传入的包名是com.example.springbootmybatis,就会被转换成classpath*:com/example/springbootmybatis/**/*.class这个路径进行解析查找,将找到的类作为BeanDefinition的定义,返回
		Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
		for (BeanDefinition candidate : candidates) {
			ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
			candidate.setScope(scopeMetadata.getScopeName());
			//获取bean的名字
			String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
			//这里candidate的类型是ScannedGenericBeanDefinition,所以会进入这个if分支,这个没啥,就是设置一些bean初始化相关属性,不关注了
			if (candidate instanceof AbstractBeanDefinition) {
				postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
			}
			//也会进入这个if分支,这个也不进去看了
			if (candidate instanceof AnnotatedBeanDefinition) {
				AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
			}
			//这个是判断beanFactory是否包含beanName的bean的定义,不包含就会进入分支,这个分支也没啥特殊的,就是把bean的定义添加到beanFactory中
			if (checkCandidate(beanName, candidate)) {
				BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
				definitionHolder =
						AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
				beanDefinitions.add(definitionHolder);
				registerBeanDefinition(definitionHolder, this.registry);
			}
		}
	}
	return beanDefinitions;
}

设置beanDefinition

填充beanDefinition中,的beanClass为MapperFactoryBean类,并且设置注入方式为通过类型注入。

private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
	......
	//这里是给bean定义的添加一个构造方法参数,就是我们扫描出来mapper注解类的类名,我这里是com.example.springbootmybatis.mapper.UserMapper。这个是为后续选择哪个构造方法服务的
    definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // issue #59
    //这个就是设置对应bean定义存放的实际类,
    //这里设置成了org.mybatis.spring.mapper.MapperFactoryBean这个类,这注意这个类实现了FactoryBean接口
    definition.setBeanClass(this.mapperFactoryBeanClass);
	......

    if (!explicitFactoryUsed) {
      LOGGER.debug(() -> "Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
       //这句也比较重要,代表属性注入模式
      definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
    }
	......
  }
}

实例化@Mapper类的bean对象

AbstractBeanFactory.doGetBean方法

protected <T> T doGetBean(
	String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
	throws BeansException {
        ......
	//由于我们的是单例对象,会走到这个分支
	if (mbd.isSingleton()) {
		sharedInstance = getSingleton(beanName, () -> {
			try {
				//在这个方法中会创建bean对象,我们下面看看这个方法
				return createBean(beanName, mbd, args);
			}
			catch (BeansException ex) {
				destroySingleton(beanName);
				throw ex;
			}
		});
		// 由于我们的sharedInstance对象是,
		// 所以在这里最终会调用到FactoryBeanRegistrySupport的doGetObjectFromFactoryBean方法,
		// 返回真正的userMapper的bean对象,也就是调用MapperFactoryBean的getObject()方法
		beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
    }
}

AbstractAutowireCapableBeanFactory.doCreateBean方法

Mybatis如何加载mapper-locations配置下的xml文件

Mybatis如何加载@Mapper注解的dao

参考

https://blog.51cto.com/u_15069485/4082734
https://blog.csdn.net/caoyuanyenang/article/details/115936875

posted @ 2023-05-31 15:38  sunpeiyu  阅读(186)  评论(0编辑  收藏  举报