spring web.xml 部署
springboot war包部署
springboot 内嵌 Tomcat
spring web.xml 部署
1.加载 web.xml
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:applicationContext.xml
classpath:spring-shiro-web.xml
</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
2.创建父容器,根据 contextConfigLocation 加载资源文件
// ContextLoaderListener implements ServletContextListener
@Override
public void contextInitialized(ServletContextEvent event) {
initWebApplicationContext(event.getServletContext());
}
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:/servlet-context.xml</param-value>
</init-param>
<!-- 当值为0或者大于0时,表示容器在应用启动时就加载并初始化这个servlet -->
<!-- 当值小于0或者没有指定时,则表示容器在该servlet被选择时才会去加载 -->
<load-on-startup>1</load-on-startup>
</servlet>
3.创建 DispatcherServlet,创建子容器,根据 contextConfigLocation 加载资源文件
// FrameworkServlet
protected final void initServletBean() throws ServletException {
getServletContext().log("Initializing Spring " + getClass().getSimpleName() + " '" + getServletName() + "'");
if (logger.isInfoEnabled()) {
logger.info("Initializing Servlet '" + getServletName() + "'");
}
long startTime = System.currentTimeMillis();
try {
// 初始化容器
this.webApplicationContext = initWebApplicationContext();
// 空实现。子类有需要,可以实现该方法,实现自定义逻辑
initFrameworkServlet();
}
catch (ServletException | RuntimeException ex) {
logger.error("Context initialization failed", ex);
throw ex;
}
if (logger.isDebugEnabled()) {
String value = this.enableLoggingRequestDetails ?
"shown which may lead to unsafe logging of potentially sensitive data" :
"masked to prevent unsafe logging of potentially sensitive data";
logger.debug("enableLoggingRequestDetails='" + this.enableLoggingRequestDetails +
"': request parameters and headers will be " + value);
}
if (logger.isInfoEnabled()) {
logger.info("Completed initialization in " + (System.currentTimeMillis() - startTime) + " ms");
}
}
springboot war包部署
1.Servlet3.0中容器启动会扫描应用里面每一个jar包里面META-INF/services/javax.servlet.ServletContainerInitializer指定的实现类,启动并运行这个是实现的方法
public interface ServletContainerInitializer {
/**
* Notifies this <tt>ServletContainerInitializer</tt> of the startup of the application represented by the given
* <tt>ServletContext</tt>.
*
* <p>
* If this <tt>ServletContainerInitializer</tt> is bundled in a JAR file inside the <tt>WEB-INF/lib</tt> directory
* of an application, its <tt>onStartup</tt> method will be invoked only once during the startup of the bundling
* application. If this <tt>ServletContainerInitializer</tt> is bundled inside a JAR file outside of any
* <tt>WEB-INF/lib</tt> directory, but still discoverable as described above, its <tt>onStartup</tt> method will be
* invoked every time an application is started.
*
* @param c the Set of application classes that extend, implement, or have been annotated with the class types
* specified by the {@link javax.servlet.annotation.HandlesTypes HandlesTypes} annotation, or
* <tt>null</tt> if there are no matches, or this <tt>ServletContainerInitializer</tt> has not been
* annotated with <tt>HandlesTypes</tt>
*
* @param ctx the <tt>ServletContext</tt> of the web application that is being started and in which the classes
* contained in <tt>c</tt> were found
*
* @throws ServletException if an error has occurred
*/
public void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException;
}
-
SpringServletContainerInitializer 实现 ServletContainerInitializer,执行 WebApplicationInitializer 对应的 onStartup 方法,springboot 在这里创建
SpringApplication,执行 run 方法
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
}
public abstract class SpringBootServletInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
// Logger initialization is deferred in case an ordered
// LogServletContextInitializer is being used
this.logger = LogFactory.getLog(getClass());
WebApplicationContext rootApplicationContext = createRootApplicationContext(servletContext);
if (rootApplicationContext != null) {
servletContext.addListener(new SpringBootContextLoaderListener(rootApplicationContext, servletContext));
}
else {
this.logger.debug("No ContextLoaderListener registered, as createRootApplicationContext() did not "
+ "return an application context");
}
}
}
- 因为是通过 Tomcat main 方法启动。所以项目必须实现 SpringBootServletInitializer,才能将启动类 register,进而扫描到 @SpringBootApplication 注解,实现和 springboot main 方法一样的功能
@SpringBootApplication
public class SampleWebApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(SampleWebApplication.class, args);
}
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(SampleWebApplication.class);
}
}
springboot 内嵌 Tomcat
// ServletWebServerApplicationContext
@Override
protected void onRefresh() {
super.onRefresh();
try {
createWebServer();
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}
private void createWebServer() {
WebServer webServer = this.webServer;
ServletContext servletContext = getServletContext();
if (webServer == null && servletContext == null) {
StartupStep createWebServer = this.getApplicationStartup().start("spring.boot.webserver.create");
// ServletWebServerFactoryAutoConfiguration
ServletWebServerFactory factory = getWebServerFactory();
createWebServer.tag("factory", factory.getClass().toString());
this.webServer = factory.getWebServer(getSelfInitializer());
createWebServer.end();
getBeanFactory().registerSingleton("webServerGracefulShutdown",
new WebServerGracefulShutdownLifecycle(this.webServer));
getBeanFactory().registerSingleton("webServerStartStop",
new WebServerStartStopLifecycle(this, this.webServer));
}
else if (servletContext != null) {
try {
getSelfInitializer().onStartup(servletContext);
}
catch (ServletException ex) {
throw new ApplicationContextException("Cannot initialize servlet context", ex);
}
}
initPropertySources();
}
- war 包和 main 方法都会调用 ServletContextInitializer onStartup方法,其中 RegistrationBean 会注册 servlet、filter 等到 servletContext;
- DispatcherServletRegistrationBean 中 loadOnStartup 属性 为-1,说明容器启动的时候不初始化,第一次访问servlet时,才会初始化
private void selfInitialize(ServletContext servletContext) throws ServletException {
prepareWebApplicationContext(servletContext);
registerApplicationScope(servletContext);
WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
beans.onStartup(servletContext);
}
}
public abstract class RegistrationBean implements ServletContextInitializer, Ordered {
@Override
public final void onStartup(ServletContext servletContext) throws ServletException {
String description = getDescription();
if (!isEnabled()) {
logger.info(StringUtils.capitalize(description) + " was not registered (disabled)");
return;
}
register(description, servletContext);
}
}