Springboot自动装配@EnableAutoConfiguration(源码篇)——看不懂cute我

看完本章内容能学到什么?

  1. 自动装配原理
  2. 学会自动装配失败从哪里手动跟踪代码,自己排查问题,而不是谷哥度娘。

玩SpringBoot的小伙伴都知道,在项目中要创建一个DataSource对象,只需要引入部分jar依赖,并且对applicatiom.yml进行一些小的配置改动,就可以很轻松的创建目标对象。

这里维尼带大家走一波自动配置到底是如何实现的↓↓↓

走一波

application.yml

Springboot自动装配@EnableAutoConfiguration(源码篇)——看不懂cute我_第1张图片

应用启动类

@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注解瞧瞧

@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

@EnableAutoConfiguration

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
     
    ...
}

这里可以看到,直接将AutoConfigurationImportSelector.class交给IOC容器管理,那么接下来的关注点是AutoConfigurationImportSelector.class

AutoConfigurationImportSelector.class

protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
     
	if (!isEnabled(annotationMetadata)) {
     
		return EMPTY_ENTRY;
	}
	AnnotationAttributes attributes = getAttributes(annotationMetadata);
    // 获取候选配置,其实就是去查找需要自动装配的配置信息,继续断点进去
	List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
	...
}

AutoConfigurationImportSelector.getCandidateConfigurations

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;
}

SpringFactoriesLoader.loadSpringFactories

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已经拿到了所有需要自动装配的类路径,这里贴两个案例数据

Springboot自动装配@EnableAutoConfiguration(源码篇)——看不懂cute我_第2张图片

接下来的操作就很简单的。 只需要通过反射去实例化这些对象即可。
当然,有些对象实例化是需要配置的,比如本文提到的DataSource。它的自动配置类为
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration

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

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;

去注入我们需要的对象。 以上自动配置功能就实现了。

总结

经过以上源码跟踪,如果想使用自动装配,以下规则必须遵守:

  1. 自动装配的实现类必须写入META-INF/spring.factories文件内
  2. 根据相关资料阅读,得出配置规则必须是

org.springframework.boot.autoconfigure.EnableAutoConfiguration=类全路径名

比如:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.winnie.bean.UserBean

截图证明↓
Springboot自动装配@EnableAutoConfiguration(源码篇)——看不懂cute我_第3张图片

你可能感兴趣的:(java,springboot,java,spring,boot)