WEB项目相信大家一定都很了解,但是开发久了慢慢就忘记了基础,下面抛出两个问题:
(1)tomcat如何加载web项目的?
(2)tomcat如何加载带有spring的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的实现原理
下面以一个SSM项目的web.xml文件为例子:
Archetype Created Web Application
encodingFilter
org.springframework.web.filter.CharacterEncodingFilter
true
encoding
UTF-8
encodingFilter
/*
org.springframework.web.context.ContextLoaderListener
contextConfigLocation
/WEB_INF/applicationContext.xml
DispatcherServlet
org.springframework.web.servlet.DispatcherServlet
1
SpringMVC
*.do
/index.jsp
下面论述下上面文件的配置内容:
系统变量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容器启动时候初始化的。