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的時候的代理類