springboot的启动过程原理

923 阅读8分钟

原始启动

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方法的配置类启动!


欢迎大家关注我微信公众号一起学习,探讨!

在这里插入图片描述
微信公众号