Tomcat加载web项目的原理
Tomcat加载web项目的原理
WEB项目相信大家一定都很了解,但是开发久了慢慢就忘记了基础,下面抛出两个问题:
(1)tomcat如何加载web项目的?
(2)tomcat如何加载带有spring的web项目?
Tomcat如何加载web项目?
tomcat在解析web项目的war包的时候,会首先加载一个文件——web.xml文件,这个web.xml
也就是你写的应用程序配置servelt的入口,其中包含了一些url路径,最终外部就是靠这个路径定位到你的servelt的。但是你的servelt搭载的服务器tomcat只能在局域网才能找到。想被其他外网找到的话就必须搭载到一个外网可以访问的服务器。
此处介绍两个方法:
直接放到阿里云上
使用花生壳,或者ngrok。
这也就是为什么SSM项目或者SSH项目里面必须要有web.xml文件的原因。
1:tomcat 加载顺序 web.xml文件详解
(1)、启动一个WEB项目的时候,WEB容器会去读取它的配置文件web.xml,读取listener和context-param两个结点。
(2)、紧接着,容创建一个ServletContext(servlet上下文),这个web项目的所有部分都将共享这个上下文。
(3)、容器将context-param转换为键值对,并交给servletContext。
(4)、容器创建listener中的类实例,创建监听器。
2:tomcat加载web.xml后
(1)其中有一些servlet等待请求的到来。
(2)tomcat中的HttpServletRequest与HttpServletResponse分别是用来接收客户端的请求,返回处理后的请求。
Tomcat如何支持带有spring的web项目?
下面以一个SSM项目的web.xml文件为例子:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<display-name>Archetype Created Web Application</display-name>
<!-- 编码过滤器 -->
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<async-supported>true</async-supported>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- Spring监听器,用来初始化spring-ioc容器 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- Spring的配置文件-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB_INF/applicationContext.xml</param-value>
</context-param>
<!-- 注意:spring mvc框架会根据servlet-name的配置,默认去/WEB_INF/dispatcher-servlet.xml
作为spring-mvc的配置文件载入到web项目中,其中可以定义一些mvc的bean,比如说ViewResolver,或者HandlerMapping -->
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>SpringMVC</servlet-name>
<!-- 此处可以可以配置成*.do,对应struts的后缀习惯 -->
<url-pattern>*.do</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>/index.jsp</welcome-file>
</welcome-file-list>
</web-app>
下面论述下上面文件的配置内容:
系统变量contextConfigLocation告诉了spring的配置文件在哪里,这样子spring会根据路径找到这些配置文件。
contextLoaderListener实现了ServletContextListener,而ServletContextListener是在整个web工程初始化前后加入自定义代码,所以在web工程初始化前。可以完成对spring容器的初始化。注意:配置contextLoaderListener一定要保证可以找到配置文件,不然会出错。
配置DispatcherServlet,首先配置了servlet-name,这意味着会默认需要一个/WEB_INF/dispatcher-servlet.xml配置文件,同样我们配置可以在服务器启动的时候初始化完成。
servlet-mapping配置会拦截所有后缀为do的请求,然后处理他,当然最后是交给springmvc,具体是根据spring mvc中一个HandlerMapping这个骚东西去路由到指定的controller下面。
注意:
也许你的web工程中没有ContextLoaderListener这样的配置,这个时候DispatcherServlet会在初始化的时候对spring-ioc容器进行初始化。可以看DispatcherServlet源码中,如果检测到没有初始化Spring-Ioc,会首先初始化它。
DispatcherServlet的继承关系:
从图中看,DispatcherServlet继承FrameworkServlet和HttpServletBean,HttpServletBean继承了web容器的HttpServlet,所以DispatcherServlet是一个可以载入web容器的Servlet。
web容器对于servlet的初始化,首先调用其init方法,DispatcherServlet的这个方法位于其父类HttpServletBean中。
public final void init() throws ServletException {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Initializing servlet '" + this.getServletName() + "'");
}
PropertyValues pvs = new HttpServletBean.ServletConfigPropertyValues(this.getServletConfig(), this.requiredProperties);
if (!pvs.isEmpty()) {
try {
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(this.getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.getEnvironment()));
this.initBeanWrapper(bw);
bw.setPropertyValues(pvs, true);
} catch (BeansException var4) {
if (this.logger.isErrorEnabled()) {
this.logger.error("Failed to set bean properties on servlet '" + this.getServletName() + "'", var4);
}
throw var4;
}
}
// 这个方法交给子类实现
this.initServletBean();
if (this.logger.isDebugEnabled()) {
this.logger.debug("Servlet '" + this.getServletName() + "' configured successfully");
}
}
子类FrameworkServlet中的这个方法:
protected final void initServletBean() throws ServletException {
this.getServletContext().log("Initializing Spring FrameworkServlet '" + this.getServletName() + "'");
if (this.logger.isInfoEnabled()) {
this.logger.info("FrameworkServlet '" + this.getServletName() + "': initialization started");
}
long startTime = System.currentTimeMillis();
try {
//初始化 Spring Ioc容器
this.webApplicationContext = this.initWebApplicationContext();
this.initFrameworkServlet();
} catch (ServletException var5) {
this.logger.error("Context initialization failed", var5);
throw var5;
} catch (RuntimeException var6) {
this.logger.error("Context initialization failed", var6);
throw var6;
}
if (this.logger.isInfoEnabled()) {
long elapsedTime = System.currentTimeMillis() - startTime;
this.logger.info("FrameworkServlet '" + this.getServletName() + "': initialization completed in " + elapsedTime + " ms");
}
}
看initWebApplicationContext方法
protected WebApplicationContext initWebApplicationContext() {
WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
WebApplicationContext wac = null;
//判断是否被初始化
if (this.webApplicationContext != null) {
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext)wac;
if (!cwac.isActive()) {
if (cwac.getParent() == null) {
cwac.setParent(rootContext);
}
this.configureAndRefreshWebApplicationContext(cwac);
}
}
}
// IOC没有被初始化,则查找是否存在IOC容器
if (wac == null) {
wac = this.findWebApplicationContext();
}
//没有初始化,也没有找到IOC容器,则DispatcherServlet自己创建他。
if (wac == null) {
wac = this.createWebApplicationContext(rootContext);
}
// 当onRefresh没有被调用过,则执行onRefresh方法
if (!this.refreshEventReceived) {
this.onRefresh(wac);
}
if (this.publishContext) {
String attrName = this.getServletContextAttributeName();
this.getServletContext().setAttribute(attrName, wac);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Published WebApplicationContext of servlet '" + this.getServletName() + "' as ServletContext attribute with name [" + attrName + "]");
}
}
return wac;
}
下面来看 onRefresh(wac)方法,这个方法在DispatcherServlet中,其也就是在IOC容器完成后,初始化一些与SpringMVC相关的东西。看方法你就很熟悉了:
protected void onRefresh(ApplicationContext context) {
this.initStrategies(context);
}
protected void initStrategies(ApplicationContext context) {
this.initMultipartResolver(context);
this.initLocaleResolver(context);
this.initThemeResolver(context);
this.initHandlerMappings(context);
this.initHandlerAdapters(context);
this.initHandlerExceptionResolvers(context);
this.initRequestToViewNameTranslator(context);
this.initViewResolvers(context);
this.initFlashMapManager(context);
}
看到上面熟悉吧。
总结:有spring的web项目一般是在容器初始化的时候完成spring-ioc容器的初始化,比如ssm就是在web容器启动时候初始化的。