栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

SpringBoot原理讲述

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

SpringBoot原理讲述

有了对springMVC的理解:SpringMVC原理讲述
1、Tomact启动
2、解析web.xml文件
3、DispatcherServlet实例化
4、DispatcherServlet对象.init方法,创建spring容器
5、接收请求,处理请求

springBoot中也使用了tomcat,只是和springMVC顺序不一样而已
用springBoot是先创建spring容器,再启动tomcat。

在springBoot中,比如说默认情况下使用的是tomcat,也可以使用Jetty、Undertow
只需要修改pom.xml文档中的依赖就可以。

一、springBoot是如何选择使用Tomcat还是Jetty

在springApplication的run方法中


创建完spring容器之后,会去启动对应的webServer





在这里就会创建对应的服务(tomcat/jetty)


1.1 getWebServerFactory方法
protected ServletWebServerFactory getWebServerFactory() {
	// 获取spring容器里面ServletWebServerFactory类型的bean的名字(注意这里拿的不是bean对象) 
	//可能执行到这里的时候,对应的bean对象还没有创建出来,这里只是根据bean的定义来找到符合这个bean类型的所有名字
	String[] beanNames = getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class);
	//没有拿到ServletWebServerFactory类型的名字,报错
	if (beanNames.length == 0) {
		throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to missing "
					+ "ServletWebServerFactory bean.");
	}
	//拿到多个ServletWebServerFactory的名字,报错
	if (beanNames.length > 1) {
		throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to multiple "
					+ "ServletWebServerFactory beans : " + StringUtils.arrayToCommaDelimitedString(beanNames));
	}
	//getbean,真正把bean对象创建出来
	//基于性能的考虑,并不会在第一行代码的时候就把所有ServletWebServerFactory类型的bean都创建出来,只是拿到名字。
	return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);
}
1.2 getWebServer方法

通过getWebServerFactory方法返回ServletWebServerFactory 后,会去创建对应的服务。
以Tomcat为例

public WebServer getWebServer(ServletContextInitializer... initializers) {
	if (this.disableMBeanRegistry) {
		Registry.disableRegistry();
	}
	
	Tomcat tomcat = new Tomcat();
	File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
	tomcat.setBaseDir(baseDir.getAbsolutePath());
	Connector connector = new Connector(this.protocol);
	connector.setThrowOnFailure(true);
	
	tomcat.getService().addConnector(connector);
	customizeConnector(connector);
	tomcat.setConnector(connector);
	tomcat.getHost().setAutoDeploy(false);
	configureEngine(tomcat.getEngine());
	for (Connector additionalConnector : this.additionalTomcatConnectors) {
		tomcat.getService().addConnector(additionalConnector);
	}
	prepareContext(tomcat.getHost(), initializers);
	
	return getTomcatWebServer(tomcat);
}
protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
	return new TomcatWebServer(tomcat, getPort() >= 0, getShutdown());
}
public TomcatWebServer(Tomcat tomcat, boolean autoStart, Shutdown shutdown) {
	Assert.notNull(tomcat, "Tomcat Server must not be null");
	this.tomcat = tomcat;
	this.autoStart = autoStart;
	this.gracefulShutdown = (shutdown == Shutdown.GRACEFUL) ? new GracefulShutdown(tomcat) : null;
	
	initialize();
}
private void initialize() throws WebServerException {
	synchronized (this.monitor) {
		try {
			addInstanceIdToEngineName();
			Context context = findContext();
			context.addLifecycleListener((event) -> {
				if (context.equals(event.getSource()) && Lifecycle.START_EVENT.equals(event.getType())) {
					// Remove service connectors so that protocol binding doesn't
					// happen when the service is started.
					removeServiceConnectors();
				}
			});

			// 启动Tomcat
			this.tomcat.start();
			rethrowDeferredStartupExceptions();
			try {
				ContextBindings.bindClassLoader(context, context.getNamingToken(), getClass().getClassLoader());
			}catch (NamingException ex) {}
			startDaemonAwaitThread();
		}catch (Exception ex) {
			stopSilently();
			destroySilently();
			throw new WebServerException("Unable to start embedded Tomcat", ex);
		}
	}
}
1.3 ServletWebServerFactory类型的bean定义

代码看到这里,我们认为在默认的情况下,在使用springBoot时就应该有一个ServletWebServerFactory类型的bean,不然在getWebServerFactory方法中会报错。

依然以Tomcat为例,在springBoot源码中是否有某个地方定义了对应类型的bean呢?

@Configuration(proxyBeanMethods = false)
class ServletWebServerFactoryConfiguration {

	@Configuration(proxyBeanMethods = false)
	@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
	@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
	static class EmbeddedTomcat {

		
		@Bean
		TomcatServletWebServerFactory tomcatServletWebServerFactory(
				ObjectProvider connectorCustomizers,
				ObjectProvider contextCustomizers,
				ObjectProvider> protocolHandlerCustomizers) {
			TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
			factory.getTomcatConnectorCustomizers()
					.addAll(connectorCustomizers.orderedStream().collect(Collectors.toList()));
			factory.getTomcatContextCustomizers()
					.addAll(contextCustomizers.orderedStream().collect(Collectors.toList()));
			factory.getTomcatProtocolHandlerCustomizers()
					.addAll(protocolHandlerCustomizers.orderedStream().collect(Collectors.toList()));
			return factory;
		}

	}

	
	@Configuration(proxyBeanMethods = false)
	@ConditionalOnClass({ Servlet.class, Server.class, Loader.class, WebAppContext.class })
	@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
	static class EmbeddedJetty {

		@Bean
		JettyServletWebServerFactory JettyServletWebServerFactory(
				ObjectProvider serverCustomizers) {
			JettyServletWebServerFactory factory = new JettyServletWebServerFactory();
			factory.getServerCustomizers().addAll(serverCustomizers.orderedStream().collect(Collectors.toList()));
			return factory;
		}
	}

	
	@Configuration(proxyBeanMethods = false)
	@ConditionalOnClass({ Servlet.class, Undertow.class, SslClientAuthMode.class })
	@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
	static class EmbeddedUndertow {

		@Bean
		UndertowServletWebServerFactory undertowServletWebServerFactory(
				ObjectProvider deploymentInfoCustomizers,
				ObjectProvider builderCustomizers) {
			UndertowServletWebServerFactory factory = new UndertowServletWebServerFactory();
			factory.getDeploymentInfoCustomizers()
					.addAll(deploymentInfoCustomizers.orderedStream().collect(Collectors.toList()));
			factory.getBuilderCustomizers().addAll(builderCustomizers.orderedStream().collect(Collectors.toList()));
			return factory;
		}

		@Bean
		UndertowServletWebServerFactoryCustomizer undertowServletWebServerFactoryCustomizer(
				ServerProperties serverProperties) {
			return new UndertowServletWebServerFactoryCustomizer(serverProperties);
		}
	}
}

在ServletWebServerFactoryConfiguration 类中,3个类型的bean都定义了,不会报错么?

可以看到,每个类型的bean都是定义在EmbeddedXXX 这个内部类里面,每次只会解析一个内部类的内容。

怎么决定是否需要解析对应的内部类里面的内容呢?
首先需要判断对应的内部类是否符合条件(也就是ConditionalOnClass注解对应的内容)
每个对应的条件都是不一样的。
Tomcat:@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })

jeety:@ConditionalOnClass({ Servlet.class, Server.class, Loader.class, WebAppContext.class })

Undertow:@ConditionalOnClass({ Servlet.class, Undertow.class, SslClientAuthMode.class })

1.4 @ConditionalOnClass注解底层原理

到这里,可能会有个问题。
平时在使用springBoot的时候,在整个项目里面,可能没有jeety的依赖,也没有Undertow的依赖,执行到注解这里的时候不会报错么?

首先我们了解到的现状就是:虽然没有对应的jeety/Undertow依赖,springBoot也正常使用运行了,这是为什么呢?

报错分为两部分:编译报错和运行报错。
编译报错:指的是自己的项目编译,使用的springBoot.jar(jar包里面是class文件)是已经编译好的,在使用ServletWebServerFactoryConfiguration 这个类的时候就是个class,是不需要再次编译下这个类。
运行报错:运行的时候,先拿到对应的内部类,通过反射拿到类上面的注解,那就需要加载注解里面的class类,那这个时候就会报错?
可以先大概想一下:通常在spring中,我们都会指定一个扫描路径,这里路径下面会存在很多类,那么对应spring来说,扫描主要是做什么?

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1014842.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号