SpringBoot提倡通过annotation来进行bean的配置,现在spring-boot里面常用的两种创建bean的方式有@EnableAutoConfiguration和@Configuration两种方式。
@Configuration方式
Spring Application在启动的时候,@ComponentScan注解会扫描包(路径可以指定,默认的情况下就是这个目录所在的包开始扫描),当扫描到@Configuration注解以后,就会初始化这个类下面所有加了@Bean的方法,并初始化这个bean。
@Configuration
@Slf4j
public class SecondBeanConfiguration {
@Bean
@ConditionalOnMissingBean
public SecondBean secondBean() {
("log second bean");
return new SecondBean("secondBean", 2);
}}
@EnableAutoConfiguration方式
@EnableAutoConfiguration开启Spring Boot的自动配置功能。什么是自动配置功能呢?简单点说就是Spring Boot根据依赖中的jar包,自动选择实例化某些配置
@EnableAutoConfiguration注解通过读取文件里面的EnableAutoConfiguration指定的类,来初始化指定类下面的所有加了@Bean的方法,并初始化这个bean。
文件:
=\
,\
具体代码
@Slf4j
@Configuration
public class AutoFirstBeanConfiguration {
@Bean
@ConditionalOnMissingBean
public AutoFirstBean autoFirstBean() {
("log auto first bean");
return new AutoFirstBean("autoFirstBean", 1);
}}
在Spring Boot中,我们会使用@SpringBootApplication来开启Spring Boot程序。在之前的文章中我们讲到了@SpringBootApplication相当于@EnableAutoConfiguration,@ComponentScan,@Configuration三者的集合。
两种方式的差异
-
初始化的时机,@Configuration初始化的方式总是在@EnableAutoConfiguration初始化方式之前
-
@Configuration初始化的顺序和扫描的过程相关,并不能进行有效的进行指定,不方便确定文件加载的顺序
-
@EnableAutoConfiguration可以通过@AutoConfigureAfter、@AutoConfigureBefore 和 @AutoConfigureOrder来指定类的加载顺序
-
@Configuration初始化会先初始化所有被扫到加了@Configuration文件的@PostConstruct注解然后再初始化这些文件里面的@Bean注解,但是@EnableAutoConfiguration是根据文件来进行初始化的,所以会初始化完一个文件的@PostConstruct注解然后再初始化这个文件的@Bean注解,然后再接着处理另外的文件。
所以需要提供bean给其他jar包进行使用的时候,最好使用@EnableAutoConfiguration方式(spring-boot-starters里面的都是通过这种方式来进行提供的,他的所有初始化的过程全部在spring-boot-autoconfigure项目中),因为能更好的控制类文件的加载顺序。有助于维护更佳复杂的项目。
另外需要注意一点的就是,如果@EnableAutoConfiguration提供的类名称在扫描的路径之中,spring-boot会把这些类作为configuration先进行初始化了,然后@AutoConfigureAfter@AutoConfigureBefore 和 @AutoConfigureOrder 这类指定顺序的注解都会失效
@EnableAutoConfiguration剖析
先看一下@EnableAutoConfiguration的定义:
@Target()
@Retention()
@Documented
@Inherited
@AutoConfigurationPackage
@Import()public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "";
/**
* Exclude specific auto-configuration classes such that they will never be applied.
* @return the classes to exclude
*/
Class<?>[] exclude() default {};
/**
* Exclude specific auto-configuration class names such that they will never be
* applied.
* @return the class names to exclude
* @since 1.3.0
*/
String[] excludeName() default {};
}
注意这一行:@Import() AutoConfigurationImportSelector实现了ImportSelector接口,并会在实例化时调用selectImports。下面是其方法:
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata();
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
annotationMetadata);
return (());
}
这个方法中的getCandidateConfigurations会从类加载器中查找所有的META-INF/,并加载其中实现了@EnableAutoConfiguration的类。
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = (classLoader);
if (result != null) {
return result;
}
try {
Enumeration<URL> urls = (classLoader != null ?
(FACTORIES_RESOURCE_LOCATION) :
(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
while (()) {
URL url = ();
UrlResource resource = new UrlResource(url);
Properties properties = (resource);
for (<?, ?> entry : ()) {
String factoryTypeName = ((String) ()).trim();
for (String factoryImplementationName : ((String) ())) {
(factoryTypeName, ());
}
}
}
(classLoader, result);
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
我们再看一下spring-boot-autoconfigure-2.2.中的META-INF/。
里面的内容是key=value形式的,我们重点关注一下EnableAutoConfiguration:
# Auto Configure
=\
,\
,\
,\
,\
,\
,\
,\
,\
...
这里只列出了一部分内容,根据上面的代码, 所有的EnableAutoConfiguration的实现都会被自动加载。这就是自动加载的原理了。
我们仔细去看具体的实现:
@Configuration(proxyBeanMethods = false)
@AutoConfigureAfter()
@ConditionalOnProperty(prefix = "", value = "enabled", havingValue = "true",
matchIfMissing = false)public class SpringApplicationAdminJmxAutoConfiguration {
...
参考资料
-
Spring使用@Async注解
-
Spring中异步注解@Async的使用、原理及使用时可能导致的问题