看完本章内容能学到什么?
玩SpringBoot的小伙伴都知道,在项目中要创建一个DataSource对象,只需要引入部分jar依赖,并且对applicatiom.yml进行一些小的配置改动,就可以很轻松的创建目标对象。
这里维尼带大家走一波自动配置到底是如何实现的↓↓↓
@SpringBootApplication
@RestController
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class);
}
@Autowired
DataSource dataSource;
@RequestMapping("/printDs")
public void printDs() throws SQLException {
if(dataSource!=null){
System.out.println("链接已被自动装配!");
}
}
}
以上DataSource作为我们关注的重点对象,其他细节暂时忽略,进入@SpringBootApplication注解瞧瞧
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
...
}
再进入@EnableAutoConfiguration
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
...
}
这里可以看到,直接将AutoConfigurationImportSelector.class交给IOC容器管理,那么接下来的关注点是AutoConfigurationImportSelector.class
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 获取候选配置,其实就是去查找需要自动装配的配置信息,继续断点进去
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
...
}
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
// 关键点,自动装配信息是通过SpringFactoriesLoader.loadFactoryNames获取的,继续断点进入
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
getBeanClassLoader());
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
+ "are using a custom packaging, make sure that file is correct.");
return configurations;
}
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
...
...
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
try {
// 资源查找核心代码,通过classLoader.getResources("META-INF/spring.factories"),去查找每个jar文件下的META-INF/spring.factories文件,然后读取装载到Properties对象中
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryTypeName = ((String) entry.getKey()).trim();
for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
result.add(factoryTypeName, factoryImplementationName.trim());
}
}
}
cache.put(classLoader, result);
return result;
}
}
以上,Spring已经拿到了所有需要自动装配的类路径,这里贴两个案例数据
接下来的操作就很简单的。 只需要通过反射去实例化这些对象即可。
当然,有些对象实例化是需要配置的,比如本文提到的DataSource。它的自动配置类为
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({
DataSource.class, EmbeddedDatabaseType.class })
@ConditionalOnMissingBean(type = "io.r2dbc.spi.ConnectionFactory")
@EnableConfigurationProperties(DataSourceProperties.class) // 这里将DataSourceProperties对象加载到了容器,为后续创建数据源提供参数支持
@Import({
DataSourcePoolMetadataProvidersConfiguration.class, DataSourceInitializationConfiguration.class })
public class DataSourceAutoConfiguration {
...
@Configuration(proxyBeanMethods = false)
@Conditional(PooledDataSourceCondition.class)
@ConditionalOnMissingBean({
DataSource.class, XADataSource.class })
@Import({
DataSourceConfiguration.Hikari.class, DataSourceConfiguration.Tomcat.class,
DataSourceConfiguration.Dbcp2.class, DataSourceConfiguration.Generic.class,
DataSourceJmxConfiguration.class })
protected static class PooledDataSourceConfiguration {
}
}
DataSourceAutoConfiguration的代码很多,很乱不容易理解。 我们可以把焦点放在如何创建DataSource这件事情上就可以了。看上述代码,引入了许多DataSourceConfiguration.*.class对象,debug进入第一个对象DataSourceConfiguration.Hikari.class
/**
* Hikari DataSource configuration.
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(HikariDataSource.class) // spring默认加载了HikariDataSource.class,所以这里为true
@ConditionalOnMissingBean(DataSource.class) // 容器启动时,DataSource还没创建,所以这里为true
@ConditionalOnProperty(name = "spring.datasource.type", havingValue = "com.zaxxer.hikari.HikariDataSource",
matchIfMissing = true)
static class Hikari {
@Bean
@ConfigurationProperties(prefix = "spring.datasource.hikari")
HikariDataSource dataSource(DataSourceProperties properties) {
HikariDataSource dataSource = createDataSource(properties, HikariDataSource.class);
if (StringUtils.hasText(properties.getName())) {
dataSource.setPoolName(properties.getName());
}
return dataSource;
}
}
public class HikariDataSource extends HikariConfig implements DataSource, Closeable {
}
可以看到,当上述条件都成立后,开始创建HikariDataSource,而HikariDataSource实现了DataSource接口,所以数据源就创建成功了。
当数据源创建成功后,就可以通过
@Autowired
DataSource dataSource;
去注入我们需要的对象。 以上自动配置功能就实现了。
经过以上源码跟踪,如果想使用自动装配,以下规则必须遵守:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=类全路径名
比如:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.winnie.bean.UserBean