• SpringBoot启动原理详解


    SpringBoot和Spring相比,有着不少优势,比如自动配置,jar直接运行等等。那么SpringBoot到底是怎么启动的呢?

    下面是SpringBoot启动的入口:

    @SpringBootApplication
    public class HelloApplication {
    
    	public static void main(String[] args) {
    		SpringApplication.run(HelloApplication.class, args);
    	}
    }
    

      一、先看一下@SpringBoot注解:

    @Target({ElementType.TYPE})   //定义其使用时机
    @Retention(RetentionPolicy.RUNTIME)  //编译程序将Annotation储存于class档中,可由VM使用反射机制的代码所读取和使用。
    @Documented //这个注解应该被 javadoc工具记录
    @Inherited //被注解的类会自动继承. 更具体地说,如果定义注解时使用了 @Inherited 标记,然后用定义的注解来标注另一个父类, 父类又有一个子类(subclass),则父类的所有属性将被继承到它的子类中.
    @SpringBootConfiguration //@SpringBootConfiguration就相当于@Configuration。JavaConfig配置形式
    @EnableAutoConfiguration
    @ComponentScan(
        excludeFilters = {@Filter(
        type = FilterType.CUSTOM,
        classes = {TypeExcludeFilter.class}
    ), @Filter(
        type = FilterType.CUSTOM,
        classes = {AutoConfigurationExcludeFilter.class}
    )}  //自动扫描并加载符合条件的组件。以通过basePackages等属性来细粒度的定制@ComponentScan自动扫描的范围,如果不指定,则默认Spring框架实现会从声明@ComponentScan所在类的package进行扫描。
    注:所以SpringBoot的启动类最好是放在root package下,因为默认不指定basePackages。
    )
    public @interface SpringBootApplication {
        @AliasFor(
            annotation = EnableAutoConfiguration.class
        )
        Class<?>[] exclude() default {};
    
        @AliasFor(
            annotation = EnableAutoConfiguration.class
        )
        String[] excludeName() default {};
    
        @AliasFor(
            annotation = ComponentScan.class,
            attribute = "basePackages"
        )
        String[] scanBasePackages() default {};
    
        @AliasFor(
            annotation = ComponentScan.class,
            attribute = "basePackageClasses"
        )
        Class<?>[] scanBasePackageClasses() default {};
    }

    所以,实际上SpringBootApplication注解相当于三个注解的组合,@SpringBootConfiguration,@ComponentScan和@EnableAutoConfiguration。

    @SpringBootConfiguration和@ComponentScan,很容易知道它的意思,一个是JavaConfig配置,一个是扫描包。关键在于@EnableAutoConfiguration注解。先来看一下这个注解:

    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @AutoConfigurationPackage
    @Import({AutoConfigurationImportSelector.class})
    public @interface EnableAutoConfiguration {
        String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
    
        Class<?>[] exclude() default {};
    
        String[] excludeName() default {};
    }

            Springboot应用启动过程中使用ConfigurationClassParser分析配置类时,如果发现注解中存在@Import(ImportSelector)的情况,就会创建一个相应的ImportSelector对象, 并调用其方法 public String[] selectImports(AnnotationMetadata annotationMetadata), 这里 EnableAutoConfigurationImportSelector的导入@Import(EnableAutoConfigurationImportSelector.class) 就属于这种情况,所以ConfigurationClassParser会实例化一个 EnableAutoConfigurationImportSelector 并调用它的 selectImports() 方法。    

      AutoConfigurationImportSelector implements DeferredImportSelector extends ImportSelector。
    下面是
    AutoConfigurationImportSelector的执行过程:
    
    
    public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
        private static final String[] NO_IMPORTS = new String[0];
        private static final Log logger = LogFactory.getLog(AutoConfigurationImportSelector.class);
        private static final String PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE = "spring.autoconfigure.exclude";
        private ConfigurableListableBeanFactory beanFactory;
        private Environment environment;
        private ClassLoader beanClassLoader;
        private ResourceLoader resourceLoader;
        public AutoConfigurationImportSelector() {
        }
    
        public String[] selectImports(AnnotationMetadata annotationMetadata) {
            if (!this.isEnabled(annotationMetadata)) {
                return NO_IMPORTS;
            } else {
                // 从配置文件中加载 AutoConfigurationMetadata
                AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
                AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
                // 获取所有候选配置类EnableAutoConfiguration
                // 使用了内部工具使用SpringFactoriesLoader,查找classpath上所有jar包中的
                // META-INFspring.factories,找出其中key为
                // org.springframework.boot.autoconfigure.EnableAutoConfiguration 
               // 的属性定义的工厂类名称。
               // 虽然参数有annotationMetadata,attributes,但在 AutoConfigurationImportSelector 的
               // 实现 getCandidateConfigurations()中,这两个参数并未使用
                List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
                //去重
                configurations = this.removeDuplicates(configurations);
                Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
                // 应用 exclusion 属性
                this.checkExcludedClasses(configurations, exclusions);
                configurations.removeAll(exclusions);
               // 应用过滤器AutoConfigurationImportFilter,
               // 对于 spring boot autoconfigure,定义了一个需要被应用的过滤器 :
               // org.springframework.boot.autoconfigure.condition.OnClassCondition,
               // 此过滤器检查候选配置类上的注解@ConditionalOnClass,如果要求的类在classpath
               // 中不存在,则这个候选配置类会被排除掉
                configurations = this.filter(configurations, autoConfigurationMetadata);
              // 现在已经找到所有需要被应用的候选配置类
              // 广播事件 AutoConfigurationImportEvent
                this.fireAutoConfigurationImportEvents(configurations, exclusions);
                return StringUtils.toStringArray(configurations);
            }
        }
    
        protected AnnotationAttributes getAttributes(AnnotationMetadata metadata) {
            String name = this.getAnnotationClass().getName();
            AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(name, true));
            Assert.notNull(attributes, () -> {
                return "No auto-configuration attributes found. Is " + metadata.getClassName() + " annotated with " + ClassUtils.getShortName(name) + "?";
            });
            return attributes;
        }
    
        protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
            List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.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;
        }
    
    
    public abstract class SpringFactoriesLoader {
        public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
        private static final Log logger = LogFactory.getLog(SpringFactoriesLoader.class);
        private static final Map<ClassLoader, MultiValueMap<String, String>> cache = new ConcurrentReferenceHashMap();
    
        public SpringFactoriesLoader() {
        }
            public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
            String factoryClassName = factoryClass.getName();
            return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
        }
    }
    
        private List<String> filter(List<String> configurations, AutoConfigurationMetadata autoConfigurationMetadata) {
            long startTime = System.nanoTime();
            String[] candidates = StringUtils.toStringArray(configurations);
            // 记录候选配置类是否需要被排除,skip为true表示需要被排除,全部初始化为false,不需要被排除
            boolean[] skip = new boolean[candidates.length];
            // 记录候选配置类中是否有任何一个候选配置类被忽略,初始化为false
            boolean skipped = false;
            Iterator var8 = this.getAutoConfigurationImportFilters().iterator();
            // 获取AutoConfigurationImportFilter并逐个应用过滤
            while(var8.hasNext()) {
                AutoConfigurationImportFilter filter = (AutoConfigurationImportFilter)var8.next();
                // 对过滤器注入其需要Aware的信息
                this.invokeAwareMethods(filter);
                // 使用此过滤器检查候选配置类跟autoConfigurationMetadata的匹配情况
                boolean[] match = filter.match(candidates, autoConfigurationMetadata);
    
                for(int i = 0; i < match.length; ++i) {
                    if (!match[i]) {
                     // 如果有某个候选配置类不符合当前过滤器,将其标记为需要被排除,
            // 并且将 skipped设置为true,表示发现了某个候选配置类需要被排除
                        skip[i] = true;
                        skipped = true;
                    }
                }
            }
    
            if (!skipped) {
            // 如果所有的候选配置类都不需要被排除,则直接返回外部参数提供的候选配置类集合
                return configurations;
            } else {
            // 逻辑走到这里因为skipped为true,表明上面的的过滤器应用逻辑中发现了某些候选配置类
            // 需要被排除,这里排除那些需要被排除的候选配置类,将那些不需要被排除的候选配置类组成
            // 一个新的集合返回给调用者
                List<String> result = new ArrayList(candidates.length);
    
                int numberFiltered;
                for(numberFiltered = 0; numberFiltered < candidates.length; ++numberFiltered) {
                    if (!skip[numberFiltered]) {
                        result.add(candidates[numberFiltered]);
                    }
                }
    
                if (logger.isTraceEnabled()) {
                    numberFiltered = configurations.size() - result.size();
                    logger.trace("Filtered " + numberFiltered + " auto configuration class in " + TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime) + " ms");
                }
    
                return new ArrayList(result);
            }
        }
    
        /**
          * 使用内部工具 SpringFactoriesLoader,查找classpath上所有jar包中的
          * META-INFspring.factories,找出其中key为
          * org.springframework.boot.autoconfigure.AutoConfigurationImportFilter 
          * 的属性定义的过滤器类并实例化。
          * AutoConfigurationImportFilter过滤器可以被注册到 spring.factories用于对自动配置类
          * 做一些限制,在这些自动配置类的字节码被读取之前做快速排除处理。
          * spring boot autoconfigure 缺省注册了一个 AutoConfigurationImportFilter :
          * org.springframework.boot.autoconfigure.condition.OnClassCondition
        **/
        protected List<AutoConfigurationImportFilter> getAutoConfigurationImportFilters() {
            return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class, this.beanClassLoader);
        }
    View Code
    
    
      二、下面看一下SpringBoot启动时run方法执行过程。
    public ConfigurableApplicationContext run(String... args) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
        this.configureHeadlessProperty();
        //从类路径下META-INF/spring.factories获取 
        SpringApplicationRunListeners listeners = getRunListeners(args);
        //回调所有的获取SpringApplicationRunListener.starting()方法
        listeners.starting();
        try {
            //封装命令行参数
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(
                        args);
            //准备环境 
            ConfigurableEnvironment environment = prepareEnvironment(listeners,
                        applicationArguments);
            //创建环境完成后回调 
            SpringApplicationRunListener.environmentPrepared();表示环境准备完成
            this.configureIgnoreBeanInfo(environment);
            //打印Banner图
            Banner printedBanner = printBanner(environment);
            //创建ApplicationContext,决定创建web的ioc还是普通的ioc  
            context = createApplicationContext();
            //异常分析报告
            exceptionReporters = getSpringFactoriesInstances(
                        SpringBootExceptionReporter.class,
                        new Class[] { ConfigurableApplicationContext.class }, context);
            //准备上下文环境,将environment保存到ioc中
            //applyInitializers():回调之前保存的所有的ApplicationContextInitializer的initialize方法 
            //listeners.contextPrepared(context) 
            //prepareContext运行完成以后回调所有的SpringApplicationRunListener的contextLoaded()
            this.prepareContext(context, environment, listeners, applicationArguments,
                        printedBanner);
            //刷新容器,ioc容器初始化(如果是web应用还会创建嵌入式的Tomcat)
            //扫描,创建,加载所有组件的地方,(配置类,组件,自动配置)
            this.refreshContext(context);
            this.afterRefresh(context, applicationArguments);
            stopWatch.stop();
            if (this.logStartupInfo) {
                new StartupInfoLogger(this.mainApplicationClass)
                               .logStarted(getApplicationLog(), stopWatch);
            }
            //所有的SpringApplicationRunListener回调started方法
            listeners.started(context);
            //从ioc容器中获取所有的ApplicationRunner和CommandLineRunner进行回调,
            //ApplicationRunner先回调,CommandLineRunner再回调
            this.callRunners(context, applicationArguments);
        }
        catch (Throwable ex) {
            this.handleRunFailure(context, ex, exceptionReporters, listeners);
            throw new IllegalStateException(ex);
        }
        try {
            //所有的SpringApplicationRunListener回调running方法
            listeners.running(context);
        }
        catch (Throwable ex) {
            this.handleRunFailure(context, ex, exceptionReporters, null);
            throw new IllegalStateException(ex);
        }
        //整个SpringBoot应用启动完成以后返回启动的ioc容器
        return context;
    }
     
  • 相关阅读:
    转:线程Thread (1)
    jquery 比较全面的API中文版地址
    IE 出现stack overflow 报错的原因归纳
    转:C#常用的集合类型(ArrayList类、Stack类、Queue类、Hashtable类、Sort)
    转:双向链表dblinklist
    转:stack
    转:queue
    解决获取同胞元素空白节点影响的问题
    取10到100的随机数方法
    进度条
  • 原文地址:https://www.cnblogs.com/heqiyoujing/p/11924792.html
Copyright © 2020-2023  润新知