spring boot 2.0 源码分析(二)

扫码关注公众号:Java 技术驿站

发送:vip
将链接复制到本浏览器,永久解锁本站全部文章

【公众号:Java 技术驿站】 【加作者微信交流技术,拉技术群】

在上一章学习了spring boot 2.0启动的大概流程以后,今天我们来深挖一下SpringApplication实例变量的run函数。

先把这段run函数的代码贴出来:

        /** * Run the Spring application, creating and refreshing a new * {@link ApplicationContext}. * @param args the application arguments (usually passed from a Java main method) * @return a running {@link ApplicationContext} */
        public ConfigurableApplicationContext run(String... args) {
            StopWatch stopWatch = new StopWatch();
            stopWatch.start();
            ConfigurableApplicationContext context = null;
            Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
            configureHeadlessProperty();
            SpringApplicationRunListeners listeners = getRunListeners(args);
            listeners.starting();
            try {
                ApplicationArguments applicationArguments = new DefaultApplicationArguments(
                        args);
                ConfigurableEnvironment environment = prepareEnvironment(listeners,
                        applicationArguments);
                configureIgnoreBeanInfo(environment);
                Banner printedBanner = printBanner(environment);
                context = createApplicationContext();
                exceptionReporters = getSpringFactoriesInstances(
                        SpringBootExceptionReporter.class,
                        new Class[] { ConfigurableApplicationContext.class }, context);
                prepareContext(context, environment, listeners, applicationArguments,
                        printedBanner);
                refreshContext(context);
                afterRefresh(context, applicationArguments);
                stopWatch.stop();
                if (this.logStartupInfo) {
                    new StartupInfoLogger(this.mainApplicationClass)
                            .logStarted(getApplicationLog(), stopWatch);
                }
                listeners.started(context);
                callRunners(context, applicationArguments);
            }
            catch (Throwable ex) {
                handleRunFailure(context, ex, exceptionReporters, listeners);
                throw new IllegalStateException(ex);
            }

            try {
                listeners.running(context);
            }
            catch (Throwable ex) {
                handleRunFailure(context, ex, exceptionReporters, null);
                throw new IllegalStateException(ex);
            }
            return context;
        }

我们先来分析其中的第一个关键代码:SpringApplicationRunListeners listeners = getRunListeners(args);
这行代码是获取监听器,我们先跳转到getRunListeners中看一下:

        private SpringApplicationRunListeners getRunListeners(String[] args) {
            Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
            return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
                    SpringApplicationRunListener.class, types, this, args));
        }

在这段代码中,我们看到获取监听器,是new出来了一个SpringApplicationRunListeners实例并返回。
再次跳转到SpringApplicationRunListeners的构造函数中,看到一下发生了什么:

        SpringApplicationRunListeners(Log log, 
        Collection<? extends SpringApplicationRunListener> listeners) {
            this.log = log;
            this.listeners = new ArrayList(listeners);
        }

在这个构造函数里,我们看到其只是把接收到的listeners参数,保存到实例变量里,没有过多的操作。
所以,重点是在listeners参数这里,而listeners是通过getSpringFactoriesInstances创建出来的,来看一下getSpringFactoriesInstances函数做了什么事情吧:

        private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
                Class<?>[] parameterTypes, Object... args) {
            ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
            // Use names and ensure unique to protect against duplicates
            Set<String> names = new LinkedHashSet<>(
                    SpringFactoriesLoader.loadFactoryNames(type, classLoader));
            List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
                    classLoader, args, names);
            AnnotationAwareOrderComparator.sort(instances);
            return instances;
        }

在这里我们看到,首先创建了一个classloader,然后用SpringFactoriesLoader.loadFactoryNames(type, classLoader),加载了SpringFactoriesLoader列表。我们来看一下loadFactoryNames里面的代码:

        public static List<String> loadFactoryNames(Class<?> factoryClass,
         @Nullable ClassLoader classLoader) {
            String factoryClassName = factoryClass.getName();
            return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, 
            Collections.emptyList());
        }

        private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
            MultiValueMap result = (MultiValueMap)cache.get(classLoader);
            if(result != null) {
                return result;
            } else {
                try {
                    Enumeration ex = classLoader != null?
                    classLoader.getResources("META-INF/spring.factories")
                    :ClassLoader.getSystemResources("META-INF/spring.factories");
                    LinkedMultiValueMap result1 = new LinkedMultiValueMap();

                    while(ex.hasMoreElements()) {
                        URL url = (URL)ex.nextElement();
                        UrlResource resource = new UrlResource(url);
                        Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                        Iterator var6 = properties.entrySet().iterator();

                        while(var6.hasNext()) {
                            Entry entry = (Entry)var6.next();
                            List factoryClassNames = Arrays.asList(StringUtils
                            .commaDelimitedListToStringArray((String)entry.getValue()));
                            result1.addAll((String)entry.getKey(), factoryClassNames);
                        }
                    }

                    cache.put(classLoader, result1);
                    return result1;
                } catch (IOException var9) {
                    throw new IllegalArgumentException(
                    "Unable to load factories from location [META-INF/spring.factories]", var9);
                }
            }
        }

通过这里我们看到了其首先加载了META-INF/spring.factories这个配置文件下的所有资源,并放入缓存,然后再获取了org.springframework.context.ApplicationListener定义的资源列表。

小发现:在这里我们发现spring boot自动装配文件的位置。

获取到META-INF/spring.factories这个配置文件下的资源名称列表以后,通过createSpringFactoriesInstances函数创建了SpringFactories的实例。

        private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, 
        ClassLoader classLoader, Object[] args, Set<String> names) {
            ArrayList instances = new ArrayList(names.size());
            Iterator var7 = names.iterator();

            while(var7.hasNext()) {
                String name = (String)var7.next();

                try {
                    Class ex = ClassUtils.forName(name, classLoader);
                    Assert.isAssignable(type, ex);
                    Constructor constructor = ex.getDeclaredConstructor(parameterTypes);
                    Object instance = BeanUtils.instantiateClass(constructor, args);
                    instances.add(instance);
                } catch (Throwable var12) {
                    throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name,
                     var12);
                }
            }

            return instances;
        }

通过上面的这些代码流转,我们大概搞清楚了listeners是怎么创建出来的。
然后调用了listeners的starting方法。我们先大概地看一下EventPublishingRunListener里面的starting的实现:

        public void starting() {
            this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application,
             this.args));
        }

关键代码在这里:

        public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) {
            ResolvableType type = eventType != null?eventType:this.resolveDefaultEventType(event);
            Iterator var4 = this.getApplicationListeners(event, type).iterator();

            while(var4.hasNext()) {
                ApplicationListener listener = (ApplicationListener)var4.next();
                Executor executor = this.getTaskExecutor();
                if(executor != null) {
                    executor.execute(() -> {
                        this.invokeListener(listener, event);
                    });
                } else {
                    this.invokeListener(listener, event);
                }
            }

        }

        protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
            ErrorHandler errorHandler = this.getErrorHandler();
            if(errorHandler != null) {
                try {
                    this.doInvokeListener(listener, event);
                } catch (Throwable var5) {
                    errorHandler.handleError(var5);
                }
            } else {
                this.doInvokeListener(listener, event);
            }

        }

在上面代码中我们看到,starting就是拿到META-INF/spring.factories中定义的资源的实例以后,然后再创建一个线程去启动起来。

通过上面的这些代码我们知道了spring boot会获取META-INF/spring.factories中的资源,并创建这些资源的实例(listeners监听器),然后为每一个监听器创建一个线程启动起来。

篇幅有限, 今天就写到这里吧。有希望一起学习spring boot 2.0源码的同学可以关注我,跟我一起分析spring boot 2.0 源码的实现方式。


来源:http://ddrv.cn

赞(0) 打赏
版权归原创作者所有,任何形式的转载请联系博主:daming_90:Java 技术驿站 » spring boot 2.0 源码分析(二)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏