SpringBoot中集成官方的第三方组件是通过在POM文件中添加组件的starter的Maven依赖来完成的。添加相关的Maven依赖之后,会引入具体的jar包,在SpringBoot启动的时候会根据默认自动装配的配置类的注入条件判断是否注入该自动配置类到Spring容器中。自动配置类中会创建具体的第三方组件需要的类 。Tomcat和SpringMVC都是通过这样的方式进行集成的。SpringBoot出现之前SpringMVC项目是直接部署在Tomcat服务器中的,Tomcat是一个符合Servlet标准的Web服务器,Tomcat单独作为一个可安装软件。这种方式下Tomcat是一个完整独立的web服务器,SpringMVC项目不能脱离Web服务器直接运行,需要先部署在Tomcat服务器的目录中才能运行。SpringBoot出现之后改变了这种方式,我们可以直接运行SpringBoot项目,因为SpringBoot中可以内嵌tomcat服务器,而tomcat也是java开发的。在启动SpringBoot项目的时候直接创建tomcat服务并且把SpringMVC的项目部署在tomcat服务中。
在SpringBoot自动装配的程中,会加载/META-INF/factories文件中的以EnableAutoConfiguration为Key的配置类的字符串数组,在该类中会根据具体引入的服务器类进行创建具体的服务器对象。这些字符串数组是否真正加载进Spring容器,需要经过两方面的判断,
1.根据META-INF/spring-autoconfigure-metadata.properties文件中配置的规则判断是否需要过滤。
2.在org.springframework.context.annotation.ConfigurationClassParser#processConfigurationClass中会根据自动装配类的条件注解来判断是否进行自动加载。如果条件注解全部校验成功才会加载该配置类。
两种情况的底层校验逻辑都是一样的,都是通过调用OnWebApplicationCondition、OnClassCondition、OnBeanCondition的match方法进行判断。但是在processConfigurationClass方式的检查类型会更多,比如ConditionalOnMissingBean 等条件的检查。
| 条件注解 | 注解备注 |
|---|---|
| OnWebApplicationCondition | 判断是否符合服务器类型 |
| OnClassCondition | 判断项目中是否存在配置的类 |
| OnBeanCondition | 判断Spring容器中是否注入配置的类 |
1.META-INF/factories文件中以EnableAutoConfiguration为Key的配置类的字符串数组,其中 ServletWebServerFactoryAutoConfiguration 为创建Web服务器的自动配置类,根据META-INF/spring-autoconfigure-metadata.properties文件中配置的规则判断是否需要过滤,该文件关于ServletWebServerFactoryAutoConfiguration的配置描述如下,
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration.ConditionalOnClass=javax.servlet.ServletRequest
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration.ConditionalOnWebApplication=SERVLET
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration.AutoConfigureOrder=-2147483648
此配置说明需要在当前的项目中有javax.servlet.ServletRequest类并且WebApplication是SERVLET容器,才不会过滤。
@Configuration(proxyBeanMethods = false)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
// 判断是否有 ServletRequest 类
@ConditionalOnClass(ServletRequest.class)
// 判断 WebApplication 是 SERVLET 容器
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(ServerProperties.class)
此配置说明需要在当前的项目中有javax.servlet.ServletRequest类并且WebApplication是SERVLET容器,才会进行ServletWebServerFactoryAutoConfiguration 类的加载。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<version>2.2.5.RELEASE</version>
<scope>compile</scope>
</dependency>

SpringBoot的在创建服务器类型的时候是Servlet的,在创建SpringApplication的run方法中会调用createApplicationContext方法创建具体的AnnotationConfigServletWebServerApplicationContext类,该类继承了org.springframework.web.context.support.GenericWebApplicationContext类。OnWebApplicationCondition条件注解中就是通过判断是否有GenericWebApplicationContext类来检查是否是SERVLET类型的。
// 导入具体服务器类
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
三个Embeddedxxx表示相应的服务器实现类,每个服务器类都有自己的条件注解实现不同的加载逻辑,如果这里注入了多个web服务器,在服务器启动的时候会报错。EmbeddedTomcat类的代码如下,
@Configuration(proxyBeanMethods = false)
#当前工程下需要有Servlet、Tomcat UpgradeProtocol类
@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
#当前的Spring容器类不能有ServletWebServerFactory类型的Bean,搜索的是当前容器。
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
static class EmbeddedTomcat {
@Bean
TomcatServletWebServerFactory tomcatServletWebServerFactory(
ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers,
ObjectProvider<TomcatContextCustomizer> contextCustomizers,
ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) {
// 创建 TomcatServletWebServerFactory ,是产生TomcatServletWebServer的工厂类
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;
}
}
从以上代码看出,EmbeddedTomcat配置类能加载注入的条件是当前工程下需要有Servlet、Tomcat、 UpgradeProtocol3个类并且当前Spring容器没有注入ServletWebServerFactory类型的Bean。而这个三个类都在tomcat-embed-core.jar包中,并且Spring容器中之前没有注入ServletWebServerFactory类型的Bean,所会自动加载EmbeddedTomcat类。EmbeddedTomcat用来注入TomcatServletWebServerFactory到Spring容器,TomcatServletWebServerFactory的实现了ServletWebServerFactory接口,TomcatServletWebServerFactory可以创建具体的TomcatWebServer类。在SpringBoot启动的时候会从Spring容器中获取ServletWebServerFactory类型的Bean。至此Tomcat的自动装置解析完了。
SpringMVC中最重要的组件是DispatchServlet,SpringMVC要在Servlet类型的Web服务器运行,就要把Servlet添加到Web容器中,用Servlet来处理请求。回想一下以前的SpringMVC,我们需要在web.xml配置文件中添加Servlet的配置,会默认把SpringMVC的DispatchServlet配置到Servlet配置节点中,web.xm配置节点代码如下,
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<!--Web服务器一旦启动,Servlet就会实例化创建对象,然后初始化(预备创建对象)-->
<load-on-startup>1</load-on-startup>
</servlet>
在SpringBoot中我们使用的是内嵌式的Tomcat,那tomcat和SpringMVC的DispatchServlet是怎么关联起来的?一起往下看,META-INF/factories文件中的以EnableAutoConfiguration为Key的配置类的字符串数组,其中 DispatcherServletAutoConfiguration 和 WebMvcAutoConfiguration 是SpringMVC中最为重要的两个自动配置类,DispatcherServletAutoConfiguration 用来注册 DispatcherServlet到 Spring容器,WebMvcAutoConfiguration 用来注入MVC的相关组件到Spring容器。
1.1 根据META-INF/spring-autoconfigure-metadata.properties文件中配置的规则判断是否需要过滤,该文件关于DispatcherServletAutoConfiguration 的配置描述如下,
#WebApplication是SERVLET容器
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration.ConditionalOnWebApplication=SERVLET
#工程中需要引入 DispatcherServlet 类
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration.ConditionalOnClass=org.springframework.web.servlet.DispatcherServlet
#容器注入在 ServletWebServerFactoryAutoConfiguration(服务器自动配置类) 类之后,
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration.AutoConfigureAfter=org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration.AutoConfigureOrder=-2147483648
DispatcherServletAutoConfiguration 的逻辑为:需要在当前的项目中有DispatcherServlet类并且WebApplication是SERVLET容器,才不会过滤掉。
1.2 根据自动装配类的条件注解来判断是否进行自动加载,DispatcherServletAutoConfiguration 配置类的条件注解如下,
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass(DispatcherServlet.class)
@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)
此逻辑需要在当前的项目中有DispatcherServlet类并且WebApplication是SERVLET容器,并且注入在 DispatcherServletAutoConfiguration 类之后,才会进行DispatcherServletAutoConfiguration 类的加载。在DispatcherServletAutoConfiguration 中有两个非常重要的内部类,DispatcherServletConfiguration 和DispatcherServletRegistrationConfiguration ,这两个内部类也会根据条件注解加载来决定是否加载进Spring容器中 。DispatcherServletConfiguration 主要用来注入 DispatcherServlet ;DispatcherServletRegistrationConfiguration 主要用来注入 DispatcherServletRegistrationBean,DispatcherServletRegistrationBean 用来包装 DispatcherServlet 类,它实现了ServletContextInitializer接口,用来注册Servlet对象到tomcat中的ServletContext对象。
DispatcherServletConfiguration 中有个DispatcherServlet类型的@Bean对象,会把DispatcherServlet注入到Spring容器中,DispatcherServletConfiguration 代码如下,
@Configuration(proxyBeanMethods = false)
@Conditional(DefaultDispatcherServletCondition.class)
@ConditionalOnClass(ServletRegistration.class)
@EnableConfigurationProperties({ HttpProperties.class, WebMvcProperties.class })
protected static class DispatcherServletConfiguration {
@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServlet dispatcherServlet(HttpProperties httpProperties, WebMvcProperties webMvcProperties) {
DispatcherServlet dispatcherServlet = new DispatcherServlet();
dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());
dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());
dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound());
dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());
dispatcherServlet.setEnableLoggingRequestDetails(httpProperties.isLogRequestDetails());
return dispatcherServlet;
}
}
以上条件注解代码可知,将BeanName为 dispatcherServlet ,类型为DispatcherServlet 的Bean注入到容器中的条件为:先查找是否已经容器中是否存在名称 dispatcherServlet的Bean或者类型DispatcherServlet 的Bean,如果都不存并且当前工程中有ServletRegistration类则将DispatcherServletConfiguration注入到Spring容器中,并且还会注入DispatcherServlet类。
DispatcherServletRegistrationConfiguration 主要用来注入 DispatcherServletRegistrationBean,DispatcherServletRegistrationBean 用来包装 DispatcherServlet 类,它实现了ServletContextInitializer接口,SpringBoot用DispatcherServletRegistrationBean 来注册Servlet对象到Tomcat服务器中的ServletContext对象,在后面Tomcat启动的时候会讲具体实现。DispatcherServletRegistrationConfiguration代码如下,
@Configuration(proxyBeanMethods = false)
@Conditional(DispatcherServletRegistrationCondition.class)
@ConditionalOnClass(ServletRegistration.class)
@EnableConfigurationProperties(WebMvcProperties.class)
@Import(DispatcherServletConfiguration.class)
protected static class DispatcherServletRegistrationConfiguration {
@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
@ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet,
WebMvcProperties webMvcProperties, ObjectProvider<MultipartConfigElement> multipartConfig) {
DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet,
webMvcProperties.getServlet().getPath());
registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());
multipartConfig.ifAvailable(registration::setMultipartConfig);
return registration;
}
}
以上条件注解代码可知,将BeanName为 dispatcherServletRegistration,类型为DispatcherServletRegistrationBean的Bean注入到容器中的条件为:先查找是否已经容器中是否存在名称 dispatcherServletRegistration的Bean或者类型DispatcherServletRegistrationBean的Bean,如果都不存并且当前工程中有ServletRegistration类则将DispatcherServletRegistrationConfiguration注入到Spring容器中,并且还会注入DispatcherServletRegistrationBean类。
4. 1 根据META-INF/spring-autoconfigure-metadata.properties文件中配置的规则判断是否需要过滤,该文件关于 WebMvcAutoConfiguration 的配置描述如下,
#工程中需要引入Servlet、WebMvcConfigurer、 DispatcherServlet类 org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration.ConditionalOnClass=javax.servlet.Servlet,org.springframework.web.servlet.config.annotation.WebMvcConfigurer,org.springframework.web.servlet.DispatcherServlet
# 容器注入在 DispatcherServletAutoConfiguration、TaskExecutionAutoConfiguration、ValidationAutoConfiguration之
# 后
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration.AutoConfigureAfter=org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration,org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration.AutoConfigureOrder=-2147483638
WebMvcAutoConfiguration的逻辑为:需要在当前的项目中有Servlet、WebMvcConfigurer、DispatcherServlet3个类,才不会过滤掉。
4.2 根据自动装配类的条件注解来判断是否进行自动加载,WebMvcAutoConfiguration配置类的条件注解如下,
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
ValidationAutoConfiguration.class })
此逻辑需要当前的应用程序为SERVLET类型,当前的项目中有Servlet、DispatcherServlet、WebMvcConfigurer3个类、WebMvcConfigurationSupport没有被注入到Spring容器、在 DispatcherServletAutoConfiguration 、TaskExecutionAutoConfiguration、ValidationAutoConfiguration 类注入到Spring容器之后,才会进行WebMvcAutoConfiguration类的注入。在该中有个内部类WebMvcAutoConfigurationAdapter ,主要用来注入 SpringMVC的组件,比如ViewResolver、LocaleResolver等。
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.4.RELEASE</version>
<scope>compile</scope>
</dependency>
添加Maven依赖配置后,会引入spring-webmvc.jar包,其中有DispatcherServlet类、WebMvcConfigurer接口。而springmvc的自动配置类在ServletWebServerFactoryAutoConfiguration配置类加载之后进行加载,所以肯定会有Servlet的存在。
所以添加完springmvc的Maven依赖配置后,会自动加载 DispatcherServletAutoConfiguration类和WebMvcAutoConfiguration类以及他们配置类中其他的配置类。至此SpringMVC的自动装置解析完了。
protected void onRefresh() {
super.onRefresh();
try {
// 创建 web 服务器
createWebServer();
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}
private void createWebServer() {
WebServer webServer = this.webServer;
ServletContext servletContext = getServletContext();
// 如果为 null,则创建
if (webServer == null && servletContext == null) {
// 获取WebServerFactory
ServletWebServerFactory factory = getWebServerFactory();
// 获取 webServer ,getSelfInitializer() 返回的是 函数式接口
// 是一个 实现了@FunctionalInterface 接口的列表
this.webServer = factory.getWebServer(getSelfInitializer());
}
else if (servletContext != null) {
try {
getSelfInitializer().onStartup(servletContext);
}
catch (ServletException ex) {
throw new ApplicationContextException("Cannot initialize servlet context", ex);
}
}
initPropertySources();
}
protected ServletWebServerFactory getWebServerFactory() {
// 先获 BeanFactory ,再从 BeanFactory取 ServletWebServerFactory 类型的 BeanNames
String[] beanNames = getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class);
// 如果长度为 0,则抛异常
if (beanNames.length == 0) {
throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to missing "
+ "ServletWebServerFactory bean.");
}
// 如果长度大于 1,则抛异常
if (beanNames.length > 1) {
throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to multiple "
+ "ServletWebServerFactory beans : " + StringUtils.arrayToCommaDelimitedString(beanNames));
}
// 返回根据第一个beanName和ServletWebServerFactory类型获取容器中的Bean
return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);
}
因为之前是注入了Tomcat的TomcatServletWebServerFactory,这里返回的是前面注入到容器里面的 TomcatServletWebServerFactory,然后调用该类的getWebServer方法,传入getSelfInitializer()方法参数,这个时候不会进行getSelfInitializer()里面具体业务逻辑调用,而是生成实现了ServletContextInitializer接口的匿名类后传入方法中。ServletContextInitializer接口代码如下,
@FunctionalInterface
public interface ServletContextInitializer {
/**
* Configure the given {@link ServletContext} with any servlets, filters, listeners
* context-params and attributes necessary for initialization.
* @param servletContext the {@code ServletContext} to initialize
* @throws ServletException if any call against the given {@code ServletContext}
* throws a {@code ServletException}
*/
void onStartup(ServletContext servletContext) throws ServletException;
}
private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {
return this::selfInitialize;
}
private void selfInitialize(ServletContext servletContext) throws ServletException {
prepareWebApplicationContext(servletContext);
registerApplicationScope(servletContext);
// 注入 servletContext 类型的 bean
WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
// 注入实现了ServletContextInitializer 接口的类 ,并进行循环调用onStartup
for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
beans.onStartup(servletContext);
}
}
上面的getServletContextInitializerBeans()方法主要逻辑为:创建一个ServletContextInitializerBeans类,它实现了AbstractCollection
//getServletContextInitializerBeans具体代码
protected Collection<ServletContextInitializer> getServletContextInitializerBeans() {
// 构造ServletContextInitializerBeans对象,该对象实现了 AbstractCollection<ServletContextInitializer> 接口
// 是 ServletContextInitializer的 一个集合类。
return new ServletContextInitializerBeans(getBeanFactory());
}
// ServletContextInitializerBeans 构造方法
public ServletContextInitializerBeans(ListableBeanFactory beanFactory,
Class<? extends ServletContextInitializer>... initializerTypes) {
// 初始化 initializers
this.initializers = new LinkedMultiValueMap<>();
this.initializerTypes = (initializerTypes.length != 0) ? Arrays.asList(initializerTypes)
: Collections.singletonList(ServletContextInitializer.class);
// 在beanFactory中查找实现了 ServletContextInitializer 接口的类
// 这里包括了 ServletRegistrationBean 、FilterRegistrationBean
// DelegatingFilterProxyRegistrationBean、ServletListenerRegistrationBean 和其他。
// 添加到 initializers 集合中
addServletContextInitializerBeans(beanFactory);
// 添加适配的 Bean
addAdaptableBeans(beanFactory);
// initializers 排序
List<ServletContextInitializer> sortedInitializers = this.initializers.values().stream()
.flatMap((value) -> value.stream().sorted(AnnotationAwareOrderComparator.INSTANCE))
.collect(Collectors.toList());
// 给该集合可遍历对象赋值
this.sortedList = Collections.unmodifiableList(sortedInitializers);
// 返回 initializers
logMappings(this.initializers);
}
addServletContextInitializerBeans()方法中会找到所有实现了 ServletContextInitializer 接口的类,即会找到3.1章节中解析的DispatcherServletRegistrationBean类,都添加到 this.initializers 集合中。
5.1DispatcherServletRegistrationBean的onStartup方法主要逻辑为:把当前的DispacherServlet对象添加到从参数传进来的ServletContext对象中,onStartup具体代码如下,
public final void onStartup(ServletContext servletContext) throws ServletException {
String description = getDescription();
if (!isEnabled()) {
logger.info(StringUtils.capitalize(description) + " was not registered (disabled)");
return;
}
// 注册 Servlet 到 tomcat的 servletContext中
register(description, servletContext);
}
5.2 register()方法是具体注册当前Servlet到tomcat的 servletContext中,代码如下,
@Override
protected final void register(String description, ServletContext servletContext) {
// 添加当前的 servlet 到 tomcat 的 servletContext 中
D registration = addRegistration(description, servletContext);
if (registration == null) {
logger.info(StringUtils.capitalize(description) + " was not registered (possibly already registered?)");
return;
}
configure(registration);
}
5.3 addRegistration方法是先获取servlet名称再注册当前Servlet到参数传进来的的servletContext参数中,代码如下
@Override
protected ServletRegistration.Dynamic addRegistration(String description, ServletContext servletContext) {
// 获取 当前对象的 servlet 名称
String name = getServletName();
//添加当前的 servlet 到 tomcat 的 servletContext 中
return servletContext.addServlet(name, this.servlet);
}
5.4总结一下,这个getSelfInitializer()很关键,主要逻辑为:把springmvc中的DispatcherServlet 添加到tomcat服务器中的servletContext对象中 。
public WebServer getWebServer(ServletContextInitializer... initializers) {
if (this.disableMBeanRegistry) {
Registry.disableRegistry();
}
// 创建具体的Tomcat类
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);
// 设置 host 的自动部署属性为 false
tomcat.getHost().setAutoDeploy(false);
// 配置 engine
configureEngine(tomcat.getEngine());
for (Connector additionalConnector : this.additionalTomcatConnectors) {
tomcat.getService().addConnector(additionalConnector);
}
// 初始化 TomcatEmbeddedContext ,并把 TomcatEmbeddedContext 添加至 host 中
// 用 initializersToUse 初始化 TomcatStarter ,并设置 TomcatEmbeddedContext 的Starter属性为 TomcatStarter
// initializersToUse 为函数式接口 ,即 实现了ServletContextInitializer接口 的匿名类
prepareContext(tomcat.getHost(), initializers);
// 创建具体的 TomcatwebServer
return getTomcatWebServer(tomcat);
}
protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
return new TomcatWebServer(tomcat, getPort() >= 0);
}
TomcatWebServer的构造函数中,除了给tomcat和 autoStart赋值,还会调用初始化方法initialize,TomcatWebServer的构造方法代码如下,
public TomcatWebServer(Tomcat tomcat, boolean autoStart) {
Assert.notNull(tomcat, "Tomcat Server must not be null");
this.tomcat = tomcat;
// 自动开启
this.autoStart = autoStart;
// 初始化方法
initialize();
}
// Start the server to trigger initialization listeners
// tomcat 服务器启动
this.tomcat.start();
整个tomcat的启动比较复杂,有兴趣的可以去研究下tomcat源码,这里不做多讲直接给出结论。其中会调用上面创建的TomcatStarter类的onStartup方法,并传入tomcat的servletContext对象。TomcatStarter的onStartup具体代码如下,
public void onStartup(Set<Class<?>> classes, ServletContext servletContext) throws ServletException {
try {
// 遍历已经注册的 initializers,
for (ServletContextInitializer initializer : this.initializers) {
initializer.onStartup(servletContext);
}
}
catch (Exception ex) {
this.startUpException = ex;
// Prevent Tomcat from logging and re-throwing when we know we can
// deal with it in the main thread, but log for information here.
if (logger.isErrorEnabled()) {
logger.error("Error starting Tomcat context. Exception: " + ex.getClass().getName() + ". Message: "
+ ex.getMessage());
}
}
}
onStartup方法中会遍历 initializers 集合并且调用其onStartup()方法,而initializers包括了我们在调用getWebServer时传入的getSelfInitializer方法,该方法体的主要业务逻辑上面已经讲了(请看本章的第5点),就是给传进去的servletContext添加当前Spring容器中注入的SpringMVC的DispatchServlet类。
一、引擎主循环UE版本:4.27一、引擎主循环的位置:Launch.cpp:GuardedMain函数二、、GuardedMain函数执行逻辑:1、EnginePreInit:加载大多数模块int32ErrorLevel=EnginePreInit(CmdLine);PreInit模块加载顺序:模块加载过程:(1)注册模块中定义的UObject,同时为每个类构造一个类默认对象(CDO,记录类的默认状态,作为模板用于子类实例创建)(2)调用模块的StartUpModule方法2、FEngineLoop::Init()1、检查Engine的配置文件找出使用了哪一个GameEngine类(UGame
目录1.漏洞简介2、AJP13协议介绍Tomcat主要有两大功能:3.Tomcat远程文件包含漏洞分析4.漏洞复现 5、漏洞分析6.RCE实现的原理1.漏洞简介2020年2月20日,公开CNVD的漏洞公告中发现ApacheTomcat文件包含漏洞(CVE-2020-1938)。ApacheTomcat是Apache开源组织开发的用于处理HTTP服务的项目。ApacheTomcat服务器中被发现存在文件包含漏洞,攻击者可利用该漏洞读取或包含Tomcat上所有webapp目录下的任意文件。该漏洞是一个单独的文件包含漏洞,依赖于Tomcat的AJP(定向包协议)。AJP自身存在一定缺陷,导致存在可控
目录0专栏介绍1平面2R机器人概述2运动学建模2.1正运动学模型2.2逆运动学模型2.3机器人运动学仿真3动力学建模3.1计算动能3.2势能计算与动力学方程3.3动力学仿真0专栏介绍?附C++/Python/Matlab全套代码?课程设计、毕业设计、创新竞赛必备!详细介绍全局规划(图搜索、采样法、智能算法等);局部规划(DWA、APF等);曲线优化(贝塞尔曲线、B样条曲线等)。?详情:图解自动驾驶中的运动规划(MotionPlanning),附几十种规划算法1平面2R机器人概述如图1所示为本文的研究本体——平面2R机器人。对参数进行如下定义:机器人广义坐标
网站的日志分析,是seo优化不可忽视的一门功课,但网站越大,每天产生的日志就越大,大站一天都可以产生几个G的网站日志,如果光靠肉眼去分析,那可能看到猴年马月都看不完,因此借助网站日志分析工具去分析网站日志,那将会使网站日志分析工作变得更简单。下面推荐两款网站日志分析软件。第一款:逆火网站日志分析器逆火网站日志分析器是一款功能全面的网站服务器日志分析软件。通过分析网站的日志文件,不仅能够精准的知道网站的访问量、网站的访问来源,网站的广告点击,访客的地区统计,搜索引擎关键字查询等,还能够一次性分析多个网站的日志文件,让你轻松管理网站。逆火网站日志分析器下载地址:https://pan.baidu.
1.回顾.TransportServicepublicclassTransportServiceextendsAbstractLifecycleComponentTransportService:方法:1publicfinalTextendsTransportResponse>voidsendRequest(finalTransport.Connectionconnection,finalStringaction,finalTransportRequestrequest,finalTransportRequestOptionsoptions,TransportResponseHandlerT>
参考文章搭建文章gitte源码在线体验可以注册两个号来测试演示图:一.整体介绍 介绍SignalR一种通讯模型Hub(中心模型,或者叫集线器模型),调用这个模型写好的方法,去发送消息。 内容有: ①:Hub模型的方法介绍 ②:服务器端代码介绍 ③:前端vue3安装并调用后端方法 ④:聊天室样例整体流程:1、进入网站->调用连接SignalR的方法2、与好友发送消息->调用SignalR的自定义方法 前端通过,signalR内置方法.invoke() 去请求接口3、监听接受方法(渲染消息)通过new signalR.HubConnectionBuilder().on
一、机器人介绍 此处是基于MATLABRVC工具箱,对ABB-IRB-1200型号的微型机械臂进行正逆向运动学分析,并利Simulink工具实现对机械臂进行具有动力学参数的末端轨迹规划仿真,最后根据机械模型设计Simulink-Adams联合仿真。 图1.ABBIRB 1200尺寸参数示意图ABBIRB 1200提供的两种型号广泛适用于各作业,且两者间零部件通用,两种型号的工作范围分别为700 mm 和 900 mm,大有效负载分别为 7 kg 和5 kg。 IRB 1200 能够在狭小空间内能发挥其工作范围与性能优势,具有全新的设计、小型化的体积、高效的性能、易于集成、便捷的接
目录一.大致如下常见问题:(1)找不到程序所依赖的Qt库version`Qt_5'notfound(requiredby(2)CouldnotLoadtheQtplatformplugin"xcb"in""eventhoughitwasfound(3)打包到在不同的linux系统下,或者打包到高版本的相同系统下,运行程序时,直接提示段错误即segmentationfault,或者Illegalinstruction(coredumped)非法指令(4)ldd应用程序或者库,查看运行所依赖的库时,直接报段错误二.问题逐个分析,得出解决方法:(1)找不到程序所依赖的Qt库version`Qt_5'
我想使用ruby-prof和JMeter分析Rails应用程序。我对分析特定Controller/操作/或模型方法的建议方法不感兴趣,我想分析完整堆栈,从上到下。所以我运行这样的东西:RAILS_ENV=productionruby-prof-fprof.outscript/server>/dev/null然后我在上面运行我的JMeter测试计划。然而,问题是使用CTRL+C或SIGKILL中断它也会在ruby-prof可以写入任何输出之前杀死它。如何在不中断ruby-prof的情况下停止mongrel服务器? 最佳答案
文章目录认识unity打包目录结构游戏逆向流程Unity游戏攻击面可被攻击原因mono的打包建议方案锁血飞天无限金币攻击力翻倍以上统称内存挂透视自瞄压枪瞬移内购破解Unity游戏防御开发时注意数据安全接入第三方反作弊系统外挂检测思路狠人自爆实战查看目录结构用il2cppdumper例子2-森林whoishe后记认识unity打包目录结构dll一般很大,因为里面是所有的游戏功能编译成的二进制码游戏逆向流程开发人员代码被编译打包到GameAssembly.dll中使用il2ppDumper工具,并借助游戏名_Data\il2cpp_data\Metadata\global-metadata.dat