欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

SpringMVC开发实际项目的详细介绍

程序员文章站 2022-04-15 17:56:08
前言过了下SpringMVC,简单总结一下。知识点一. 基本概念1.1 三层架构表现层:即web层,负责接收客户端请求,向客户端返回响应结果。通常客户端通过http协议请求web层,web需要接受http请求并完成http响应。表现层包括展示层和控制层,展示层负责结果的展示(前端),控制层负责接收请求(servlet)。MVC是表现层的常用设计模型,SpringMVC是MVC模型的一个具体的实现框架业务层:即service层,负责业务逻辑处理,与开发需求息息相关,web层依...

前言

过了下SpringMVC,简单总结一下。

参考

B站学习视频:https://www.bilibili.com/video/BV1mE411X7yp

知识点

一. 基本概念

1.1 三层架构

SpringMVC开发实际项目的详细介绍

  • 表现层:即web层,负责接收客户端请求,向客户端返回响应结果。通常客户端通过http协议请求web层,web需要接受http请求并完成http响应。表现层包括展示层和控制层,展示层负责结果的展示(前端),控制层负责接收请求(servlet)。

SpringMVC开发实际项目的详细介绍

MVC是表现层的常用设计模型,SpringMVCMVC模型的一个具体的实现框架

  • 业务层:即service层,负责业务逻辑处理,与开发需求息息相关,web层依赖业务层,但业务层不依赖web层。(业务层在业务处理的时候可能会依赖持久层,如果要对数据持久化需要保证事务一致性,即事务应该放到业务层进行控制
  • 持久层:即dao层,负责数据持久化,包括数据层即数据库和数据访问层。数据库是对数据进行持久化的载体,数据访问层是业务层和持久层交互的接口,业务层需要通过数据访问层将数据持久化到数据库中(即持久层就是与数据库交互,对数据库进行CURD操作的)

1.2 MVC

MVC:Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,是一种用于设计创建web应用程序表现层的模式。

SpringMVC开发实际项目的详细介绍

  • 模型Model:通常指数据模型,作用是封装数据(JavaBean实体类)
  • 视图View:通常指jsp/html,作用是展示数据,视图一般是依据模型数据创建的(前端)
  • 控制器Controller:应用程序中处理用户交互的部分,作用是处理程序逻辑。(controller层)

1.3 SpringMVC

SpringMVC是MVC模式中的一种框架(模式底下有各种框架适用于不同应用场景),适用于Spring框架,支持RESTful编程风格,运行效率快适用于现在大型的电商项目等对业务处理速度要求较高的场景。

SpringMVC是三层架构中的表现层的一个常用框架,负责接受浏览器传过来的请求参数,返回从业务层接收的响应结果给浏览器。包括数据封装对象(domain/entityvo),视图(前端)和控制层(controller。与业务层通过service交互,业务层通过dao和持久层交互。

SpringMVC开发实际项目的详细介绍

1.4 SpringMVC的优势

SpringMVC开发实际项目的详细介绍

SpringMVC开发实际项目的详细介绍

1.5 SpringMVC与Struts2的区别

SpringMVC开发实际项目的详细介绍

ONGL:Object Graph Navigation Language(对象图导航语言),它是一种强大的表达式语言,让你通过简单一致的表达式语法来读取和设置Java对象的属性值,调用对象的方法,遍历整个对象的结构图,实现字段类型转换等功能。

JSTL:Java server pages standarded tag library,即JSP标准标签库。是由JCP(Java community Proces)所制定的标准规范,它主要提供给Java Web开发人员一个标准通用的标签库,并由Apache的Jakarta小组来维护。开发人员可以利用这些标签取代JSP页面上的Java代码,从而提高程序的可读性,降低程序的维护难度。

二. 简单入门

简单的给一个场景,完成SpringMVC框架的作用:将浏览器通过前端传来的请求发送给后台,并将后台传来的响应通过前端传到浏览器上。

2.1 使用IDEA创建一个maven项目,选择原型webapp

SpringMVC开发实际项目的详细介绍SpringMVC开发实际项目的详细介绍

2.2 配置pom.xml

 <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.2.RELEASE</spring.version>
  </properties>

  <dependencies>
     <!-- spring依赖 -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <!-- 使用既定的版本 -->
      <version>${spring.version}</version>
    </dependency>
    <!-- spring web依赖 -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>${spring.version}</version>
    </dependency>
     <!-- spring mvc依赖 -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <!-- servlet依赖 -->
      <dependency>
          <groupId>javax.servlet</groupId>
          <artifactId>servlet-api</artifactId>
        <version>2.5</version>
        <scope>provided</scope>
      </dependency>
    <!-- servlet-jsp依赖 -->
    <dependency>
      <groupId>javax.servlet.jsp</groupId>
      <artifactId>jsp-api</artifactId>
      <version>2.0</version>
      <scope>provided</scope>
    </dependency>
  </dependencies> 

2.3 在web.xml中配置前端控制器dispatcherServlet

SpringMVC开发实际项目的详细介绍

2.4 在resources目录下配置springmvc.xml

SpringMVC开发实际项目的详细介绍

2.5 部署tomcat服务器

SpringMVC开发实际项目的详细介绍

2.6 编写起始页面index.jsp

SpringMVC开发实际项目的详细介绍

2.7 编写controller层

SpringMVC开发实际项目的详细介绍

2.8 配置springmvc.xml

<?xml version="1.0" encoding="UTF-8"?>
<!-- 引用的名称和对象 -->
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/mvc
       http://www.springframework.org/schema/mvc/spring-mvc.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 开启注解扫描 -->
    <context:component-scan base-package="com.practice"></context:component-scan>

    <!-- 视图解析器 -->
    <bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 文件的前缀,一般为路径 -->
        <property name="prefix" value="/WEB-INF/pages/"></property>
        <!-- 文件名的后缀 -->
        <property name="suffix" value=".jsp"></property>
    </bean>

    <!-- 开启springmvc框架注解的支持,替代处理器映射器和处理器适配器 -->
    <mvc:annotation-driven/>
</beans>

2.9 配置web.xml,加载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>

  <!-- 前端控制器 -->
  <servlet>
    <servlet-name>dispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <!-- dispatcherServlet前端控制器加载后,会加载springmvc.xml,然后配置文件中会加载扫描注解创建bean对象
    (如果没有这一步的话,就算有springmvc.xml可以开启注解扫描,但是这个xml本身没有被加载,所以不会生效,即项目中所有的注解不生效 -->
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:springmvc.xml</param-value>
    </init-param>
    <!-- 启动服务器后就会创建dispatcherServlet对象 -->
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>dispatcherServlet</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>
</web-app>

2.10 编写响应页面success.jsp

SpringMVC开发实际项目的详细介绍

2.11 测试,在index.jsp页面中点击超链接,进入success.jsp页面。即点击页面将访问success.jsp的请求通过springmvc框架(dispatcherServlet控制中心)传递到controller层,controller根据链接调用方法(视图解析器对象),跳转success.jsp页面(发送响应给浏览器)

SpringMVC开发实际项目的详细介绍

2.12 使用到组件介绍

  • 前端控制器DispatcherServlet

SpringMVC开发实际项目的详细介绍

  • 处理器映射器HandlerMapping即Controller的注解配置等

SpringMVC开发实际项目的详细介绍

  • 处理器Handler即Controller

SpringMVC开发实际项目的详细介绍

  • 处理器适配器HandlerAdapter(对于不同的请求使用不同的controller)

SpringMVC开发实际项目的详细介绍

  • 视图解析器ViewResovler(配置页面的路径,以供提供响应)

SpringMVC开发实际项目的详细介绍

  • 视图View(前端)

SpringMVC开发实际项目的详细介绍

注:可使用springmvc注解替代处理器适配器和处理器映射器的配置

SpringMVC开发实际项目的详细介绍

三. 请求参数

3.1 @RequestMapping

作用:用于建立请求URL和处理请求方法之间的对应关系(前端发送请求到controller,根据映射关系调用相关的方法)

SpringMVC开发实际项目的详细介绍

从@Target原注解上可以看出@RequestMapping可以加到类上表示一级路径,也可放在方法上表示二级路径。便于URL模块化管理

SpringMVC开发实际项目的详细介绍

优势:模块驱动开发

SpringMVC开发实际项目的详细介绍SpringMVC开发实际项目的详细介绍

属性:

  • Value/path:指定映射路径(前端请求按照这个路径发送,否则会404Not Found)

SpringMVC开发实际项目的详细介绍

  • Method:指定请求方式

SpringMVC开发实际项目的详细介绍SpringMVC开发实际项目的详细介绍SpringMVC开发实际项目的详细介绍

  • Params:指定请求参数(前端发送请求必须携带此处指定的参数,否则会400Bad Request)

SpringMVC开发实际项目的详细介绍SpringMVC开发实际项目的详细介绍

如果@RequestMapping指定params的值,前端必须匹配否则400

SpringMVC开发实际项目的详细介绍SpringMVC开发实际项目的详细介绍

  • Headers:请求头必须包含指定的内容

SpringMVC开发实际项目的详细介绍SpringMVC开发实际项目的详细介绍

3.2 绑定请求参数

SpringMVC开发实际项目的详细介绍SpringMVC开发实际项目的详细介绍

注意:请求参数中如果有中文,会出现乱码问题

解决方法:后端controller中设定编码格式为UTF-8

SpringMVC开发实际项目的详细介绍

但是每个controller中都进行request设置冗余代码过多,SpringMVC中支持使用过滤器解决中文乱码(通过配置即可实现)

SpringMVC开发实际项目的详细介绍

四. 常用注解

4.1 @RequestParam

SpringMVC开发实际项目的详细介绍

属性中value和name含义相同。默认为value/name

@RequestMapping中的value/name值要和前端中的参数name保持一致(这样即使controller中的方法参数名和前端的不一致,请求参数中的值依旧可以正常传给后端)。

SpringMVC开发实际项目的详细介绍

注意有时候controller层的方法参数也不加@RequestParam,不加的话就是普通的参数并且不是必须传的参数(即前端传过来可以为null);如果加@RequestParam,说明这个参数是必须要有的,默认required=true,当然也可以设置required=false设置为非必传。

SpringMVC开发实际项目的详细介绍

使用的时候一般可以不加,当需要默认值或者限制必须传递参数的时候可以加@RequestParam注解在方法的参数前。

  • @RequestParam,默认required=true

SpringMVC开发实际项目的详细介绍SpringMVC开发实际项目的详细介绍

  • @RequestParam,指定required=false

SpringMVC开发实际项目的详细介绍SpringMVC开发实际项目的详细介绍

  • @RequestParam,指定defaultValue

SpringMVC开发实际项目的详细介绍SpringMVC开发实际项目的详细介绍

  • 不加@RequestParam

SpringMVC开发实际项目的详细介绍SpringMVC开发实际项目的详细介绍

注意到swagger-ui上的参数description同参数名

4.2 @RequestBody

SpringMVC开发实际项目的详细介绍SpringMVC开发实际项目的详细介绍

实际中可以对应项目中的vo(实体对象都是转为json进行前后端交互的),并根据需要加@RequestBody

SpringMVC开发实际项目的详细介绍SpringMVC开发实际项目的详细介绍SpringMVC开发实际项目的详细介绍

实际中vo实体可以用@RequestBody,前后端交互使用json;也可以使用@RequestParam,前后端交互使用form

4.3 @ModelAttribute

SpringMVC开发实际项目的详细介绍

4.4 @PathVariable

SpringMVC开发实际项目的详细介绍

不同的@XXXMapping无需占位符,相同的@XXXMapping路径需要使用占位符区分以便方法的调用

SpringMVC开发实际项目的详细介绍

4.5 @RequestHeader

SpringMVC开发实际项目的详细介绍SpringMVC开发实际项目的详细介绍SpringMVC开发实际项目的详细介绍

4.6 @CooikeValue

SpringMVC开发实际项目的详细介绍SpringMVC开发实际项目的详细介绍SpringMVC开发实际项目的详细介绍

4.7 @SessionAttribute

SpringMVC开发实际项目的详细介绍

注意只能加在

SpringMVC开发实际项目的详细介绍

  • 存储对象到request域中

SpringMVC开发实际项目的详细介绍

相当于HttpServletRequest中的setAttribute方法(不推荐使用这种方式的原因是过分依赖ServletAPI,代码的耦合性变高

  • 存储对象到session中

SpringMVC开发实际项目的详细介绍

  • 从session中获取对象

SpringMVC开发实际项目的详细介绍

  • 清除session

SpringMVC开发实际项目的详细介绍SpringMVC开发实际项目的详细介绍

4.8 @Validated

当一个实体类中的属性使用validate.annotation中的验证注解之后,在使用该实体作为controller层的参数时添加@Validated表示开启对该实体类属性的验证。如果传入参数不符合验证则提示101及相应错误。

SpringMVC开发实际项目的详细介绍

五. 响应类型

5.1 响应类型为String

SpringMVC开发实际项目的详细介绍

5.2 响应类型为void

SpringMVC开发实际项目的详细介绍

5.3 响应类型为@ResponseBody

SpringMVC开发实际项目的详细介绍SpringMVC开发实际项目的详细介绍SpringMVC开发实际项目的详细介绍

5.4 过滤静态资源

SpringMVC开发实际项目的详细介绍

六. 上传文件

SpringMVC中使用MultipartFile对象来封装前端传过来的文件对象

6.1 文件上传的前提

SpringMVC开发实际项目的详细介绍

6.2 SpringMVC实现文件上传

6.2.1 springmvc.xml中配置文件解析器

<?xml version="1.0" encoding="UTF-8"?>
<!-- 引用的名称和对象 -->
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/mvc
       http://www.springframework.org/schema/mvc/spring-mvc.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 开启注解扫描 -->
    <context:component-scan base-package="com.practice"></context:component-scan>

    <!-- 视图解析器 -->
    <bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 文件的前缀,一般为路径 -->
        <property name="prefix" value="/WEB-INF/pages/"></property>
        <!-- 文件名的后缀 -->
        <property name="suffix" value=".jsp"></property>
    </bean>

    <!-- 文件解析器器,注意此处bean的id必须为multipartResolver,否则会显示multipartFile加载失败 -->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <!-- 配置总文件最大为10MB 10*1024*1024,单位为字节 1KB = 1024Byte字节 -->
        <property name="maxUploadSize" value="10485760"></property>
    </bean>

    <!-- 开启springmvc框架注解的支持,替代处理器映射器和处理器适配器 -->
    <mvc:annotation-driven/>
</beans>

6.2.2 编写controller层

package com.practice;

import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.WebResource;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.UUID;

@Controller
@RequestMapping("/user")
public class UserController {

    @RequestMapping("/fileUpload")
    public String fileUpload(HttpServletRequest request,MultipartFile upload/*必须和前端文件上传input标签中的name相同*/) throws IOException {
        System.out.println("upload...");

        //使用fileupload组件完成文件上传
        //上传的位置(部署在tomcat服务器中,服务器路径为tomcat的安装路径)
        String path = request.getSession().getServletContext().getRealPath("/upload/");

        System.out.println(path);

        //判断路径是否存在,如果不存在手动创建
        File file = new File(path);
        if(!file.exists()){
            //创建文件夹
            file.mkdirs();
        }
        //获取上传文件的名称
        String fileName = upload.getOriginalFilename();
        //将文件名设置为唯一值,使用UUID
        fileName = UUID.randomUUID().toString().replace("-","") + "_" + fileName;
        //完成文件上传
        upload.transferTo(new File(path,fileName));
        return "success";
    }
} 

6.2.3 编写index.jsp

<%--
  Created by IntelliJ IDEA.
  User: cici
  Date: 2020/8/9
  Time: 18:16
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <h3>文件上传</h3>

    <form action="user/fileUpload" method="post" enctype="multipart/form-data">
        选择文件:<input type="file" name="upload"/><br>
        <input type="submit" value="upload">
    </form>
</body>
</html> 

6.2.4 web.xml pom.xml success.jsp 同2.1-2.11

七. 异常处理与拦截器实现

7.1 异常处理

SpringMVC开发实际项目的详细介绍SpringMVC开发实际项目的详细介绍SpringMVC开发实际项目的详细介绍

7.1.1 自定义异常类

7.1.2 自定义异常处理器 implements HandlerExceptionResolver

SpringMVC开发实际项目的详细介绍注意error为/WEB-INF/page/error.jsp表示异常展示页面

7.1.3 配置异常处理器

SpringMVC开发实际项目的详细介绍

7.1.4 捕获异常处理异常

SpringMVC开发实际项目的详细介绍

实际项目中对于异常处理除了会定义一些自定义的异常,另外还会编写高复用的ResponseResult类(一般包含code/message/data属性),用来封装业务处理的返回值(code为常见的响应code,传给前端,前端好调用相关的展示页面)

SpringMVC开发实际项目的详细介绍

7.2 拦截器

7.2.1 拦截器的概念

SpringMVC开发实际项目的详细介绍

SpringMVC开发实际项目的详细介绍

拦截器只对controller层有效,过滤器对规定的路径下所有请求都有效

7.2.2 拦截器入门

1. 自定义拦截器

package com.practice.interceptor;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

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

//自定义拦截器
public class MyInterceptor implements HandlerInterceptor {

    /**
     * 预处理:controller方法执行前(一般用于登录合理性判断,是否可登录)
     * return true:表示放行,执行下一个拦截器/controller方法
     * return false:不放行
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("myInterceptor...");
//        request.getRequestDispatcher("/WEB-INF/pages/error.jsp").forward(request,response);
        return true;
    }

    /**
     * 后处理:在controller执行后,controller的return前执行(例如success.jsp页面跳转前)
     * @param request
     * @param response
     * @param handler
     * @param modelAndView
     * @throws Exception
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("after");
    }

    /**
     * success.jsp执行后,执行(一般用于释放资源)
     * @param request
     * @param response
     * @param handler
     * @param ex
     * @throws Exception
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("completion");
    }
} 

2. springmvc.xml配置拦截器

<?xml version="1.0" encoding="UTF-8"?>
<!-- 引用的名称和对象 -->
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/mvc
       http://www.springframework.org/schema/mvc/spring-mvc.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 开启注解扫描 -->
    <context:component-scan base-package="com.practice"></context:component-scan>

    <!-- 视图解析器 -->
    <bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 文件的前缀,一般为路径 -->
        <property name="prefix" value="/WEB-INF/pages/"></property>
        <!-- 文件名的后缀 -->
        <property name="suffix" value=".jsp"></property>
    </bean>

    <!-- 配置拦截器 -->
    <mvc:interceptors>
        <mvc:interceptor>
            <!-- 需要拦截的方法 /**表示任意方法都拦截-->
            <mvc:mapping path="/user/*"/>
            <!-- 不需要拦截的方法,两个配一个即可。例如配置需要拦截的方法后,其他的九尾需要拦截的-->
<!--            <mvc:exclude-mapping path=""/>-->
            <!-- 配置拦截器对象 -->
            <bean id="myInterceptor" class="com.practice.interceptor.MyInterceptor"></bean>
        </mvc:interceptor>
        <!-- 如果有多个拦截器,继续配置   执行顺序:interceptor1的preHandle方法-interceptor2的preHandle方法
            -controller方法-interceptor2的postHandler方法-interceptor1的postHandler方法
            -controller方法的return-interceptor2的afterCompletion方法-interceptor的afterCompletion方法-->
<!--        <mvc:interceptor>-->
<!--            <mvc:mapping path="/user/*"/>-->
<!--            &lt;!&ndash; 配置拦截器对象 &ndash;&gt;-->
<!--            <bean id="myInterceptor1" class="com.practice.interceptor.MyInterceptor1"></bean>-->
<!--        </mvc:interceptor>-->
    </mvc:interceptors>

    <!-- 开启springmvc框架注解的支持,替代处理器映射器和处理器适配器 -->
    <mvc:annotation-driven/>
</beans>

3. pom.xml web.xml controller index.jsp success.jsp 同上

本文地址:https://blog.csdn.net/qq_38586378/article/details/107892289