springboot集成mybatis源码分析-启动加载mybatis过程(二)
1、springboot项目最核心的就是自动加载配置,该功能则依赖的是一个注解@SpringBootApplication中的@EnableAutoConfiguration
2、EnableAutoConfiguration主要是通过AutoConfigurationImportSelector类来加载
以mybatis为例,*selector通过反射加载spring.factories中指定的java类,也就是加载MybatisAutoConfiguration类(该类有Configuration注解,属于配置类)
16 package org.mybatis.spring.boot.autoconfigure; 60 重点:SqlSessionFactory 和 SqlSessionTemplate 两个类 61 /** 62 * {@link EnableAutoConfiguration Auto-Configuration} for Mybatis. Contributes a 63 * {@link SqlSessionFactory} and a {@link SqlSessionTemplate}. 64 * 65 * If {@link org.mybatis.spring.annotation.MapperScan} is used, or a 66 * configuration file is specified as a property, those will be considered, 67 * otherwise this auto-configuration will attempt to register mappers based on 68 * the interface definitions in or under the root auto-configuration package. 69 * 70 * @author Eddú Meléndez 71 * @author Josh Long 72 * @author Kazuki Shimizu 73 * @author Eduardo Macarrón 74 */ 75 @org.springframework.context.annotation.Configuration 76 @ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class }) 77 @ConditionalOnBean(DataSource.class) 78 @EnableConfigurationProperties(MybatisProperties.class) 79 @AutoConfigureAfter(DataSourceAutoConfiguration.class) 80 public class MybatisAutoConfiguration { 81 82 private static final Logger logger = LoggerFactory.getLogger(MybatisAutoConfiguration.class); 83 //与mybatis配置文件对应 84 private final MybatisProperties properties; 85 86 private final Interceptor[] interceptors; 87 88 private final ResourceLoader resourceLoader; 89 90 private final DatabaseIdProvider databaseIdProvider; 91 92 private final List<ConfigurationCustomizer> configurationCustomizers; 93 94 public MybatisAutoConfiguration(MybatisProperties properties, 95 ObjectProvider<Interceptor[]> interceptorsProvider, 96 ResourceLoader resourceLoader, 97 ObjectProvider<DatabaseIdProvider> databaseIdProvider, 98 ObjectProvider<List<ConfigurationCustomizer>> configurationCustomizersProvider) { 99 this.properties = properties; 100 this.interceptors = interceptorsProvider.getIfAvailable(); 101 this.resourceLoader = resourceLoader; 102 this.databaseIdProvider = databaseIdProvider.getIfAvailable(); 103 this.configurationCustomizers = configurationCustomizersProvider.getIfAvailable(); 104 } 105 //postConstruct作用是在创建类的时候先调用, 校验配置文件是否存在 106 @PostConstruct 107 public void checkConfigFileExists() { 108 if (this.properties.isCheckConfigLocation() && StringUtils.hasText(this.properties.getConfigLocation())) { 109 Resource resource = this.resourceLoader.getResource(this.properties.getConfigLocation()); 110 Assert.state(resource.exists(), "Cannot find config location: " + resource 111 + " (please add config file or check your Mybatis configuration)"); 112 } 113 } 114 //conditionalOnMissingBean作用:在没有类的时候调用,创建sqlsessionFactory sqlsessionfactory最主要的是创建并保存了Configuration类 115 @Bean 116 @ConditionalOnMissingBean 117 public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { 118 SqlSessionFactoryBean factory = new SqlSessionFactoryBean(); 119 factory.setDataSource(dataSource); 120 factory.setVfs(SpringBootVFS.class); 121 if (StringUtils.hasText(this.properties.getConfigLocation())) { 122 factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation())); 123 } 124 Configuration configuration = this.properties.getConfiguration(); 125 if (configuration == null && !StringUtils.hasText(this.properties.getConfigLocation())) { 126 configuration = new Configuration(); 127 } 128 if (configuration != null && !CollectionUtils.isEmpty(this.configurationCustomizers)) { 129 for (ConfigurationCustomizer customizer : this.configurationCustomizers) { 130 customizer.customize(configuration); 131 } 132 } 133 factory.setConfiguration(configuration); 134 if (this.properties.getConfigurationProperties() != null) { 135 factory.setConfigurationProperties(this.properties.getConfigurationProperties()); 136 } 137 if (!ObjectUtils.isEmpty(this.interceptors)) { 138 factory.setPlugins(this.interceptors); 139 } 140 if (this.databaseIdProvider != null) { 141 factory.setDatabaseIdProvider(this.databaseIdProvider); 142 } 143 if (StringUtils.hasLength(this.properties.getTypeAliasesPackage())) { 144 factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage()); 145 } 146 if (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) { 147 factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage()); 148 } 149 if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) { 150 factory.setMapperLocations(this.properties.resolveMapperLocations()); 151 } 152 153 return factory.getObject(); 154 } 155 156 @Bean 157 @ConditionalOnMissingBean 158 public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) { 159 ExecutorType executorType = this.properties.getExecutorType(); 160 if (executorType != null) { 161 return new SqlSessionTemplate(sqlSessionFactory, executorType); 162 } else { 163 return new SqlSessionTemplate(sqlSessionFactory); 164 } 165 } 238 }
3、MybatisAutoConfiguration:
①类中有个MybatisProperties类,该类对应的是mybatis的配置文件
②类中有个sqlSessionFactory方法,作用是创建SqlSessionFactory类、Configuration类(mybatis最主要的类,保存着与mybatis相关的东西)
③SelSessionTemplate,作用是与mapperProoxy代理类有关
4、关注下Configuration中的MapperRegister类,该类是创建dao(mapper的代理类),后续具体执行dao查询操作的都是基于该类的
下边是springboot项目启动栈信息:
1、springboot启动调用SpringApplication的静态方法run
2、进入run方法:先进行springboot相关初始化
3、进入refreshContext方法:初始化springboot的上下文
4、进入refresh方法(AbstractApplicationContext)
5、通过beanfactory实力各种bean
。。。。
6、在ConfigurationClassBeanDefinitionReader类来注册加载所有的spring.facotrys中指定的类
7、最后到MybatisAutoConfiguration的registerBeanDefinition方法中,并且在new classPathMapperScanner对象中会配置environment
8、通过ClassPathMapperScanner扫描mapper文件
9、进入scanner的doscan方法(其实调用的是父类的doScan方法)
10、进入父类ClassPathBeanDefinitionScanner类的doScan方法(其中basePackages就是application的包路径,这个包路径其实就是@springBootApplication注解中的一个主机ComponentScan类得到的)
11、在findCandidateComponents中返回Set集合(mapper类的集合)
。。。。
12、当springboot调用getBean方法是才是真正创建类的时候
13、最终到创建MybatisAutoConfiguration类了
在创建之前会调用checkConfigFileExists方法(因为方法上有@PostConstruct),校验mybatis配置文件是否存在
14、以上校验完之后,到注入属性
注入的时候通过beanname调用getbean方法来获取一个bean
15、在doGetBean方法中通过getSigngton(name)方法来获取已经注册过得bean,注册的bean都存在DefaultSingletonBeanRegister类的SingtonObjects的map对象中
另外该map对象中还存了这些bean:
SqlsessionFactory:
与事务相关的bean:
环境变量:
数据源配置bean:
sqlSessionTemplate:
等等。。。。
16、getbean最终通过MapperFactoryBean中的getObject方法来获取,其中getSqlSession方法返回的是SqlSessionTemplate,也就是getMapper调用的是SQLSessionTemplate中
的方法
17、进去到SqlSessionTemplage,getMapper实际上是从Configuration对象中类获取mapper
18、进去到Configuration中,getMapper实际上是从MapperRegister对象中获取的
19、进入到MapperRegister中,getMapper是从knownMappers 的map对象中来获取的,获取到mapperProxyFactory后,mapper工厂通过newInstance来创建一个mapperProxy
代理对象
20、这个mapperProxy就是在使用userdao的时候的代理类