1. 代码
- springboot项目主类上有注解@SpringBootApplication,该注解中引入注解@EnableAutoConfiguration注解开启自动配置,@EnableAutoConfiguration中通过引入注解@Import(AutoConfigurarionImportSelector.class)导入一系列bean组件。
- 其中,AutoConfigurationImportSelector是DeferedImportSelector的实现类,DeferedImportSelector是ImportSelector的实现类,ImportSelector接口有一个方法String[] selectImports(AnnotationMetadata importingClassMetadata);
- 通过实现ImportSelector接口并重写selectImports方法来指定要导入到容器中的组件的全类名
@SpringBootApplication
public class HelloMain {
public static void main(String[] args) {
SpringApplication.run(HelloMain.class,args);
}
}
@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 {
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
//实现了DeferedImportSelector->ImportSelector,并重写了selectImports方法
AutoConfigurationImportSelector.java
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
//获得自动配置的信息
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
//获取候选配置
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
//移除重复配置
configurations = removeDuplicates(configurations);
//获取需要排除的配置并排除之
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
//通过指定的classLoader来获取一系列的要导入的组件的全限定类名
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.java
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
//factoryTypeName="org.springframework.boot.autoconfigure.EnableAutoConfiguration"
String factoryTypeName = factoryType.getName();
//获取到所有需要导入的组件的全限定类名Map后,
//get以EnableAutoConfiguration为key的组件
return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
//public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
//the location to look for fatories. Can be present in multiple JAR files.
//META-INF/spring.factories可能被包含在多个jar包中
try {
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;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
其中,spring-boot-autoconfiguration.jar中包含该文件,文件部分内容如下:
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration
2. 自动配置的例子
- 配置端口时在application.properties文件中设置server.port=8083
@Configuration(proxyBeanMethods = false)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
//在类路径下有指定类时才使xxxAutoConfiguration类生效
//@Conditional注解可以通过条件控制是否注入Bean
@ConditionalOnClass(ServletRequest.class)
//当spring服务为servlet web时,当前AutoConfiguraion类生效
@ConditionalOnWebApplication(type = Type.SERVLET)
//使使用@ConfiguraionProperties注解的类生效
@EnableConfigurationProperties(ServerProperties.class)
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {
其中,ServerProperties类如下:
//配置的前缀为server
@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties {
/**
* Server HTTP port.
*/
private Integer port;
/**
* Network address to which the server should bind.
*/
private InetAddress address;
点ServerProperties中的port即可看到其在application.properties中定义了
@EnbaleConfigurationProperties注解的作用为:
* Enable support for {@link ConfigurationProperties @ConfigurationProperties} annotated
* beans. {@code @ConfigurationProperties} beans can be registered in the standard way
* (for example using {@link Bean @Bean} methods) or, for convenience, can be specified
* directly on this annotation.
启用对带有@ConfigurationProperties注解的Bean的支持。@ConfigurationProperties beans可以使用标准方式注册(使用@Bean注解),也可以直接在这个注解(@EnableConfigurationProperties)上指定。