序
本文主要研究一下spring.mvc.servlet.load-on-startup
spring.mvc.servlet.load-on-startup
org/springframework/boot/autoconfigure/web/servlet/WebMvcProperties.java
@ConfigurationProperties(prefix = "spring.mvc")
public class WebMvcProperties {
//......
private final Servlet servlet = new Servlet();
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;
}
}
}
WebMvcProperties.Servlet定义了loadOnStartup属性,默认为-1
DispatcherServletAutoConfiguration
org/springframework/boot/autoconfigure/web/servlet/DispatcherServletAutoConfiguration.java
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass(DispatcherServlet.class)
@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)
public class DispatcherServletAutoConfiguration {
//......
@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;
}
}
//......
}
DispatcherServletRegistrationConfiguration注册了DispatcherServletRegistrationBean,它会读取webMvcProperties.getServlet().getLoadOnStartup()然后设置其loadOnStartup属性
ServletRegistrationBean
org/springframework/boot/web/servlet/ServletRegistrationBean.java
public class ServletRegistrationBean<T extends Servlet> extends DynamicRegistrationBean<ServletRegistration.Dynamic> {
private static final String[] DEFAULT_MAPPINGS = { "/*" };
private T servlet;
private Set<String> urlMappings = new LinkedHashSet<>();
private boolean alwaysMapUrl = true;
private int loadOnStartup = -1;
private MultipartConfigElement multipartConfig;
//......
@Override
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);
}
}
}
ServletRegistrationBean定义了loadOnStartup属性,默认为-1,其configure方法会设置loadOnStartup到ServletRegistration.Dynamic
StandardWrapper
org/apache/catalina/core/StandardWrapper.java
/**
* Set the load-on-startup order value (negative value means
* load on first call).
*
* @param value New load-on-startup value
*/
@Override
public void setLoadOnStartup(int value) {
int oldLoadOnStartup = this.loadOnStartup;
this.loadOnStartup = value;
support.firePropertyChange("loadOnStartup",
Integer.valueOf(oldLoadOnStartup),
Integer.valueOf(this.loadOnStartup));
}
/**
* @return the load-on-startup order value (negative value means
* load on first call).
*/
@Override
public int getLoadOnStartup() {
if (isJspServlet && loadOnStartup < 0) {
/*
* JspServlet must always be preloaded, because its instance is
* used during registerJMX (when registering the JSP
* monitoring mbean)
*/
return Integer.MAX_VALUE;
} else {
return this.loadOnStartup;
}
}
loadOnStartup属性最后设置到了tomcat的StandardWrapper
StandardContext
org/apache/catalina/core/StandardContext.java
/**
* Load and initialize all servlets marked "load on startup" in the
* web application deployment descriptor.
*
* @param children Array of wrappers for all currently defined
* servlets (including those not declared load on startup)
* @return <code>true</code> if load on startup was considered successful
*/
public boolean loadOnStartup(Container children[]) {
// Collect "load on startup" servlets that need to be initialized
TreeMap<Integer, ArrayList<Wrapper>> map = new TreeMap<>();
for (Container child : children) {
Wrapper wrapper = (Wrapper) child;
int loadOnStartup = wrapper.getLoadOnStartup();
if (loadOnStartup < 0) {
continue;
}
Integer key = Integer.valueOf(loadOnStartup);
ArrayList<Wrapper> list = map.get(key);
if (list == null) {
list = new ArrayList<>();
map.put(key, list);
}
list.add(wrapper);
}
// Load the collected "load on startup" servlets
for (ArrayList<Wrapper> list : map.values()) {
for (Wrapper wrapper : list) {
try {
wrapper.load();
} catch (ServletException e) {
getLogger().error(sm.getString("standardContext.loadOnStartup.loadException",
getName(), wrapper.getName()), StandardWrapper.getRootCause(e));
// NOTE: load errors (including a servlet that throws
// UnavailableException from the init() method) are NOT
// fatal to application startup
// unless failCtxIfServletStartFails="true" is specified
if(getComputedFailCtxIfServletStartFails()) {
return false;
}
}
}
}
return true;
}
StandardContext的loadOnStartup方法会取出所有loadOnStartup大于等于0的wrapper,按loadOnStartup值放入到
TreeMap<Integer, ArrayList<Wrapper>>
,然后遍历该TreeMap挨个执行wrapper.load()进行加载
小结
springboot的spring.mvc.servlet.load-on-startup属性,最后设置到tomcat的StandardWrapper;而tomcat的StandardContext的loadOnStartup方法会取出所有loadOnStartup大于等于0的wrapper,按loadOnStartup值放入到TreeMap<Integer, ArrayList<Wrapper>>
,然后遍历该TreeMap挨个执行wrapper.load()进行加载。