spring boot 2.0 源碼分析(二)


在上一章學習了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 源碼的實現方式。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM