Servlet和SpringMVC

本文深入介绍了Servlet和SpringMVC的概览、工作流程、搭建步骤以及执行过程。Servlet是JavaWeb的基础,用于处理HTTP请求,而SpringMVC作为Spring框架的一部分,提供了更高级的MVC架构,简化了Web应用的开发。文章详细阐述了两者从创建项目、配置文件、请求处理到视图解析的全过程,适合初学者和进阶开发者参考。

目录

Servlet概述

Servlet流程

搭建过程

初始化过程

执行过程

Servlet--细节

SpringMvc概述

SpringMvc流程

搭建过程

核心组件类

执行流程

Servlet与SpringMvc


Servlet概述

Java Servlet 是运行在 Web 服务器或应用服务器上的程序,它是作为来自 Web 浏览器或其他 HTTP 客户端的请求和 HTTP 服务器上的数据库或应用程序之间的中间层。使用 Servlet,您可以收集来自网页表单的用户输入,呈现来自数据库或者其他源的记录,还可以动态创建网页。

Servlet流程

搭建过程

  1. 创建普通web项目

  2. maven引入servlet相关jar包

    <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>4.0.1</version>
    </dependency>
  3. 新建java源文件XXXServlet继承自HttpServlet,并重写其中的(doGet && doPost) || service 方法

    返回视图:

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req, resp);
    }
    ​
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setAttribute("username","aaaaaa");
        req.setAttribute("password","bbbbbb");
        req.getRequestDispatcher("/index.jsp").forward(req, resp);
    }

    返回数据:

    public class JsonServlet extends HttpServlet {
    ​
        @Override
        protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            PrintWriter printWriter = resp.getWriter();
            Map<String,String> jsonData = new HashMap<>();
            jsonData.put("username","service");
            jsonData.put("password","service");
            
            printWriter.println(jsonData);
        }
    }

  4. web.xml中注册Servlet

    <servlet>
      <servlet-name>helloServlet</servlet-name>
      <servlet-class>com.fengwuj.controller.HelloServlet</servlet-class>
    </servlet>
    <servlet-mapping>
      <servlet-name>helloServlet</servlet-name>
      <url-pattern>/helloServlet</url-pattern>
    </servlet-mapping>
    ​
    ​
    <servlet>
      <servlet-name>jsonServlet</servlet-name>
      <servlet-class>com.fengwuj.controller.JsonServlet</servlet-class>
    </servlet>
    <servlet-mapping>
      <servlet-name>jsonServlet</servlet-name>
      <url-pattern>/jsonServlet</url-pattern>
    </servlet-mapping>  

初始化过程

  1. 在启动Web项目时,容器(比如Tomcat)会读web.xml配置文件中的两个节点<listener>和<contex-param>。

  2. 接着容器会创建一个ServletContext(上下文),应用范围内即整个WEB项目都能使用这个上下文。

  3. 接着容器会将读取到<context-param>转化为键值对,并交给ServletContext。

  4. 容器创建<listener></listener>中的类实例,即创建监听(备注:listener定义的类可以是自定义的类但必须需要继承ServletContextListener)。

  5. 在监听的类中会有一个contextInitialized(ServletContextEvent event)初始化方法,在这个方法中可以通过event.getServletContext().getInitParameter("contextConfigLocation") 来得到context-param 设定的值。在这个类中还必须有一个contextDestroyed(ServletContextEvent event) 销毁方法.用于关闭应用前释放资源,比如说数据库连接的关闭。

  6. 得到这个context-param的值之后,你就可以做一些操作了.注意,这个时候你的WEB项目还没有完全启动完成.这个动作会比所有的Servlet都要早。

执行过程

img

Servlet--细节

Servlet解析详述:JavaWeb——Servlet(全网最详细教程包括Servlet源码分析)_扬俊的小屋-CSDN博客_servlet

接口请求-->Tomcat-->servlet的解析详述Tomcat 9 源码解析 -- 请求如何经过tomcat到达servlet_gaohaicheng123的博客-CSDN博客

SpringMvc概述

SpringMVC,基于MVC架构,提供了模型-视图-控制的体系结构和灵活、松散耦合的 web 应用程序的组件;对Servlet进行封装、整合、管理;作为Spring框架的一部分,能 够 使 用 Spring 的 IoC 和 Aop 方 便 整 合 Strtus,MyBatis,Hiberate,JPA 等其他框架;强化了注解使用,方便灵活

SpringMvc流程

搭建过程

  • 创建javaWeb项目

  • pom文件引入spring包

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.7</maven.compiler.source>
        <maven.compiler.target>1.7</maven.compiler.target>
        <spring.version>5.0.8.RELEASE</spring.version>
    </properties>
    ​
    <dependencies>
      <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.11</version>
        <scope>test</scope>
      </dependency>
    ​
      <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>${spring.version}</version>
      </dependency>
    ​
      <!-- pring IOC的基础实现,包含访问配置文件、创建和管理bean等 -->
      <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
        <version>${spring.version}</version>
      </dependency>
    ​
      <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${spring.version}</version>
      </dependency>
    ​
      <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context-support</artifactId>
        <version>${spring.version}</version>
      </dependency>
    ​
      <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-web</artifactId>
        <version>${spring.version}</version>
      </dependency>
    ​
      <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>${spring.version}</version>
      </dependency>
    ​
      <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-tx</artifactId>
        <version>${spring.version}</version>
      </dependency>
    ​
      <!-- https://mvnrepository.com/artifact/org.springframework/spring-aop -->
      <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aop</artifactId>
        <version>${spring.version}</version>
      </dependency>
    ​
      <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>${spring.version}</version>
      </dependency>
    ​
      <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>${spring.version}</version>
      </dependency>
    ​
      <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-core</artifactId>
        <version>2.9.8</version>
      </dependency>
      <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-annotations</artifactId>
        <version>2.9.8</version>
      </dependency>
      <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.9.8</version>
      </dependency>
    </dependencies>
  • 创建Main-servlet.xml文件,用于初始化springmvc的启动配置信息

    <?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-4.0.xsd
            http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">
    ​
        <!-- 配置自动扫描的包 -->
        <context:component-scan base-package="com.fengwuj.controller"></context:component-scan>
    ​
        <!-- 转换器-->
        <mvc:annotation-driven>
            <mvc:message-converters>
                <bean class="org.springframework.http.converter.StringHttpMessageConverter"/>
                <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"/>
            </mvc:message-converters>
        </mvc:annotation-driven>
    ​
        <!-- 配置视图解析器 如何把handler 方法返回值解析为实际的物理视图 -->
        <!--prefix和suffix:查找视图页面的前缀和后缀(前缀[逻辑视图名]后缀), -->
        <!-- 比如传进来的逻辑视图名为result,则该jsp视图页面应该存放在“/WEB-INF/result.jsp”  -->
        <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
            <property name = "prefix" value="/WEB-INF/"></property>
            <property name = "suffix" value = ".jsp"></property>
        </bean>
    </beans>
  • web.xml注册DispatcherServlet,引入springmvc.xml

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >
​
<web-app>
  <display-name>Archetype Created Web Application</display-name>
​
​
  <!--
      /WEB-INF/springmvc-servlet.xml
      /WEB-INF/myweb-servlet.xml
​
      DispatcherServlet在创建对象的时候,在init()方法中会创建springmvc的容器对象
      WebApplicationContext, 创建容器的时候会读取配置文件, 创建配置文件中的所有bean对象。
      默认读取的配置文件的位置是 /WEB-INF ,默认的文件名称是 <servlet-name>-servlet.xml
   -->
  <!-- 配置DispatchcerServlet -->
  <servlet>
    <servlet-name>Main</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:Main-servlet.xml</param-value>
    </init-param>
    <!-- load-on-startup:表示启动容器时初始化该Servlet; -->
    <load-on-startup>1</load-on-startup>
  </servlet>
​
  <servlet-mapping>
    <servlet-name>Main</servlet-name>
    <!-- url-pattern:表示哪些请求交给Spring Web MVC处理, “/” 是用来定义默认servlet映射的。 -->
    <!-- 也可以如“*.html”表示拦截所有以html为扩展名的请求。 -->
    <url-pattern>/</url-pattern>
  </servlet-mapping>
​
​
  <!-- 至此请求已交给Spring Web MVC框架处理,因此我们需要配置Spring的配置文件, -->
  <!-- 默认DispatcherServlet会加载WEB-INF/[DispatcherServlet的Servlet名字,也就是上面的Main]-servlet.xml配置文件。 -->
  <!-- 即Main-servlet.xml -->
</web-app>

核心组件类

①:DispatcherServlet是SpringMVC中的前端控制器(front controller),负责接收request并将request转发给对应的处理组件。

②:HanlerMapping是SpringMVC中完成url到Controller映射的组件。DispatcherServlet接收request,然后从HandlerMapping查找处理request的controller.

③:Controller处理request,并返回ModelAndView对象,Controller是SpringMVC中负责处理request的组件(类似于struts2中的Action),ModelAndView是封装结果视图的组件。

④ ⑤ ⑥:视图解析器解析ModelAndView对象并返回对应的视图给客户端。

执行流程

  1. 项目启动,扫描Controller,RequestMapping注解,绑定路由和方法,进行注册

    RequestMappingHandlerMapping-->afterPropertiesSet()-->super.afterPropertiesSet()

    AbstractHandlerMethodMapping-->initHandlerMethods()

    //isHandler:判断当前bean是否有Controller注解或者RequestMapping注解
    //detectHandlerMethods:注册ur-method的Mapping关系,用于后期路由匹配分发
    if (beanType != null && isHandler(beanType)) {
        detectHandlerMethods(beanName);
    }
    ​
    //detectHandlerMethods中
    //key:方法,value:RequestMappingInfo对象,patternsCondition存储url,
    //url格式 [/return-jsp || /test/{id}/test || /test-all/*]
    Map<Method, T> methods = MethodIntrospector.selectMethods();
        
    //方法注册
    //handler:Controller对象
    //invocableMethod:反射得到的方法,通过了一系列校验
    //mapping:RequestMappingInfo对象
    registerHandlerMethod(handler, invocableMethod, mapping);
    ​
    //主要注册到mappingRegistry中,后续DispatcherServlet会从这里取
    this.mappingRegistry.register(mapping, handler, method);
    ​
    //mappingLookup:key-->RequestMappingInfo对象,value-->HandlerMethod,spring封装的对象,记录了方法、对象等信息
    //urlLookup:key-->精确的url,不包含*或者?,value-->RequestMappingInfo对象
    //nameLookup:key-->方法的自定义规则限定名,TC#test(TestController中test方法),value-->HandlerMethod对象
    //corsLookup:key-->HandlerMethod对象,value-->CorsConfiguration对象,存储CrossOrigin注解相关跨域配置
    //registry:key-->RequestMappingInfo对象,value-->MappingRegistration对象,包含RequestMappingInfo,method,url,方法的自定义规则限定名
  2. 请求过程

    DispatcherServlet拦截了所有请求,所有请求进入DispatcherServlet的doService方法

    doService-->doDispatch(request, response)进行具体的请求处理

    2.1 匹配Url和Method

    // Determine handler for the current request.
    //匹配url和方法
    mappedHandler = getHandler(processedRequest);
    //getHandler------>追踪
    //handlerMappings--->RequestMappingHandlerMapping,BeanNameTypeMapping.....
    for (HandlerMapping hm : this.handlerMappings) {
        if (logger.isTraceEnabled()) {
            logger.trace("Testing handler map [" + hm + "] in DispatcherServlet with name '" +          getServletName() + "'");
        }
        
        HandlerExecutionChain handler = hm.getHandler(request);
        if (handler != null) {
            return handler;
        }
    }
    //hm.getHandler---->getHandlerInternal(request)--->AbstractHandlerMethodMapping#getHandlerInternal
    //lookupPath:url,去除掉部署指定的前缀/springmvcdemo/prexxx/return-jsp------------>/prexxx/return-jsp
    String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
    ​
    //handlerMethod:找到url匹配到的方法
    HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
    //lookupHandlerMethod------>this.mappingRegistry.getMappingsByUrl(lookupPath)--------------->>urlLookup
    //通过urlLookup(key:url)  拿到RequestMappingInfo对象
    //通过mappingLookup(key:RequestMappingInfo对象)  拿到  HandlerMethod对象
    //将RM和HM封装为Match对象,添加到matches这个List中
    ​
    //排序,默认第一个为最佳匹配
    //这里是路由命中规则的实现原理,主要是各种情况的考虑及RequestMappingInfo#compareTo方法的重写
    Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
                matches.sort(comparator);
    ​
    if (matches.size() > 1) {
        //跨域校验
        if (CorsUtils.isPreFlightRequest(request)) {
            return PREFLIGHT_AMBIGUOUS_MATCH;
        }
        
        if((comparator.compare(bestMatch, secondBestMatch) == 0){
            //抛错,多个方法对应同一个url路由
        }
        ......
    }
    ​
    ​
    ​

    2.2 封装HandlerExecutionChain,适配method相应的MappedInterceptor(默认)

    protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
       HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
             (HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
    ​
       String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
       for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
          if (interceptor instanceof MappedInterceptor) {
             MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
             if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
                chain.addInterceptor(mappedInterceptor.getInterceptor());
             }
          }
          else {
             chain.addInterceptor(interceptor);
          }
       }
       return chain;
    }

    2.3 选择Handler适配器,SpringMVC提供RequestMappingHandlerAdapter

    // Determine handler adapter for the current request.
    HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

    2.4 Controller#Method方法调用

    // Actually invoke the handler.
    //得到view
    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
    //handle---->AbstractHandlerMethodAdapter#handleInternal
    //------->RequestMappingHandlerAdapter#handleInternal
    //------->RequestMappingHandlerAdapter#invokeHandlerMethod
    //------->invocableMethod.invokeAndHandle(webRequest, mavContainer)-----准备调用Controller方法
    //------->ServletInvocableHandlerMethod#invokeAndHandle
    //    --->Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
    //      --->getMethodArgumentValues--------获取方法参数,填充
    //        --->getMethodParameters--------获取Controller方法形参,并循环依次填充
    //      ---->doInvoke(args)-----直接反射调用,Method.invoke(obj,args)
    //    --->this.returnValueHandlers.handleReturnValue()----对标Controller#Method封装返回参数
    //      --->HandlerMethodReturnValueHandlerComposite#handleReturnValue
    //          --->1.ViewNameMethodReturnValueHandler:处理视图返回
    //          --->2.RequestResponseBodyMethodProcessor:处理json
    ​
        public boolean supportsReturnType(MethodParameter returnType) {
            return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||
                    returnType.hasMethodAnnotation(ResponseBody.class));
        }
    ​
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Controller
    @ResponseBody
    public @interface RestController {
    ​
        /**
         * The value may indicate a suggestion for a logical component name,
         * to be turned into a Spring bean in case of an autodetected component.
         * @return the suggested component name, if any (or empty String otherwise)
         * @since 4.0.1
         */
        @AliasFor(annotation = Controller.class)
        String value() default "";
    ​
    }
    ​
    //------->RequestMappingHandlerAdapter#getModelAndView
    ​
    ​

Servlet与SpringMvc

总结

SpringMVC基于Servlet进行演化,使用DispatcherServlet拦截所有API请求,自定义注解进行URL匹配,将请求Mapping到相应的方法上进行处理。

其他

SpringMvc 将@RequestMapping注册到HandlerMapping_shanchahua123456的博客-CSDN博客_springmvcrequestmapping的实现

SpringBoot 中DispatcherServlet请求分发流程源码分析_shanchahua123456的博客-CSDN博客_springboot 分发请求

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值