zoukankan      html  css  js  c++  java
  • 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 源码的实现方式。

    作者:DSeven
    出处:http://www.cnblogs.com/lizongshen/
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接,否则保留追究法律责任的权利。

  • 相关阅读:
    设计模式学习总结:(7)工厂方法模式和抽象工厂模式
    设计模式学习总结:(6)桥模式
    设计模式学习总结:(5)装饰模式
    设计模式学习总结:(4)观察者模式
    设计模式学习总结:(3)策略模式
    设计模式学习总结:(2)模板方法模式
    [算法总结]DFS(深度优先搜索)
    [总结]拓扑排序
    [总结]树与图的遍历
    [算法总结]康托展开Cantor Expansion
  • 原文地址:https://www.cnblogs.com/lizongshen/p/9130740.html
Copyright © 2011-2022 走看看