2. springboot2.2.x源码学习-SpringApplication构造说起

QQ: 408365330
email: egojit@qq.com


springboot2.2.x源码学习-SpringApplication构造说起

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
		// 2.1 设置资源加载器
		this.resourceLoader = resourceLoader;
		Assert.notNull(primarySources, "PrimarySources must not be null");
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
		// 2.2 判断webserver启动类型
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
		//2.3 设置Initializers
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
		//2.4  设置Listeners
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		// 2.5 设置主类也就是main函数所在的类
		this.mainApplicationClass = deduceMainApplicationClass();
	}

2.1 设置资源加载器

	我们的springboot项目main函数中一般都是 SpringApplication.run(ProxyBootApplication.class, args);

所以第一个参数resourceLoader 是null;也就是使用系统默认的资源加载器

2.2 判断webserver启动类型

三种web服务器类型

  • 1.REACTIVE
    • 说明:reactive类型是netty实现的web服务器,其实本质上就是相应是编程风格,据说性能上面更优,springboot2以上才支持的
  • 2.NONE
    • 说明:不带任何web服务器,这个可以理解成普通的main程序,以前碰到一个需求就是:根据配置来决定是否开启接口和端口,看到这里我们可以实现这种需求;
  • 3.SERVLET
    • 说明:传统的servlet方式,只要是支持servlet标准的web服务器都支持,例如tomcat,jetty……;
      springboot是如何判断选择那种类型的呢?通过
static WebApplicationType deduceFromClasspath() {
		if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
				&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
			return WebApplicationType.REACTIVE;
		}
		for (String className : SERVLET_INDICATOR_CLASSES) {
			if (!ClassUtils.isPresent(className, null)) {
				return WebApplicationType.NONE;
			}
		}
		return WebApplicationType.SERVLET;
	}

第一步:

判断是否是reactive类型的web;通过扫描所有的classpath也就是引用的jar中包含了org.springframework.web.reactive.DispatcherHandler 不能包含org.springframework.web.servlet.DispatcherServlet不能保护 org.glassfish.jersey.servlet.ServletContainer类;
从以上三个条件我们看出两点
1.要使用reactive类型web我们的项目中只能包含spring-webflux 的jar包;也就是引用下面的pom,它保护了spring-webflux;而不能包含spring-webmvc;二选其一;否则默认使用的就是非响应式web开发,而是使用传统的servlet(这是什么就不多说,java web的基础

必须引用:

<dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-webflux</artifactId>
     <version>xxxx</version>
  </dependency>

不应该引用:

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>xxxx</version>
</dependency>

第二步

判断只要 javax.servlet.Servlet 或者 org.springframework.web.context.ConfigurableWebApplicationContext两个类其中有一个不存在都不会以web服务启动;如果我们不希望springboot以web的方式启动破坏其中一个条件就行了

2.3 设置Initializers

重点是这个方法getSpringFactoriesInstances(ApplicationContextInitializer.class);这个方法的作用是从目录**META-INF/spring.factories的的文件中获取ApplicationContextInitializer类相关的配置;一下是spring-boot的jar包中META-INF/spring.factories文件关于ApplicationContextInitializer类的配置;当然其它的类在这个文件中这样配置也是类似的,例如下面 ApplicationListener,在这里我且成为springboot的factories机制


# Application Context Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer

它们的作用后面的博客会一个一个介绍;这里我们知道springboot初始化了这样一个ApplicationContextInitializer实现类的列表就行了;读源码千万不要一步追求到位;先抓大后捡小;到这里我们知道,如过我们希望给我们的项目添加自己的ApplicationContextInitializer,只要在自己的项目中新建META-INF/spring.factories,并且按照上面的格式填上内容
例如:

org.springframework.context.ApplicationContextInitializer=\
xxxxxApplicationContextInitializer

2.4 设置ApplicationListener

setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

通过springboot的factories机制从META-INF/spring.factories中读取ApplicationListener列表;和上面的ApplicationContextInitializer一样,如果我们要添加自定义的ApplicationListener,也只要新建一个META-INF/spring.factories文件并且配置上就行了;结合后面这些类的生命周期我们就知道如何在springboot启动前和springboot启动后干一些自己想干的事情了

2.5 设置主类也就是main函数所在的类

private Class<?> deduceMainApplicationClass() {
		try {
			StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
			for (StackTraceElement stackTraceElement : stackTrace) {
				if ("main".equals(stackTraceElement.getMethodName())) {
					return Class.forName(stackTraceElement.getClassName());
				}
			}
		}
		catch (ClassNotFoundException ex) {
			// Swallow and continue
		}
		return null;
	}

deduceMainApplicationClass();这个方法逻辑就是便利调用堆栈,然后比对找到main函数所在的类;我这种方式很巧妙;以后我们要找某个方法所在的类也可以通过这种方式干;看上面的逻辑我们知道,我们的项目中是不允许有多个main存在的否则后面使用这个类的时候会出问题,毕竟它只是返回了第一个包含了main函数的类


QQ: 408365330
email: egojit@qq.com

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值