从Tomcat到SpringMVC

一、核心概念

1.1 配置文件

1. Web.xml :负责配置Spring和Spring MVC,重点配置了两个地方,一个是contextConfigLocation负责将Web与Spring结合,一个是DispatcherServlet,负责整合Spring MVC

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
         http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

    <display-name>Web Application</display-name>
    
    <!--配置 spring-->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:applicationContext.xml</param-value>
    </context-param>
    <!--配置前置控制器 -->
    <servlet>
        <servlet-name>SpringDispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup> 1 </load-on-startup>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <!-- 引入spring mvc配置文件 -->
            <param-value>classpath:spring-servlet.xml</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>SpringDispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <!--2.上下文载入器,载入除DispatcherServlet加载的配置文件之外的其他配置文件-->
    <listener>
        <listener-class>org.Springframework.web.context.ContextLoadListener</listener-class>
    </listener>
    
</web-app>

2. spring的配置文件(applicationContext.xml)

由于web.xml配置的文件名为applicationContext.xml,所以Web项目只能扫描到名为applicationContext.xml的配置文件给spring使用。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans.xsd
	http://www.springframework.org/schema/context
	http://www.springframework.org/schema/context/spring-context.xsd
	http://www.springframework.org/schema/tx
	http://www.springframework.org/schema/tx/spring-tx.xsd">
    <!-- 扫描除controller包外所有使用注解的类型 -->
    <context:component-scan base-package="com.ssm">
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>
    <!-- 配置数据库相关参数properties的属性:${url} -->
   <!-- <context:property-placeholder location="classpath:jdbc.properties" /> -->
    <!-- 配置基于注解的声明式事务 -->
    <tx:annotation-driven transaction-manager="transactionManager" />
</beans>

3. spring mvc的 配置文件(spring-servlet.xml)

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans.xsd
	http://www.springframework.org/schema/context
	http://www.springframework.org/schema/context/spring-context.xsd
	http://www.springframework.org/schema/mvc
	http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
    <!-- 配置SpringMVC -->
    <!-- 1.开启SpringMVC注解模式 -->
    <!-- 简化配置:
        (1)自动注册DefaultAnootationHandlerMapping,AnotationMethodHandlerAdapter
        (2)提供一些列:数据绑定,数字和日期的format @NumberFormat, @DateTimeFormat, xml,json默认读写支持
    -->
    <mvc:annotation-driven />

    <!-- 2.静态资源默认servlet配置
        (1)加入对静态资源的处理:js,gif,png
        (2)允许使用"/"做整体映射
     -->
    <mvc:default-servlet-handler/>

    <!-- 3.配置jsp 显示ViewResolver -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
        <property name="prefix" value="/WEB-INF/jsp/" />
        <property name="suffix" value=".jsp" />
    </bean>

    <!-- 4.扫描web相关的bean -->
    <context:component-scan base-package="com.ssm.controller" />
</beans>

1.2 ContextLoadListener

该类实现了ServletContextListener接口,ServletContextListener接口提供了一个在Web容器启动时,向其中添加信息的机会。

所以 ContextLoadListener 该类作用是启动Web容器时,自动装配WebApplicationContext (完成spring容器的初始化),创建ioc容器并初始化,并将WebApplicationContext注入到ServletContext中。(WebApplicationContext是ApplicationContext的一个子类,ServletContext是每个Web应用的上下文,每个Web应用与一个ServletContext相关联。)

容器启动时,会发布事件(beforeContextInitialized、afterContextInitialized),然后调用ContextLoadListener的contextInitialized()方法,该方法调用initWebApplicationContext()方法,该方法首先验证WebApplicationContext是否存在,不存在才去创建,然后注入到ServletContext中,key为WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE所表示的值。

在创建WebApplicationContext过程中会读取ContextLoader.properties文件,从中取出XmlWebApplicationContext的全类名,然后通过反射创建一个WebApplicationContext的实现类。

创建完成后,会调用configureAndRefreshWebApplicationContext(cwac, servletContext)方法进行ioc容器的初始化。(最终会调用abstractApplicationContext的refresh()方法和FrameworkServlet的onRefresh()方法)

1.3 DispatcherServlet

DispatcherServlet配置的load-on-startup为1,即web容器启动时,就创建该实例,并执行init()方法。

DispatcherServlet的init()方法将web.xml中配置的servlet类型实例转换为BeanWrapper类型。然后调用initServletBean()方法初始化spring mvc容器。(最终会调用abstractApplicationContext的refresh()方法和FrameworkServlet的onRefresh()方法去初始化spring mvc容器,该容器为前面ContextLoadListener初始化的 ioc 容器的子容器)

二、总体流程(**)

主要介绍通过配置文件来启动web项目的流程,如果通过注解的方式的话,主要通过spi机制,导入WebApplicationInitializer的实现类。

2.1 编写配置文件

编写web.xml、spring相关的xml、springmvc相关的xml、整合mybatis的xml等等

2.2 启动tomcat启动

Tomcat对应于一个Catalina对象,一个Catalina对应一个Server,一个Server包含多个service,一个Service包含一个Container和多个Connector,Connector负责连接相关的工作,例如Http的Connector就负责将Socket接收到的Http请求封装为HttpRequest或者将HttpResponse转换为SokcetResponse发送给对应的Socket。Container则相当于一个Web容器。

一个Container包含多个Host,Host包含多个Context,每个Context包含多个Wrapper。
Wrapper相当于我们编写的每个项目中的Servlet
Context相当于我们编写的一个项目或者对应于一个web.xml文件

tomcat下的webapps目录下的每个目录都相当于一个context

2.2.1 bootstrap启动

tomcat启动就是执行bootstrap的main方法。该方法创建一个Bootstrap对象,调用init()方法。init方法初始化一个ClassLoader,并通过ClassLoader创建一个Catalina对象,赋给catalinaDemon变量。

2.2.2 Catalina启动

当执行Bootstrap传入的命令参数为start时,启动catalina。根据conf/server.xml文件创建一个server对象,并调用server的init()方法。(该配置文件在tomcat的conf目录下)
在这里插入图片描述

2.2.3 Server启动

server的 init() 方法和 strat() 方法循环调用其含有的所有 Service 的 init() 方法和 start() 方法。
server.xml中默认只有一个name为"catalina"的service,即每个tomcat服务器默认只有一个service。

该service有两个connector分别负责处理http请求和ajp请求,监听的端口号为8080和8009
该service有一个host,对应localhost这个虚拟站点

<Service name="Catalina">
    <Connector port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" />
    <!-- Define an AJP 1.3 Connector on port 8009 -->
    <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
    <Engine name="Catalina" defaultHost="localhost">
      <Realm className="org.apache.catalina.realm.LockOutRealm">
        <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
               resourceName="UserDatabase"/>
      </Realm>

      <Host name="localhost"  appBase="webapps"
            unpackWARs="true" autoDeploy="true">  
        <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
               prefix="localhost_access_log." suffix=".txt"
               pattern="%h %l %u %t &quot;%r&quot; %s %b" />
      </Host>
    </Engine>
  </Service>
2.2.4 Service启动

Service的init()和start()会分别调用container、executors、mapperListener、connectors的init()和start()。

mapperListener是Mapper的监听器,负责监听container容器的变化,executors为connectors中管理线程的线程池。
container管理servlet以及处理request,Connectors用于处理连接相关的事情。
tomcat启动时会初始化Context并启动,在启动Context的时候,加载web.xml文件。

2.2.4 Container启动

会获取所有的子容器并启动。

2.2.4 Context启动

这部分是我们关注的重点,即我们真正的web应用。

2.3 Context启动的详细流程

2.3.1 initInternal()

首先调用initInternal()方法进行一些初始化操作(在tomcat的init()阶段)。

2.3.2 startInternal()

当bootstrap调用start()方法后,一层一层的向下调用,一直到调用standardContext的startInternal()方法。

 protected synchronized void startInternal() throws LifecycleException {
           //...省略一部分      
        try {
            if (ok) {
                //...
                //加载web.xml文件,此处会发布一个configure_start事件
                //而该事件的监听者为ContextConfig,所以会调用ContextConfig的lifecycleEvent()
                //而contextConfig的lifecycleEvent()根据事件的分类,为configure_start事件
                //调用webconfig()方法,解析web.xml文件
                //将context-param元素存放到context中的parameters中,
                //将listener元素放到context中的applicationListener中
                //将filter放到context中的filterConfigs中
                //将servlet元素封装为wrapper添加到context中的child属性
                this.fireLifecycleEvent("configure_start", (Object)null);
                //...
           }

             //....
                 
             //将parameters存放到servletContext中,前面的代码会调用getServletContext,
             //如果servletContext不存在,则创建一个ApplicationContext
            this.mergeParameters();
            //启动所有的applicationListener中的监听器
            if (ok && !this.listenerStart()) {
                log.error(sm.getString("standardContext.listenerFail"));
                ok = false;
            }
            //...
            //启动所有的filter
            if (ok && !this.filterStart()) {
                log.error(sm.getString("standardContext.filterFail"));
                ok = false;
            }
            //将该context的child属性中包含的子容器(即servlet)
            //封装为wrapper然后加载(会判断load-on-startup是否大于0,大于0才加载)
            if (ok && !this.loadOnStartup(this.findChildren())) {
                log.error(sm.getString("standardContext.servletFail"));
                ok = false;
            }

            super.threadStart();
        } finally {
            this.unbindThread(oldCCL);
        }
        //。。。。
    }
2.3.3 listenerStart()

我们在web.xml中配置了ContextLoadListener,所以在此方法中,至少会执行ContextLoadListener的contextInitialized(ServletEvent event)方法。
ContextLoadListener的contextInitialized()方法完成了ApplicationContext的创建和初始化

  1. createWebApplicationContext(servletContext)----完成创建ioc容器的创建
  2. configureAndRefreshWebApplicationContext(cwac, servletContext)-----完成ioc容器的初始化
/**    ContextLoadListener的contextInitialized(ServletEvent event)
     *. 初始化web应用上下文
     */
    @Override
    public void contextInitialized(ServletContextEvent event) {
        initWebApplicationContext(event.getServletContext());
    }

==========================================
//主要关注两个重点:
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
        if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
            throw new IllegalStateException(
                    "Cannot initialize context because there is already a root application context present - " +
                    "check whether you have multiple ContextLoader* definitions in your web.xml!");
        }

        //....一些其他代码
        long startTime = System.currentTimeMillis();

        try {
            //创建应用上下文
            if (this.context == null) {
            //创建应用上下文的时候,会调用父类ContextLoader的方法,
            //该方法会读取ContextLoader.properties,从中取出ApplicationContext实现类的全类名
            //然后通过反射创建实例
                this.context = createWebApplicationContext(servletContext);
            }
            if (this.context instanceof ConfigurableWebApplicationContext) {
                ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
                if (!cwac.isActive()) {
                    // The context has not yet been refreshed -> provide services such as
                    // setting the parent context, setting the application context id, etc
                    if (cwac.getParent() == null) {
                        // The context instance was injected without an explicit parent ->
                        // determine parent for root web application context, if any.
                        ApplicationContext parent = loadParentContext(servletContext);
                        cwac.setParent(parent);
                    }
                    //初始化ApplicationContext
                    configureAndRefreshWebApplicationContext(cwac, servletContext);
                }
            }
            servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);

            ClassLoader ccl = Thread.currentThread().getContextClassLoader();
            
            //。。。。。省略一些代码
            return this.context;
        }
        catch (RuntimeException | Error ex) {
            logger.error("Context initialization failed", ex);
            servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
            throw ex;
        }
    }

1.ioc容器创建

/**
     * 返回WebApplicationContext的实现类,否则返回默认XmlWebApplicationContext或者通过开发者自定义.
     * @param servletContext 当前servlet上下文
     * @return  WebApplicationContext的实现类
     */
    protected Class<?> determineContextClass(ServletContext servletContext) {
        String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);//"contextClass"
        if (contextClassName != null) {
            try {
                return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
            }
            catch (ClassNotFoundException ex) {
                throw new ApplicationContextException(
                        "Failed to load custom context class [" + contextClassName + "]", ex);
            }
        }
        else { // 否则,读取ContextLoader.properties中配置的key=WebApplicationContetxt.class.getName()的value,这里返回XmlWebApplicationContext.class.getName()
            contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
            try {
                return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
            }
            catch (ClassNotFoundException ex) {
                throw new ApplicationContextException(
                        "Failed to load default context class [" + contextClassName + "]", ex);
            }
        }
    }
///ContextLoader.properties中的内容
org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext
  1. ioc容器初始化
//configureAndRefreshWebApplicationContext
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
   if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
      String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
      if (idParam != null) {
         wac.setId(idParam);
      }
      else {  
         wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
               ObjectUtils.getDisplayString(sc.getContextPath()));
      }
   }

   wac.setServletContext(sc);
   String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
   if (configLocationParam != null) {
      //从servletContext中得到web.xml中的<context-param>标签,得到name为contextConfigLocation
      //value为applicationContext.xml的文件,存到WebApplicationContext中
      wac.setConfigLocation(configLocationParam);
   }

   
   ConfigurableEnvironment env = wac.getEnvironment();
   if (env instanceof ConfigurableWebEnvironment) {
      ((ConfigurableWebEnvironment) env).initPropertySources(sc, null);
   }

   customizeContext(sc, wac);
   //调用父类AbstractApplicationContext的refresh()方法,进行spring ioc容器的初始化。
   wac.refresh();
}

这样就完成了Tomcat和spring的连接。

2.3.4 filterStart()

启动过滤器,过滤一些用户不合法的请求等等。

2.3.5 loadOnStartup()

我们在web.xml中配置了DispatcherServlet,所以此处至少会初始化并启动DispatcherServlet。

loadOnStartup()会从StandardContext的child容器中取出解析web.xml所获得的所有servlet,将其封装为Wrapper对象,并判断load-on-startup属性是否大于0,大于0则加载,加载会调用load()方法。

@Override
    public synchronized void load() throws ServletException {
        //加载servlet类,并返回一个实例
        instance = loadServlet();

        if (!instanceInitialized) {
        //进行初始化
            initServlet(instance);
        }

        if (isJspServlet) {
            StringBuilder oname = new StringBuilder(getDomain());

            oname.append(":type=JspMonitor");

            oname.append(getWebModuleKeyProperties());

            oname.append(",name=");
            oname.append(getName());

            oname.append(getJ2EEKeyProperties());

            try {
                jspMonitorON = new ObjectName(oname.toString());
                Registry.getRegistry(null, null).registerComponent(instance, jspMonitorON, null);
            } catch (Exception ex) {
                log.warn("Error registering JSP monitoring with jmx " + instance);
            }
        }
    }

由于DispatcherServlet继承了FrameworkServlet,FrameworkServlet又继承了HttpServletBean。

所以DispatcherServlet执行init()方法时,调用的是HttpServletBean的init()方法。

HttpServletBean的init()创建一个BeanWrapper(),并进行了初始化,然后调用initServletBean()方法,该方法是个空方法,由FrameworkServlet实现。

FrameworkServlet的initServletBean()的核心方法为:

  • initWebApplicationContext(),创建Spring mvc容器,即spring ioc容器的子容器,然后重写onRefresh()方法,本类中onRefresh()方法是空的,由DispatcherServlet实现。
  • initFrameworkServlet()
    其中initFrameworkServlet()为空方法,所以核心逻辑为initWebApplicationContext()。
    protected WebApplicationContext initWebApplicationContext() {
    // 1)获取由ContextLoaderListener创建的根IoC容器
    // 2)获取根IoC容器有两种方法,还可通过key直接获取
        WebApplicationContext rootContext =
                WebApplicationContextUtils.getWebApplicationContext(getServletContext());
        WebApplicationContext wac = null;

        if (this.webApplicationContext != null) {
            // A context instance was injected at construction time -> use it
            wac = this.webApplicationContext;
            if (wac instanceof ConfigurableWebApplicationContext) {
                ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
                if (!cwac.isActive()) {
                    // The context has not yet been refreshed -> provide services such as
                    // setting the parent context, setting the application context id, etc
                    if (cwac.getParent() == null) { 
            // 如果当前Servelt存在一个WebApplicationContext即子IoC容器,
            // 并且上文获取的根IoC容器存在,则将根IoC容器作为子IoC容器的父容器
                        // The context instance was injected without an explicit parent -> set
                        // the root application context (if any; may be null) as the parent
                        cwac.setParent(rootContext);
                    }
                    configureAndRefreshWebApplicationContext(cwac);
                }
            }
        }
        if (wac == null) {
      // 如果当前Servlet不存在一个子IoC容器则去查找一下
            // No context instance was injected at construction time -> see if one
            // has been registered in the servlet context. If one exists, it is assumed
            // that the parent context (if any) has already been set and that the
            // user has performed any initialization such as setting the context id
            wac = findWebApplicationContext();
        }
        if (wac == null) {
      // 如果仍旧没有查找到子IoC容器则创建一个子IoC容器
            // No context instance is defined for this servlet -> create a local one
            wac = createWebApplicationContext(rootContext);
        }

        if (!this.refreshEventReceived) {
      // 调用子类DispatcherServlet覆盖的onRefresh方法完成“可变”的初始化过程
            // Either the context is not a ConfigurableApplicationContext with refresh
            // support or the context injected at construction time had already been
            // refreshed -> trigger initial onRefresh manually here.
            synchronized (this.onRefreshMonitor) {
                onRefresh(wac);
            }
        }

        if (this.publishContext) {
            // Publish the context as a servlet context attribute.
            String attrName = getServletContextAttributeName();
            getServletContext().setAttribute(attrName, wac);
        }

        return wac;
    }
  
    /**
     * Template method which can be overridden to add servlet-specific refresh work.
     * Called after successful context refresh.
     * <p>This implementation is empty.
     * @param context the current WebApplicationContext
     * @see #refresh()
     */
    protected void onRefresh(ApplicationContext context) {
        // For subclasses: do nothing by default.
    }
@SuppressWarnings("serial")
public class DispatcherServlet extends FrameworkServlet {
  ...
    /**
     * This implementation calls {@link #initStrategies}.
     */
    @Override
    protected void onRefresh(ApplicationContext context) {
        initStrategies(context);
    }

    /**
     * Initialize the strategy objects that this servlet uses.
     * <p>May be overridden in subclasses in order to initialize further strategy objects.
     */
    protected void initStrategies(ApplicationContext context) {
        initMultipartResolver(context);
        initLocaleResolver(context);
        initThemeResolver(context);
        initHandlerMappings(context);
        initHandlerAdapters(context);
        initHandlerExceptionResolvers(context);
        initRequestToViewNameTranslator(context);
        initViewResolvers(context);
        initFlashMapManager(context);
    }
    ...
}

/**
onRefresh()方法直接调用了initStrategies()方法.
该方法用于初始化创建multipartResovle来支持图片等文件的上传、
本地化解析器、主题解析器、HandlerMapping处理器映射器、H
andlerAdapter处理器适配器、异常解析器、视图解析器、flashMap管理器等,
这些组件都是SpringMVC开发中的重要组件,相关组件的初始化创建过程均在此完成。
/

2.4 总结

2.4.1 启动流程
  1. 编写配置文件,web.xml,spring相关配置,spring mvc相关配置等。

  2. 启动tomcat,从bootstrap的main方法开始,一级一级的往下调用startIniternal()方法。

  3. 当调用到context这一层时,调用context的startIniternal()方法时,会加载web.xml文件,正式开始我们的项目的加载。

    3.1 :通过发布configure_start事件,由ContextConfig类监听,然后读取ContextLoader.properties文件,取出ApplicationContext实现类的全类名,通过反射创建ApplicationContext实例。

    此过程会解析web.xml,然后将context-param、servlet、fliter、listener等标签的内容放入到ServletContext对应的成员变量中,配置最少含有ContextLoadListener和DispatcherServlet,分别用于ioc容器创建和springmvc子容器的创建。

    3.2: 启动所有监听器,此处至少含有一个ContextLoadListener,所以会调用其contextInitialized(ServletEvent event)方法。

    通过执行createWebApplicationContext(servletContext)方法完成ioc容器的创建。

    通过configureAndRefreshWebApplicationContext(cwac, servletContext)完成ioc容器初始化,在该方法内会执行AbstractApplicationContext的refresh()方法。

    3.3 启动所有的fliter,过滤一些不合法的请求等等

    3.4 启动所有在web.xml中配置的servlet,调用其load()方法,此处至少含有DispatcherServlet。

    调用initWebApplicationContext()方法,创建spring mvc容器,并且DispatcherServlet重写了onRefresh()方法,所以创建完spring mvc容器,进行初始化,调用AbstractApplicationContext的refresh()方法的时候会调用DispatcherServlet重写的onRefresh()方法,进行mvc的一些组件的初始化。(例如 handlerMapping、Handler等等)

  4. 项目启动完成,提供服务。

ioc容器和spring mvc容器的关系:
在这里插入图片描述
tomcat的启动流程图:
在这里插入图片描述

2.4.2 服务流程

当我们发送请求:http://localhost:8080/articles/67/comment

  1. 请求发送到服务器,服务器将请求分发到8080端口,客户端与监听8080端口的socket建立连接,Tomcat负责处理http请求的Connector 从socket 中取出数据封装为 ServletRequest ,交给Engine(即Container)处理。
  2. Engine获取到request,根据Host匹配自己所含有的所有虚拟站点,匹配到名为localhost的站点,将请求交给host
  3. host匹配context路径,匹配不到交给主应用去处理(即webapp下的root目录),匹配到/articles这个context后,交给对应context去处理。
  4. context处理实际上是交给DispatcherServlet处理 (即DispatcherServlet的service()方法,该方法来自其父类FrameworkServlet,其又会先调用super.service())。所以,DispatcherServlet处理请求时,先调用HttpServletBean的service()方法,将ServletRequest转换为HttpServletRequest,然后Framework的service()又调用了processRequest(request, response),根据request的请求方法的不同,去调用对应的doGet、doPost、doPut等方法。
  5. Framework的doGet、doPost等方法再调用doSerivce()方法,该方法由DispatcherServlet重写
  6. 然后检查是否是上传请求。
  7. 然后查找所有的HandlerMapping,查找与/{articlesId}/comment对应的handler,找出对应的Handler处理链(HandlerExecutionChain对象),返回给DispatcherServlet
  8. DispatcherServlet得到handler链,然后将request和handler链交给HandlerAdapter去处理。
  9. HandlerAdapter执行链中每一个Handler对应的方法,得到结果并返回(ModelAndView对象)
    10.DisPatcherServlet接收到结果后,交给ViewResolver去解析ModelAndView,返回给DispatcherServlet解析出的View对象,然后进行渲染,返回给客户端。

tomcat处理请求:
在这里插入图片描述
spring mvc处理请求:
在这里插入图片描述

参考:

StandardContext启动详解
《spring源码深度解析》
《看透spring mvc》
https://www.cnblogs.com/yy3b2007com/p/12219805.html#autoid-2-0-0

  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要在TomcatSpring MVC中将照片上传到本地,你可以按照以下步骤进行操作: 1. 配置文件上传的相关依赖: 在你的项目中添加Apache Commons FileUpload和Apache Commons IO的依赖。你可以使用Maven或Gradle来管理依赖。 2. 创建文件上传表单: 在你的前端页面中创建一个表单,用于选择要上传的照片文件。 ```html <form method="POST" action="/upload" enctype="multipart/form-data"> <input type="file" name="photo" /> <input type="submit" value="Upload" /> </form> ``` 3. 创建Controller处理上传请求: 创建一个Spring MVC的Controller类,用于处理上传请求,并保存照片到本地。 ```java import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.multipart.MultipartFile; import org.springframework.web.bind.annotation.RestController; @RestController public class UploadController { @PostMapping("/upload") public String handleFileUpload(@RequestParam("photo") MultipartFile file) { if (!file.isEmpty()) { try { byte[] bytes = file.getBytes(); // 将照片保存到本地文件系统 // 这里可以根据你的需求,指定保存的路径和文件名 // 例如:Files.write(bytes, new File("D:/uploads/photo.jpg")); return "File uploaded successfully!"; } catch (IOException e) { e.printStackTrace(); return "Error uploading file!"; } } else { return "No file selected!"; } } } ``` 在上述代码中,`handleFileUpload`方法会接收名为"photo"的文件参数,并将其保存到本地文件系统。你可以根据需要进行修改。 4. 配置文件上传的临时目录: 在Tomcat的配置文件(如`server.xml`)中,你可以配置一个临时目录用于存储上传的文件。找到一个可写的目录,并添加以下配置: ```xml <Context docBase="your_app_path" path="/" reloadable="true"> <WatchedResource>WEB-INF/web.xml</WatchedResource> <Resources allowLinking="true" cachingAllowed="false" className="org.apache.naming.resources.FileDirContext" /> <Parameter name="org.apache.tomcat.util.http.fileupload.DefaultFileItemFactory.repository" value="/path/to/temp/dir" /> </Context> ``` 将上述代码中的`your_app_path`替换为你的应用程序路径,将`/path/to/temp/dir`替换为你想要的临时目录路径。 5. 部署并启动Tomcat服务器: 将你的应用程序打包成WAR文件,并将其部署到Tomcat服务器中。启动Tomcat服务器。 现在,当你选择一个照片文件并提交表单时,该文件会被上传到服务器,并保存到你指定的本地目录中。请根据你的实际需求修改代码中的路径和文件名。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值