整合的核心思想就是把其他框架所产生的对象放到Spring容器中,让其成为Bean。
单独使用Mybatis框架就需要用到Mybatis所提供的一些类构造出对应的对象,然后使用该对象,就能使用到Mybatis框架给我们提供的功能,和Mybatis整合Spring就是为了将这些对象放入Spring容器中成为Bean,只要成为了Bean,在我们的Spring项目中就能很方便的使用这些对象了,也就能很方便的使用Mybatis框架所提供的功能了。
上代码:直接整springboot整合mybatis,多了自动装配而已,整合mybatis都一样的
上总结:
要处理的问题:mapper是一个接口,接口不能实例化,也就是不能干活,想让他干活的话就要用动态代理,找一个代理类帮他干活。mapper.xml有具体的sql,想让它执行就要把mapper接口的方法和xml中的sql关联起来。
mapper怎么被spring注册为beanDefinition
mapper怎么被实例化
mapper方法怎么被执行
- @MapperScan注解通过@Import方法导入MapperScannerRegistrar类,MapperScannerRegistrar实现了ImportBeanDefinitionRegistrar接口,覆写了registerBeanDefinitions方法,作用为手动注册某个Bean的BeanDefinition到容器中【DefaultListableBeanFactory=>beanDefinitionMap】
- MapperScannerRegistrar类实现了ImportBeanDefinitionRegistrar接口,当执行refresh的invokeBeanFactoryPostProcessors(这个方法的作用是用来执行Bean工厂的后置处理器)会加载所有实现了ImportBeanDefinitionRegistrar接口的类,并执行它的registerBeanDefinitions方法,即执行MapperScannerRegistrar.registerBeanDefinitions
- 在registerBeanDefinitions方法中注册一个MapperScannerConfigurer类型的BeanDefinition
其中basePackages的值包含@MapperScan注解的"com.junziln.study.order"
- MapperScannerConfigurer实现了BeanDefinitionRegistryPostProcessor接口,所以Spring在启动过程中时会调用它的postProcessBeanDefinitionRegistry()方法
- postProcessBeanDefinitionRegistry方法中创建了ClassPathMapperScanner对象,该对象对@MapperScan注解中配置的包路径进行了扫描
- 设置ClassPathMapperScanner对象可以扫描到接口,因为在Spring中是不会扫描接口的
- 同时因为ClassPathMapperScanner中重写了isCandidateComponent方法,导致isCandidateComponent只会认为接口是备选者Component解决了第一个问题
- 为每个mapper接口创建对应的BeanDefinition实例,并修改所有实例中的beanClass属性值为MapperFactoryBean,AutowireMode为byType。(通过属性的类型查找JavaBean依赖的对象并为其注入。比如类Computer有个属性printer,类型为Printer,那么,指定其autowire属性为byType后,Spring IoC容器会查找Class属性为Printer的bean,使用set方法为其注入。)
- 将所有mapper接口的BeanDefinition实例注册到Spring的容器中,为下一步实例化mapper接口做准备。
- MapperFactryBean集成了SqlSessionDaoSupport类,实现了FactoryBean接口,覆写了getObject()方法。调用了getSqlSession()去得到一个sqlSession对象,然后根据对应的Mapper接口生成一个Mapper接口代理对象,最后将代理对象注册到了Spring容器中。,但是单例池中放的依旧是MapperFactryBean的bean,代理对象是在代理对象的缓存池中,解决了第二个问题
- sqlSession对象是Mybatis中的,一个sqlSession对象需要SqlSessionFactory来产生
- MapperFactoryBean的AutowireMode为byType,所以Spring会自动调用set方法,有两个set方法,一个setSqlSessionFactory,一个setSqlSessionTemplate,而这两个方法执行的前提是根据方法参数类型能找到对应的bean,所以Spring容器中要存在SqlSessionFactory类型的bean或者SqlSessionTemplate类型的bean。
- 如果你定义的是一个SqlSessionFactory类型的bean,那么最终也会被包装为一个SqlSessionTemplate对象,并且赋值给sqlSession属性
- 而在SqlSessionTemplate类中就存在一个getMapper方法,这个方法中就产生一个Mapper接口代理对象
代码
核心依赖
<dependencies>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.22</version>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.3.0</version>
</dependency>
</dependencies>
代码
package com.junziln.study.order;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@MapperScan("com.junziln.study.order.mapper")
public class JunzilnOrderApplication {
public static void main(String[] args) {
SpringApplication.run(JunzilnOrderApplication.class, args);
}
}
server:
port: 19903
spring:
datasource:
url: jdbc:mysql://101.43.187.71:3306/blog_demo?characterEncoding=UTF-8&useUnicode=true&useSSL=false&serverTimezone=Asia/Shanghai&tinyInt1isBit=false
driver-class-name: com.mysql.cj.jdbc.Driver
username:
password:
type: com.alibaba.druid.pool.DruidDataSource
application:
name: junziln-order
mybatis:
mapper-locations: classpath:/mapper/*Mapper.xml
classpath 和 classpaths 区别: classpath:只会到你的class路径中查找找文件; classpaths:不仅包含class路径,还包括jar文件中(class路径)进行查找.**
分析
创建MapperScannerConfigurer的bean定义及扫描mapper接口
也就是扫描到我们定义的mapper接口,变成BeanDefinition。
这里是两个问题:
1.接口默认不会被spring装载成BeanDefinition。所以mybatis框架要处理。
2.接口不会被实例化,需要对接口类型进行封装。
- 通过@MapperScan导入了MapperScannerRegistrar类
在启动类使用了@MapperScan注解
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({MapperScannerRegistrar.class})
@Repeatable(MapperScans.class)
public @interface MapperScan {
String[] value() default {};
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
}
- MapperScannerRegistrar类实现了ImportBeanDefinitionRegistrar接口,当执行refresh的invokeBeanFactoryPostProcessors(这个方法的作用是用来执行Bean工厂的后置处理器)会加载所有实现了ImportBeanDefinitionRegistrar接口的类,并执行它的registerBeanDefinitions方法,即执行MapperScannerRegistrar.registerBeanDefinitions
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// 获取@MapperScan注解元数据
AnnotationAttributes mapperScanAttrs = AnnotationAttributes
.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
// @MapperScan注解元数据不为空,注册MapperScannerConfigurer的bean定义
// generateBaseName(importingClassMetada,0)返回的beanName为
// com.xx.yy.zz.Application#MapperScannerRegistrar#0
if (mapperScanAttrs != null) {
registerBeanDefinitions(importingClassMetadata, mapperScanAttrs, registry,
generateBaseBeanName(importingClassMetadata, 0));
}
}
void registerBeanDefinitions(AnnotationMetadata annoMeta, AnnotationAttributes annoAttrs,
BeanDefinitionRegistry registry, String beanName) {
// 实例化MapperScannerConfigurer的bean定义构造者
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
// 通过构造者为bean定义设置属性processPropertyPlaceHolders为true
builder.addPropertyValue("processPropertyPlaceHolders", true);
// 获取@MapperScan注解的annotationClass的属性
Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass");
if (!Annotation.class.equals(annotationClass)) {
builder.addPropertyValue("annotationClass", annotationClass);
}
// 获取@MapperScan注解的markerInterface的属性
Class<?> markerInterface = annoAttrs.getClass("markerInterface");
if (!Class.class.equals(markerInterface)) {
builder.addPropertyValue("markerInterface", markerInterface);
}
// 获取@MapperScan注解的nameGenerator的属性
Class<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator");
if (!BeanNameGenerator.class.equals(generatorClass)) {
builder.addPropertyValue("nameGenerator", BeanUtils.instantiateClass(generatorClass));
}
// 获取@MapperScan注解的factoryBean属性
Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");
if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {
builder.addPropertyValue("mapperFactoryBeanClass", mapperFactoryBeanClass);
}
// 获取@MapperScan注解的sqlSessionTemplateRef属性
String sqlSessionTemplateRef = annoAttrs.getString("sqlSessionTemplateRef");
if (StringUtils.hasText(sqlSessionTemplateRef)) {
// bean定义中设置属性名为sqlSessionTemplateBeanName,值为@MapperScan注解的sqlSessionTemplateRef属性值
builder.addPropertyValue("sqlSessionTemplateBeanName", annoAttrs.getString("sqlSessionTemplateRef"));
}
// 获取@MapperScan注解的sqlSessionFactoryRef属性
String sqlSessionFactoryRef = annoAttrs.getString("sqlSessionFactoryRef");
if (StringUtils.hasText(sqlSessionFactoryRef)) {
// bean定义中设置属性名为sqlSessionFactoryBeanName,值为@MapperScan注解的sqlSessionFactoryRef属性值
builder.addPropertyValue("sqlSessionFactoryBeanName", annoAttrs.getString("sqlSessionFactoryRef"));
}
List<String> basePackages = new ArrayList<>();
basePackages.addAll(
// 获取并合并@MapperScan中value、basePackages、basePackageClasses属性 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()));
// basePackages为空,将设置默认的basePackages,详见getDefaultBasePackage(annoMeta)方法
// 解析@MapperScan注解的类的包名,作为basePackages
if (basePackages.isEmpty()) {
basePackages.add(getDefaultBasePackage(annoMeta));
}
// 获取@MapperScan注解的lazyInitialization的属性
String lazyInitialization = annoAttrs.getString("lazyInitialization");
if (StringUtils.hasText(lazyInitialization)) {
builder.addPropertyValue("lazyInitialization", lazyInitialization);
}
// 获取@MapperScan注解的defaultScope属性
String defaultScope = annoAttrs.getString("defaultScope");
if (!AbstractBeanDefinition.SCOPE_DEFAULT.equals(defaultScope)) {
builder.addPropertyValue("defaultScope", defaultScope);
}
// bean定义添加basePackage属性
builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(basePackages));
// for spring-native
// 设置bean定义的role成员变量,ROLE_INFASTRUCTURE表示该bean为后台角色
builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
// registry中注册bean定义
registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
}
- 在registerBeanDefinitions方法中注册一个MapperScannerConfigurer类型的BeanDefinition
其中basePackages的值包含@MapperScan注解的"com.junziln.study.order"
- 而MapperScannerConfigurer实现了BeanDefinitionRegistryPostProcessor接口,所以Spring在启动过程中时会调用它的postProcessBeanDefinitionRegistry()方法,在invokeBeanFactoryPostProcessors方法里会执行
- 在postProcessBeanDefinitionRegistry方法中会生成一个ClassPathMapperScanner对象,然后进行扫描mapper
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
if (this.processPropertyPlaceHolders) {
this.processPropertyPlaceHolders();
}
// 构建一个ClassPathMapperScanner,并填充相应属性
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(this.lazyInitialization)) {
scanner.setLazyInitialization(Boolean.valueOf(this.lazyInitialization));
}
if (StringUtils.hasText(this.defaultScope)) {
scanner.setDefaultScope(this.defaultScope);
}
// 注册Filter,因为上面构造函数我们没有使用默认的Filter,
// 有两种Filter,includeFilters:要扫描的;excludeFilters:要排除的
scanner.registerFilters();
// 扫描basePackage,basePackage可通过",; \t\n"来填写多个,
// ClassPathMapperScanner重写了doScan方法
scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ",; \t\n"));
}
- 同时因为ClassPathMapperScanner中重写了isCandidateComponent方法,导致isCandidateComponent只会认为接口是备选者Component,解决了第一个问题
public int scan(String... basePackages) {
int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
// 进行真正的扫描,调用的ClassPathMapperScanner中的doScan
this.doScan(basePackages);
// Register annotation config processors, if necessary.
if (this.includeAnnotationConfig) {
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}
// 返回注册BeanDefinition的个数
return this.registry.getBeanDefinitionCount() - beanCountAtScanStart;
}
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
// 调用父类(ClassPathBeanDefinitionScanner)去扫描指定包下面的类
Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
if (beanDefinitions.isEmpty()) {
LOGGER.warn(() -> {
return "No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.";
});
} else {
// 将扫描到的beanDefinitions进行处理,把beanDefinition的beanClass替换为
this.processBeanDefinitions(beanDefinitions);
}
return beanDefinitions;
}
@Override
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();
}
- 上面主要的作用是扫描指定包下的类,并生成bean定义,然后注册到beanFactory的bean定义注册表中,并返回beanDefinitions。
- 继续执行processBeanDefinitions(beanDefinitions),接下来把扫描得到的BeanDefinition进行修改,把BeanClass修改为MapperFactoryBean,把AutowireMode修改为byType
- 设置beanClass为MapperFactryBean设置beanClass为MapperFactryBean?
因为mapper是接口,但是接口是不能实例化的,所以mybatis中就把mapper的beanDefinition的beanClass定义为mapperFactoryBeanClass,利用mapperFactoryBeanClass是通过getObject()来进行实例化,即通过jdk代理的方式,生成的代理对象,
简单地说,这里就是先生成MapperScannerConfigurer的bean定义,并放入bean定义注册表中。然后通过MapperScannerConfigurer.postProcessBeanDefinitionRegistry扫描所有mapper类,创建mapper的bean定义,也是放入bean定义注册表中,并将mapper的bean定义的BeanClass属性值设置为mapperFactoryBeanClass,为后面创建bean做准备
- 设置autoWireMode=byType 复习下这个:4、依赖注入
在父类SqlSessionDaoSupport中有两个set方法,一个setSqlSessionFactory,一个setSqlSessionTemplate,而这两个方法执行的前提是根据方法参数类型能找到对应的bean,所以Spring容器中要存在SqlSessionFactory类型的bean或者SqlSessionTemplate类型的bean。
No:即不启用自动装配。Autowire默认的值。
byName:通过属性的名字的方式查找JavaBean依赖的对象并为其注入。比如说类Computer有个属性printer,指定其autowire属性为byName后,Spring IoC容器会在配置文件中查找id/name属性为printer的bean,然后使用Seter方法为其注入。
byType:通过属性的类型查找JavaBean依赖的对象并为其注入。比如类Computer有个属性printer,类型为Printer,那么,指定其autowire属性为byType后,Spring IoC容器会查找Class属性为Printer的bean,使用set方法为其注入。
constructor:通byType一样,也是通过类型查找依赖对象。与byType的区别在于它不是使用set方法注入,而是使用构造子注入。。
如果不使用@MapperScan,会怎么处理?
加载MybatisAutoConfiguration类后,因为MybatisAutoConfiguration内部类AutoConfiguredMapperScannerRegistrar实现了ImportBeanDefinitionRegistrar接口,当执行refresh的invokeBeanFactoryPostProcessors会加载所有实现了ImportBeanDefinitionRegistrar接口的类,并执行它的registerBeanDefinitions方法,即执行AutoConfiguredMapperScannerRegistrar.registerBeanDefinitions
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");
// 获取要扫描的包名,即在哪里找mapper
// 对照示例,就是{"com.micro.provider"}
List<String> packages = AutoConfigurationPackages.get(this.beanFactory);
if (MybatisAutoConfiguration.logger.isDebugEnabled()) {
packages.forEach((pkg) -> {
MybatisAutoConfiguration.logger.debug("Using auto-configuration base package '{}'", pkg);
});
}
// 使用BeanDefinitionBuilder来构造MapperScannerConfigurer的BeanDefinition对象
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 = (Set)Stream.of(beanWrapper.getPropertyDescriptors()).map(FeatureDescriptor::getName).collect(Collectors.toSet());
if (propertyNames.contains("lazyInitialization")) {
builder.addPropertyValue("lazyInitialization", "${mybatis.lazy-initialization:false}");
}
if (propertyNames.contains("defaultScope")) {
builder.addPropertyValue("defaultScope", "${mybatis.mapper-default-scope:}");
}
// 注册该MapperScannerConfigurer的bd
registry.registerBeanDefinition(MapperScannerConfigurer.class.getName(), builder.getBeanDefinition());
}
}
这里也是生成MapperScannerConfigurer的bean定义,放到IOC容器(beanFactory)中,后面扫描mapper的逻辑和上面的是一个方法,但是执行后,发现没有找到mapper,报错:“Field userMapper in com.micro.provider.service.impl.UserServiceImpl required a bean of type ‘com.micro.provider.dao.UserMapper’ that could not be found.”,什么原因呢?上面的代码,已经说明了会扫描带@Mapper注解的作为mapper,所以需要在mapper接口上添加@Mapper注解
扫描整体流程
- .先注册MapperScannerConfigurer,它实现了BeanDefinitionRegistryPostProcessor接口。这个过程是在PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors方法第一次执行,
方法调用顺序为:
AbstractApplicationContext#refresh方法的invokeBeanFactoryPostProcessors(beanFactory)方法 ->
PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors方法中的invokeBeanDefinitionRegistryPostProcessors方法 -> ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry方法中的processConfigBeanDefinitions方法 -> parser.parse(candidates)和this.reader.loadBeanDefinitions(configClasses)方法。
这两个关键方法很关键。
parser.parse(candidates)负责处理compnentscan controller等。关键的一点是会把实现了ImportBeanDefinitionRegistrar接口的bean放到ConfigurationClass属性的importBeanDefinitionRegistrars属性中。这其中就包括@mapperscan注解引入的MapperScannerRegistrar。
然后ConfigurationClassBeanDefinitionReader类的loadBeanDefinitions方法则会处理实现了ImportBeanDefinitionRegistrar接口的bean,调用他们的registerBeanDefinitions方法,这是方式之一,
方式之二就是没加@mapperscan注解的话,由mybatis自动配置类实现,他会引入另一个实现了ImportBeanDefinitionRegistrar接口的bean,也就是AutoConfiguredMapperScannerRegistrar,然后引入MapperScannerConfigurer的过程就和方式1相同了。
关键的MapperScannerConfigurer就是这么来的。
- 然后就是MapperScannerConfigurer干活的时候了,这个过程是在PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors方法第三次执行,能执行是因为他实现了BeanDefinitionRegistryPostProcessor接口,就像把他扫描到的ConfigurationClassPostProcessor一样,但是它干的活也就到这了,后面是交给了
ClassPathBeanDefinitionScanner及他的子类ClassPathMapperScanner,方法是ClassPathBeanDefinitionScanner#doScan。ClassPathMapperScanner由mybatis提供,重写isCandidateComponent方法保证mapper接口也可以被spring扫描到。
doScan方法针对加没加@mapperscan(这里说的加没加,指的是容器中有没有MapperScannerConfigurer这个bean,看这个:添加)也有两种逻辑,加了的话会扫描到所有mapper,没加的话在自动配置类(MybatisAutoConfiguration.AutoConfiguredMapperScannerRegistrar类的registerBeanDefinitions方法中)指明@mapper注解也要被扫描到。
- 再总结一下,先注册MapperScannerConfigurer(通过自动配置或mapperscan注解),然后通过MapperScannerConfigurer,实现mapper的扫描,具体工作的bean为ClassPathBeanDefinitionScanner及他的子类ClassPathMapperScanner。
- 扫描完成后,Spring就会基于BeanDefinition去创建Bean了,相当于每个Mapper对应一个FactoryBean
- 在MapperFactoryBean中的getObject方法中,调用了getSqlSession()去得到一个sqlSession对象,然后根据对应的Mapper接口生成一个Mapper接口代理对象,这个代理对象就成为Spring容器中的Bean
实例化SqlSessionFactory、SqlSessionTemplate及mapper三个bean
执行到refresh.finishBeanFactoryInitialization(beanFactory),
那么mapper、SqlSessionFactory、SqlSessionTemplate实例化的先后顺序是怎样的呢? 这里需注意一点,在每个创建bean实例之后,初始化bean之前,会执行populateBean进行属性赋值,如果依赖其他的bean,那么会先创建依赖的bean,所以,即使先实例化的是controller(正常controller依赖的service,service依赖mapper,mapper依赖sqlSessionTemplate,sqlSessionTemplate依赖SqlSessionFactory,但实例化的顺序是反过来的),最终还是先实例化SqlSessionFactory。
- sqlSession对象是Mybatis中的,一个sqlSession对象需要SqlSessionFactory来产生
实例化SqlSessionFactory
因为SqlSessionFactory在开始的MybatisAutoConfiguration就已经用@Bean进行注解,所以会执行到
(在加载mybatis自动配置类时,会先判断SqlSessionFactory(mybatis jar包中)类和SqlSessionFactoryBean(mybatis-spring jar包中)类是否引入了,这里肯定是引入了(在mybatis-starter中),这里是为了注入bean,只是扫描到了SqlSessionFactory类,但是并没有实例化,所以会在这里进行实例化)
传入的是datasource,这个应该是在配置文件配置好的,很重要,保存数据库的连接信息,会被封装到SqlSessionFactory唯一的一个属性Configuration属性的Environment属性中
最后会调用SqlSessionFactoryBean的getObject,构建SqlSessionFactory,返回SqlSessionFactoryBean的SqlSessionFactory属性。**** **
buildSqlSessionFactory就是通过XMLConfigBuilder解析mybatis配置,通过XMLMapperBuilder解析mapper.xml的配置(重点是生成mappedStatements、resultMaps、sqlFragments等),以及其他的配置,最终放到Configuration里,供后面使用。
在XMLMapperBuilder.parse()里通过mapperRegistry.addMapper方法,会把mapper接口添加到knownMappers中(knownMappers结构为HashMap,每个mapper的value为MapperProxyFactory对象),那么后面调用getMapper的时候就可以直接获取了。所以XMLMapperBuilder.parse()方法,完成了mapper接口和mapperproxyfactory这个mapper代理工厂的绑定。(获取是为了实例化mapper接口,也就是创建mapper代理对象)
这里进行绑定的前提是在resources/mapper/**下面要有*mapper.xml文件,因为mybaitsPlus可能不会写mapper.xml,所以如果没写的话会在实例化mapper的时候进行绑定。afterPropertiesSet方法(InitializingBean接口)
- configuration创建
yml配置
自定义配置类
sqlSessionTemplate
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());
}
- 用jdk动态代理生成SqlSession代理对象
- 第一个参数表示生成代理对象的类加载器,第二个参数表示被代理类对象,第三个参数表示代理实现类(里面主要是执行代理对象时要做的事情)
代理实现是SqlSessionTemplate内部类SqlSessionInterceptor,
实例化mapper
mapper接口在前面被封装成了MapperFactoryBean,实现了InitializingBean接口,所以应该重写该接口的方法afterPropertiesSet,但是在MapperFactoryBean类中没有重新这个方法,而他的父类SqlSessionDaoSupport也没重写,所以是在SqlSessionDaoSupport的父类DaoSupport类中重写的,所以初始化MapperFactoryBean时会先调用DaoSupport中的afterPropertiesSet方法。执行MapperFactoryBean的checkDaoConfig方法
public final void afterPropertiesSet() throws IllegalArgumentException, BeanInitializationException {
this.checkDaoConfig();
try {
this.initDao();
} catch (Exception var2) {
throw new BeanInitializationException("Initialization of DAO failed", var2);
}
}
protected void checkDaoConfig() {
super.checkDaoConfig();
Assert.notNull(this.mapperInterface, "Property 'mapperInterface' is required");
//获取SqlSessionTemplate
Configuration configuration = this.getSqlSession().getConfiguration();
// 如果configuration中没有该mapper接口,则加载
// 注意,上面通过xmlMapperBuilder.parse()已经加载了mapper接口,所以这一步判断不会进入
if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
try {
// 通过 addMapper() 方法加载到configuration中,内部走的也是mapperRegistry.addMapper方法
configuration.addMapper(this.mapperInterface);
} catch (Exception var6) {
this.logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", var6);
throw new IllegalArgumentException(var6);
} finally {
ErrorContext.instance().reset();
}
}
}
mapper是什么时候放到Configuration中的呢,请看上面的checkDaoConfig(也可能是在实例化sqlSessionFactory过程中,通过xmlMapperBuilder加载进来的,两种方式最终都是调用mapperRegistry.addMapper方法加载进来)因为mybaitsplus可能没有写mapper.xml文件,所以在初始化sqlSessionFactory时不会解析xml,需要在这里进行addMapper操作。这个操作是在createBean时完成的,完成后 mapper接口就变成了MapperFactoryBean类型。并执行了MapperFactoryBean的父类的父类(DaoSupport)的afterPropertiesSet方法
上面会针对当前mapper创建MapperFactryBean类型的bean,因为mapper的bean定义的classtype就是这个MapperFactryBean类型
MapperFactoryBean.getObject
初始化之后,因为MapperFactoryBean是FactoryBean,所以会执行MapperFactoryBean.getObject(这里执行的前提条件是有service注入了这个mapper,不然是不会调用这个方法的)
为什么呢 因为跟着spring的步骤,mapper接口在调用getBean方法时会加&前缀,然后getObjectForBeanInstance判断如果加了前缀则直接返回。但是通过service的属性注入调用的getbean方法则不会加&前缀,保证了MapperFactoryBean.getObject的方法调用
- 复习下知识
- 在没有注入的情况下,依次获取bean,由于bean定义的class是MapperFactoryBean,所以肯定是factorybean,所以beanName就加了&前缀;图1
- getObjectForBeanInstance判断如果加了前缀则直接返回,返回的并不是代理对象;图2
- 在通过service注入mapper后,获取AdminLogService的bean,就会进行依赖注入,找到beanName为adminLogMapper的bean,从而取构建bean;图3,这时候就会进入到getObject的方法了。执行getObjectFromFactoryBean; 就会把代理对象放入 FactoryBean 的缓存中,而原本的单例对象MapperFactoryBean则会放到单例对象池中;
// MapperFactoryBean.java
@Override
public T getObject() throws Exception {
return getSqlSession().getMapper(this.mapperInterface);
}
getSqlSession()获取的就是上面创建的SqlSessionTemplate对象,这是mapper的SqlSession
,然后继续执行SqlSessionTemplate.getMapper
// SqlSessionDaoSupport.java
public SqlSession getSqlSession() {
return this.sqlSessionTemplate;
}
// SqlSessionTemplate.java
@Override
public <T> T getMapper(Class<T> type) {
return getConfiguration().getMapper(type, this);
}
// Configuration.java
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return this.mapperRegistry.getMapper(type, sqlSession);
}
// MapperRegistry.java
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
// mapperRegistry类中内部有knownMappers属性保存的就是
//namespace指向的dao也就是当前解析的dao
MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
} else {
try {// 通过 MapperProxyFactory.newInstance 创建 MapperProxy
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception var5) {
throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);
}
}
}
// MapperProxyFactory.java
protected T newInstance(MapperProxy<T> mapperProxy) {
return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
}
// MapperProxyFactory.java
public T newInstance(SqlSession sqlSession) {
MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
return this.newInstance(mapperProxy);
}
Configuration对象的mapperRegistry变量在Mybatis配置解析时赋值,mapperRegistry内部的knownMappers存储所有mapper接口的map信息,key为mapper接口的Class对象,value为持有对应Class对象的MapperProxyFactory实例,以此创建对应的dao的代理类MapperProxy,在该代理类中封装了mapperInterface (是被代理类的class)和SqlSessionTemplate 两个重要属性。
MapperProxyFactory是代理类工厂,用于生成代理对象MapperProxy的代理类,即为mapper接口的实际代理对象。
通过jdk代理方式创建的mapper代理对象,代理实现类为MapperProxy,以后有人调用mapper方法,就会调用代理对象的invoke方法了。也就是MapperProxy的invoke方法了。
最后把实例化mapper的bean放到bean缓存池中
执行
执行mapper方法,实际就是通过jdk代理机制,执行MapperMethod的execute方法。execute方法里会调用session的方法,这个session是sqlSessionTemplate,创建sqlSessionTemplate时创建了一个代理类,也就是SqlSessionInterceptor,然后通过sqlSessionTemplate的jdk代理机制,执行SqlSessionInterceptor的invoke方法,这个方法会获取到DefaultSqlSession,然后执行method.invoke方法,然后通过反射机制,执行DefaultSqlSession.selectList方法。通过configuration.getMappedStatement获取MappedStatement,这时找到了实际的sql了,然后到了Executor模块。执行SimpleExecutor的doQuery方法,然后执行PreparedStatementHandler的query方法,最终调用jdbc操作执行sql,最后解析结果并返回