序
本文主要研究一下
WebMvcProperties$Servlet
org/springframework/boot/autoconfigure/web/servlet/WebMvcProperties.java
public static class Servlet {
/**
* Path of the dispatcher servlet.
*/
private String path = "/";
/**
* Load on startup priority of the dispatcher servlet.
*/
private int loadOnStartup = -1;
public String getPath() {
return this.path;
}
public void setPath(String path) {
Assert.notNull(path, "Path must not be null");
Assert.isTrue(!path.contains("*"), "Path must not contain wildcards");
this.path = path;
}
public int getLoadOnStartup() {
return this.loadOnStartup;
}
public void setLoadOnStartup(int loadOnStartup) {
this.loadOnStartup = loadOnStartup;
}
public String getServletMapping() {
if (this.path.equals("") || this.path.equals("/")) {
return "/";
}
if (this.path.endsWith("/")) {
return this.path + "*";
}
return this.path + "/*";
}
public String getPath(String path) {
String prefix = getServletPrefix();
if (!path.startsWith("/")) {
path = "/" + path;
}
return prefix + path;
}
public String getServletPrefix() {
String result = this.path;
int index = result.indexOf('*');
if (index != -1) {
result = result.substring(0, index);
}
if (result.endsWith("/")) {
result = result.substring(0, result.length() - 1);
}
return result;
}
}
loadOnStartup默认为-1
DispatcherServletAutoConfiguration
org/springframework/boot/autoconfigure/web/servlet/DispatcherServletAutoConfiguration.java
@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;
}
}
这里注册了dispatcherServlet,并设置loadOnStartup
ServletRegistrationBean
org/springframework/boot/web/servlet/ServletRegistrationBean.java
protected void configure(ServletRegistration.Dynamic registration) {
super.configure(registration);
String[] urlMapping = StringUtils.toStringArray(this.urlMappings);
if (urlMapping.length == 0 && this.alwaysMapUrl) {
urlMapping = DEFAULT_MAPPINGS;
}
if (!ObjectUtils.isEmpty(urlMapping)) {
registration.addMapping(urlMapping);
}
registration.setLoadOnStartup(this.loadOnStartup);
if (this.multipartConfig != null) {
registration.setMultipartConfig(this.multipartConfig);
}
}
configure最后设置到ServletRegistration.Dynamic
ApplicationServletRegistration
org/apache/catalina/core/ApplicationServletRegistration.java
public void setLoadOnStartup(int loadOnStartup) {
wrapper.setLoadOnStartup(loadOnStartup);
}
最后是设置到tomcat的Wrapper.loadOnStartup
TomcatWebServer
org/springframework/boot/web/embedded/tomcat/TomcatWebServer.java
public void start() throws WebServerException {
synchronized (this.monitor) {
if (this.started) {
return;
}
try {
addPreviouslyRemovedConnectors();
Connector connector = this.tomcat.getConnector();
if (connector != null && this.autoStart) {
performDeferredLoadOnStartup();
}
checkThatConnectorsHaveStarted();
this.started = true;
logger.info("Tomcat started on port(s): " + getPortsDescription(true) + " with context path '"
+ getContextPath() + "'");
}
catch (ConnectorStartFailedException ex) {
stopSilently();
throw ex;
}
catch (Exception ex) {
PortInUseException.throwIfPortBindingException(ex, () -> this.tomcat.getConnector().getPort());
throw new WebServerException("Unable to start embedded Tomcat server", ex);
}
finally {
Context context = findContext();
ContextBindings.unbindClassLoader(context, context.getNamingToken(), getClass().getClassLoader());
}
}
}
TomcatWebServer的start方法对于autoStart为true(
默认
)的会执行performDeferredLoadOnStartup
performDeferredLoadOnStartup
org/springframework/boot/web/embedded/tomcat/TomcatWebServer.java
private void performDeferredLoadOnStartup() {
try {
for (Container child : this.tomcat.getHost().findChildren()) {
if (child instanceof TomcatEmbeddedContext) {
((TomcatEmbeddedContext) child).deferredLoadOnStartup();
}
}
}
catch (Exception ex) {
if (ex instanceof WebServerException) {
throw (WebServerException) ex;
}
throw new WebServerException("Unable to start embedded Tomcat connectors", ex);
}
}
performDeferredLoadOnStartup遍历child挨个执行deferredLoadOnStartup
deferredLoadOnStartup
org/springframework/boot/web/embedded/tomcat/TomcatEmbeddedContext.java
void deferredLoadOnStartup() throws LifecycleException {
doWithThreadContextClassLoader(getLoader().getClassLoader(),
() -> getLoadOnStartupWrappers(findChildren()).forEach(this::load));
}
private Stream<Wrapper> getLoadOnStartupWrappers(Container[] children) {
Map<Integer, List<Wrapper>> grouped = new TreeMap<>();
for (Container child : children) {
Wrapper wrapper = (Wrapper) child;
int order = wrapper.getLoadOnStartup();
if (order >= 0) {
grouped.computeIfAbsent(order, (o) -> new ArrayList<>()).add(wrapper);
}
}
return grouped.values().stream().flatMap(List::stream);
}
它通过getLoadOnStartupWrappers获取Wrapper,然后挨个执行其load方法
load
org/springframework/boot/web/embedded/tomcat/TomcatEmbeddedContext.java
private void load(Wrapper wrapper) {
try {
wrapper.load();
}
catch (ServletException ex) {
String message = sm.getString("standardContext.loadOnStartup.loadException", getName(), wrapper.getName());
if (getComputedFailCtxIfServletStartFails()) {
throw new WebServerException(message, ex);
}
getLogger().error(message, StandardWrapper.getRootCause(ex));
}
}
load方法即执行wrapper.load()
load
org/apache/catalina/core/StandardWrapper.java
public synchronized void load() throws ServletException {
instance = loadServlet();
if (!instanceInitialized) {
initServlet(instance);
}
if (isJspServlet) {
StringBuilder oname = new StringBuilder(getDomain());
oname.append(":type=JspMonitor");
oname.append(getWebModuleKeyProperties());
oname.append(",name=");
oname.append(getName());
oname.append(getJ2EEKeyProperties());
try {
jspMonitorON = new ObjectName(oname.toString());
Registry.getRegistry(null, null).registerComponent(instance, jspMonitorON, null);
} catch (Exception ex) {
log.warn(sm.getString("standardWrapper.jspMonitorError", instance));
}
}
}
tomcat的StandardWrapper的load是个同步方法,它加载和初始化servlet,如果已经加载或初始化不会重复加载
小结
springboot提供了spring.mvc.servlet.load-on-startup配置,可以设置未非负,这样子springboot启动时可以对servlet进行加载与初始化。