原始启动
1@SpringBootApplication
2public class SpringbootDemoApplication {
3
4 public static void main(String[] args) {
5 SpringApplication.run(SpringbootDemoApplication.class, args);
6 }
7
8}
很明显这是通过@SpringbootApplication注解启动的,通过查看这个注解,发现这个注解除了一些元信息外,最重要的就三个注解分别是:
- @Configuration: 通过这个注解标注的类,会被认为是spring IOC容器管理bean的工厂类,结合@Bean注解,会把一个方法返回的对象注册到spring IOC容器上下环境中。
- @ComponentScan:会扫描被@Controller,@Service,包括@Bean 等等注解标注的类,从而把它们注入到spring容器上下文环境中。
- @EnableAutoConfiguration: 这个注解会自动的把收集来的bean注册到spring容器中,这个最重要了,后面详解。
代码如下:
1@Target(ElementType.TYPE)
2@Retention(RetentionPolicy.RUNTIME)
3@Documented
4@Inherited
5@SpringBootConfiguration
6@EnableAutoConfiguration
7@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
8 @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
9public @interface SpringBootApplication {
@SpringBootConfiguration的自身定义:
1@Target({ElementType.TYPE})
2@Retention(RetentionPolicy.RUNTIME)
3@Documented
4@Configuration
5public @interface SpringBootConfiguration {
6 @AliasFor(
7 annotation = Configuration.class
8 )
9 boolean proxyBeanMethods() default true;
10}
由上我们可以看到这个也是被@Configuration标注,所以说,springboot的启动类本身也是一个bean。
@EnableAutoConfiguration的定义
1 */
2@Target(ElementType.TYPE)
3@Retention(RetentionPolicy.RUNTIME)
4@Documented
5@Inherited
6@AutoConfigurationPackage
7@Import(AutoConfigurationImportSelector.class)
8public @interface EnableAutoConfiguration {
9...
10}
由上可以看到这个@EnableAutoConfigurations是通过@Import的支持借助AutoConfigurationImportSelector实现自动配置的功能,
这个时候得再往下看AutoConfigurationImportSelector的定义:
1/**
2 * Return the auto-configuration class names that should be considered. By default
3 * this method will load candidates using {@link SpringFactoriesLoader} with
4 * {@link #getSpringFactoriesLoaderFactoryClass()}.
5 * @param metadata the source metadata
6 * @param attributes the {@link #getAttributes(AnnotationMetadata) annotation
7 * attributes}
8 * @return a list of candidate configurations
9 */
10 protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
11 List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
12 getBeanClassLoader());
13 Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
14 + "are using a custom packaging, make sure that file is correct.");
15 return configurations;
16 }
由于内容太多,我们只看加载bean的方法即可,从解释我们可以看这个方法只加载合适的配置类,什么是合适的呢?那就只有被@Configuration,@Service..等等修饰的就可以.
而且是通过spring框架的工具类SpringFactoriesLoader去加载的,那到底是通过读取什么去加载的,我们根本不知道,这个时候再点开SpringFactoriesLoader.loadFactoryNamesz中的方法去看下定义:
1public final class SpringFactoriesLoader {
2
3 /**
4 * The location to look for factories.
5 * <p>Can be present in multiple JAR files.
6 */
7 public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
8
9
10 private static final Log logger = LogFactory.getLog(SpringFactoriesLoader.class);
11
12 private static final Map<ClassLoader, MultiValueMap<String, String>> cache = new ConcurrentReferenceHashMap<>();
13
14
15 private SpringFactoriesLoader() {
16 }
17 ...
18 /**
19 * Load the fully qualified class names of factory implementations of the
20 * given type from {@value #FACTORIES_RESOURCE_LOCATION}, using the given
21 * class loader.
22 * @param factoryType the interface or abstract class representing the factory
23 * @param classLoader the ClassLoader to use for loading resources; can be
24 * {@code null} to use the default
25 * @throws IllegalArgumentException if an error occurs while loading factory names
26 * @see #loadFactories
27 */
28 public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
29 String factoryTypeName = factoryType.getName();
30 return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
31 }
32
33 private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
34 MultiValueMap<String, String> result = cache.get(classLoader);
35 if (result != null) {
36 return result;
37 }
38
39 try {
40 Enumeration<URL> urls = (classLoader != null ?
41 classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
42 ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
43 result = new LinkedMultiValueMap<>();
44 while (urls.hasMoreElements()) {
45 URL url = urls.nextElement();
46 UrlResource resource = new UrlResource(url);
47 Properties properties = PropertiesLoaderUtils.loadProperties(resource);
48 for (Map.Entry<?, ?> entry : properties.entrySet()) {
49 String factoryTypeName = ((String) entry.getKey()).trim();
50 for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
51 result.add(factoryTypeName, factoryImplementationName.trim());
52 }
53 }
54 }
55 cache.put(classLoader, result);
56 return result;
57 }
58 catch (IOException ex) {
59 throw new IllegalArgumentException("Unable to load factories from location [" +
60 FACTORIES_RESOURCE_LOCATION + "]", ex);
61 }
62 }
可以看到这个是加载claapath下的META-INF/spring.factories配置文件。
- spring.factories是java 的properties文件,配置格式是key=value的形式,只不过key和value都是java类型的完整类名。
截图看下效果:
从图上可以看到,org.springframework.boot.autoconfigure.EnableAutoConfiguration对应的values是通过java的反射机制实例化为对应的@Configuration的java配置类,并汇总成一个加载到spring IOC容器中。
@ComponentScan的定义
1@Retention(RetentionPolicy.RUNTIME)
2@Target({ElementType.TYPE})
3@Documented
4@Repeatable(ComponentScans.class)
5public @interface ComponentScan {
6 @AliasFor("basePackages")
7 String[] value() default {};
8
9 @AliasFor("value")
10 String[] basePackages() default {};
11
12 Class<?>[] basePackageClasses() default {};
13
14 Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
15
16 Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class;
17 ...
18}
可以看到除了元信息,其他基本没什么了,它的作用就是自动扫描并加载符合条件的组件或bean,最后把这些bean注册到spring IOC容器中。
最后我们实现两个类,重新定位springboot的启动方式
1/**
2 * @Description DemoConfig
3 * @Author YiLong Wu
4 * @Date 2020-03-08 12:58
5 * @Version 1.0.0
6 */
7@Configuration
8@ComponentScan
9@EnableAutoConfiguration
10public class DemoConfig {
11}
12
13
14public class SpringbootDemoApplication {
15
16 public static void main(String[] args) {
17 SpringApplication.run(DemoConfig.class, args);
18 }
19
20}
显而易见,这就是一个main方法的配置类启动!
欢迎大家关注我微信公众号一起学习,探讨!