springmvc详细流程解析

本文详细解析了SpringMVC的工作流程,从用户请求到DispatcherServlet,再到处理器映射器、处理器适配器、具体处理器的执行,以及视图解析器返回视图和最终的响应用户。同时,文章介绍了SpringMVC的众多优势,如清晰的角色划分、可定制的配置和映射、可重用的业务代码、灵活的模型转换等。最后,展示了如何手写一个简单的SpringMVC框架。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

springmvc是Spring框架中的一个模块,是一个基于mvc模式的web模块,负责前后台的交互工作;所谓的mvc其实就是一种设计模式,从而实现系统的解耦。

SpringMVC执行流程 

       

01、用户发送出请求到前端控制器DispatcherServlet。

02、DispatcherServlet收到请求调用HandlerMapping(处理器映射器)

03、HandlerMapping找到具体的处理器(可查找xml配置或注解配置),生成处理器对象及处理器拦截器(如果有),再一起返回给DispatcherServlet。

04、DispatcherServlet调用HandlerAdapter(处理器适配器)。

05、HandlerAdapter经过适配调用具体的处理器(Handler/Controller)。

06、Controller执行完成返回ModelAndView对象。

07、HandlerAdapter将Controller执行结果ModelAndView返回给DispatcherServlet。

08、DispatcherServlet将ModelAndView传给ViewReslover(视图解析器)。

09、ViewReslover解析后返回具体View(视图)。

10、DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。

11、DispatcherServlet响应用户。

SpringMVC的特点优势

1.清晰的角色划分:控制器(controller)、验证器(validator)、命令对象(command obect)、表单对象(form object)、模型对象(model object)、Servlet分发器(DispatcherServlet)、处理器映射(handler mapping)、试图解析器(view resoler)等等。每一个角色都可以由一个专门的对象来实现。

2.强大而直接的配置方式:将框架类和应用程序类都能作为JavaBean配置,支持跨多个context的引用,例如,在web控制器中对业务对象和验证器validator)的引用。

3.可适配、非侵入:可以根据不同的应用场景,选择何事的控制器子类(simple型、command型、from型、wizard型、multi-action型或者自定义),而不是一个单一控制器(比如Action/Actio

nForm)继承。

4.可重用的业务代码:可以使用现有的业务对象作为命令或表单对象,而不需要去扩展某个特定框架的基类。

5.可定制的绑定(binding)和验证(validation):比如将类型不匹配作为应用级的验证错误,这可以保证错误的值。再比如本地化的日期和数字绑定等等。在其他某些框架中,你只能使用字符串表单对象,需要手动解析它并转换到业务对象。

6.可定制的handler mapping和view resolution:Spring提供从最简单的URL映射,到复杂的、专用的定制策略。与某些web MVC框架强制开发人员使用单一特定技术相比,Spring显得更加灵活。

7.灵活的model转换:在Springweb框架中,使用基于Map的键/值对来达到轻易的与各种视图技术集成。

8.可定制的本地化和主题(theme)解析:支持在JSP中可选择地使用Spring标签库、支持JSTL、支持Velocity(不需要额外的中间层)等等。

9.简单而强大的JSP标签库(Spring Tag Library):支持包括诸如数据绑定和主题(theme)之类的许多功能。他提供在标记方面的最大灵活性。

10.JSP表单标签库:在Spring2.0中引入的表单标签库,使用在JSP编写表单更加容易。

11.Spring Bean的生命周期:可以被限制在当前的HTTp Request或者HTTp Session。准确的说,这并非Spring MVC框架本身特性,而应归属于Spring MVC使用的WebApplicationContext容器。

手写一个简单的SpringMVC框架

 1、新建一个maven项目,工程目录如下

pom文件

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.xuwei</groupId>
  <artifactId>xw-springmvc</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>war</packaging>

  <name>xw-springmvc Maven Webapp</name>
  <!-- FIXME change it to the project's website -->
  <url>http://www.example.com</url>

  <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>
  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>

    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>servlet-api</artifactId>
      <version>2.4</version>
    </dependency>
  </dependencies>

  <build>
    <finalName>xw-springmvc</finalName>
    <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
      <plugins>
        <plugin>
          <artifactId>maven-clean-plugin</artifactId>
          <version>3.1.0</version>
        </plugin>
        <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
        <plugin>
          <artifactId>maven-resources-plugin</artifactId>
          <version>3.0.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>3.8.0</version>
        </plugin>
        <plugin>
          <artifactId>maven-surefire-plugin</artifactId>
          <version>2.22.1</version>
        </plugin>
        <plugin>
          <artifactId>maven-war-plugin</artifactId>
          <version>3.2.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-install-plugin</artifactId>
          <version>2.5.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-deploy-plugin</artifactId>
          <version>2.8.2</version>
        </plugin>
      </plugins>
    </pluginManagement>
  </build>
</project>

 2、在WEB-INF目录下修改web.xml文件,resources目录下新建application.properties

<!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>
  <servlet>
    <servlet-name>myspringmvc</servlet-name>
    <servlet-class>com.xuwei.servlet.MyDispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>application.properties</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>

  <servlet-mapping>
    <servlet-name>myspringmvc</servlet-name>
    <url-pattern>/*</url-pattern>
  </servlet-mapping>
</web-app>

3、设置自己相关注解

package com.xuwei.annotaion;

import java.lang.annotation.*;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyRestController {
    String value() default "";
}


package com.xuwei.annotaion;

import java.lang.annotation.*;

@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyRequestMapping {

    String value() default "";
}

package com.xuwei.annotaion;

import java.lang.annotation.*;

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyRequestParam {
    String value();
}

 4、手写自己的DispatcherServlet

package com.xuwei.servlet;

import com.xuwei.annotaion.MyRequestMapping;
import com.xuwei.annotaion.MyRestController;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;

/**
 * @program: xwspringmvc
 * @author: xuWei
 * @create: 2019/12/22
 * @description:
 */
public class MyDispatcherServlet extends HttpServlet {

    private Properties properties = new Properties();
    private List<String>classNames=new ArrayList<>();
    private Map<String ,Object>ioc=new HashMap<>();
    private Map<String, Method>handlerMapping = new HashMap<>();
    private Map<String ,Object>controllerMap=new HashMap<>();


    @Override
    public void init(ServletConfig config) throws ServletException {
        //1、加载配置文件
        doLoadConfig(config.getInitParameter("contextConfigLocation"));

        //2、初始化所有相关联的累,扫描用户设定的包下面所有的类
        doScanner(properties.getProperty("scanPackage"));

        //3、通过扫描到的累,通过反射机制实例化,并放到ioc容器之中
        doInstance();

        //4、初始化HandlerMapping(将url和Method对应)
        initHandlerMapping();
    }


    @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 {
        try {
            doDispatch(req,resp);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    private void doDispatch(HttpServletRequest req, HttpServletResponse resp)throws Exception{
        if(handlerMapping.isEmpty()){
            return;
        }
        String url=req.getRequestURI();
        String contextPath=req.getContextPath();
        url=url.replace(contextPath,"").replaceAll("/+","/");
        if(!this.handlerMapping.containsKey(url)){
            resp.getWriter().write("404 NOT FOUND!");
            return;
        }
        Method method = this.handlerMapping.get(url);

        Class<?>[] parameterTypes = method.getParameterTypes();

        //保存参数值
        Object[]paramValues =  new Object[parameterTypes.length];

        //获取请求的参数
        Map<String,String[]> parameterMap = req.getParameterMap();
        for(int i=0;i<parameterTypes.length;i++){
            String requestParam = parameterTypes[i].getSimpleName();
            if(requestParam.equals("HttpServletRequest")){
                paramValues[i]=req;
                continue;
            }
            if(requestParam.equals("HttpServletResponse")){
                paramValues[i]=resp;
                continue;
            }
            if(requestParam.equals("String")){
                for(Map.Entry<String,String[]> param:parameterMap.entrySet()){
                    String value=Arrays.toString(param.getValue()).replaceAll("\\[|\\]","").replaceAll(",\\s","");
                    paramValues[i]=value;
                }
            }
        }
        try {
            method.invoke(this.controllerMap.get(url),paramValues);
        }catch (Exception e){
            e.printStackTrace();
        }


    }

    private void doLoadConfig(String location) {
        InputStream resourceAsStream =
                this.getClass().getClassLoader().getResourceAsStream(location);
        try {
            properties.load(resourceAsStream);
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            if(null != resourceAsStream){
                try {
                    resourceAsStream.close();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        }
    }

    private void doScanner(String scanPackage) {
        URL url = this.getClass().getClassLoader().getResource("/" + scanPackage.replaceAll("\\.", "/"));
        File dir=new File(url.getFile());
        for(File file:dir.listFiles()){
            if(file.isDirectory()){
                doScanner(scanPackage+"."+file.getName());
            }else {
                String className = scanPackage + "." + file.getName().replace(".class", "");
                classNames.add(className);
            }
        }
    }

    private void doInstance() {
        if(classNames.isEmpty()){
            return;
        }
        for(String className:classNames){
            try {
                Class<?> clazz = Class.forName(className);
                if(clazz.isAnnotationPresent(MyRestController.class)){
                    ioc.put(toLowerFirstWord(clazz.getSimpleName()),clazz.newInstance());
                }else {
                    continue;
                }
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }


    private void initHandlerMapping() {
        if(ioc.isEmpty()){
            return;
        }
        try {
            for(Map.Entry<String,Object>entry:ioc.entrySet()){
                Class<?> clazz = entry.getValue().getClass();
                if(!clazz.isAnnotationPresent(MyRestController.class)){
                    continue;
                }
                String baseUrl="";
                if(clazz.isAnnotationPresent(MyRequestMapping.class)){
                    MyRequestMapping annotation = clazz.getAnnotation(MyRequestMapping.class);
                    baseUrl=annotation.value();
                }
                Method[] methods = clazz.getMethods();
                for(Method method:methods){
                    if(!method.isAnnotationPresent(MyRequestMapping.class)){
                        continue;
                    }
                    MyRequestMapping annotation = method.getAnnotation(MyRequestMapping.class);
                    String url=annotation.value();
                    url=(baseUrl+"/"+url).replaceAll("/+","/");
                    handlerMapping.put(url,method);
                    controllerMap.put(url,clazz.newInstance());
                    System.out.println(url+"----"+method);
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    private String toLowerFirstWord(String simpleName) {
        char[] chars = simpleName.toCharArray();
        chars[0]+=32;
        return String.valueOf(chars);
    }

}

5、测试类

package com.xuwei.test;

import com.xuwei.annotaion.MyRequestMapping;
import com.xuwei.annotaion.MyRequestParam;
import com.xuwei.annotaion.MyRestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @program: xwspringmvc
 * @author: xuWei
 * @create: 2019/12/22
 * @description:
 */
@MyRestController
@MyRequestMapping("/test")
public class UserController {

    @MyRequestMapping("test1")
    public void test1(HttpServletRequest request, HttpServletResponse response, @MyRequestParam("param")String param){
        System.out.println(param);
        try {
            response.getWriter().write("doTest1 method success");
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    @MyRequestMapping("test2")
    public void test2(HttpServletRequest request, HttpServletResponse response, @MyRequestParam("param")String param){
        System.out.println(param);
        try {
            response.getWriter().println("doTest2 method success");
        }catch (Exception e){
            e.printStackTrace();
        }
    }

}

运行项目,开始进行请求

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值