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容器启动时候初始化的。

posted @ 2022-09-10 10:53  Little_Monster-lhq  阅读(562)  评论(0编辑  收藏  举报