博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
spring boot 2.0 源码分析(二)
阅读量:6500 次
发布时间:2019-06-24

本文共 8688 字,大约阅读时间需要 28 分钟。

在上一章学习了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
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
listeners) { this.log = log; this.listeners = new ArrayList(listeners); }

在这个构造函数里,我们看到其只是把接收到的listeners参数,保存到实例变量里,没有过多的操作。

所以,重点是在listeners参数这里,而listeners是通过getSpringFactoriesInstances创建出来的,来看一下getSpringFactoriesInstances函数做了什么事情吧:

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

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

public static List
loadFactoryNames(Class
factoryClass, @Nullable ClassLoader classLoader) { String factoryClassName = factoryClass.getName(); return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList()); } private static Map
> 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 
List
createSpringFactoriesInstances(Class
type, Class
[] parameterTypes, ClassLoader classLoader, Object[] args, Set
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 源码的实现方式。

作者:Lizongshen

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

你可能感兴趣的文章
Wayland 1.0发布
查看>>
golang的goroutine是如何实现的?
查看>>
乐视云基于Kubernetes的PaaS平台建设
查看>>
R 学习笔记《十》 R语言初学者指南--图形工具
查看>>
PHP通过读取DOM抓取信息
查看>>
DICOM医学图像处理:DICOM网络传输
查看>>
nio和传统Io的区别
查看>>
移动端网页布局中需要注意事项以及解决方法总结
查看>>
(原创)Linux下查看系统版本号信息的方法
查看>>
oracle
查看>>
redis使用过程中主机内核层面的一些优化
查看>>
我也要谈谈大型网站架构之系列(2)——纵观历史演变(下)
查看>>
大话设计模式(Golang) 二、策略模式
查看>>
使用PostgreSQL 9.6 架设mediawiki服务器
查看>>
数据库服务器硬件对性能的影响
查看>>
LVM
查看>>
windows+群辉服务器环境下,搭建git版本管理
查看>>
Boolean类型
查看>>
Ubuntu 修改源
查看>>
php 几个比较实用的函数
查看>>