Spring MVC

星期四, 12月 26, 2024 | 38分钟阅读

MyraZ
Spring MVC

关于Spring MVC的学习!

MVC思想& SpringMVC框架概念与特点

什么是MVC?

​ 模型-视图-控制器(MVC)是一个众所周知的以设计界面应用程序为基础的设计思想。它主要通过分离模型、视图及控制器在应用程序中的角色将业务逻辑从界面中解耦。通常,模型负责封装应用程序数据在视图层展示。视图仅仅只是展示这些数据,不包含任何业务逻辑。控制器负责接收来自用户的请求,并调用后台服务(service或者dao)来处理业务逻辑。处理后,后台业务层可能会返回了一些数据在视图层展示。控制器收集这些数据及准备模型在视图层展示。MVC模式的核心思想是将业务逻辑从界面中分离出来,允许它们单独改变而不会相互影响。

image-20250102193151967

常见MVC框架运行性能比较

​ Jsp+servlet > struts1 > spring mvc > struts2+freemarker > struts2,ognl,值栈。

​ 开发效率上,基本正好相反。值得强调的是,spring mvc开发效率和struts2不相上下,但从目前来看,spring mvc 的流行度已远远超过struts2。

Spring MVC是什么?

​ Spring MVC是Spring家族中的一个web成员,它是一种基于Java的实现了Web MVC设计思想的请求驱动类型的轻量级Web框架,即使用了MVC架构模式的思想,将web层进行职责解耦,基于请求驱动指的就是使用请求-响应模型,框架的目的就是帮助我们简化开发,Spring MVC也是要简化我们日常Web开发的。

Spring MVC是服务到工作者思想的实现。前端控制器是DispatcherServlet;应用控制器拆为处理器映射

器(Handler Mapping)进行处理器管理和视图解析器(View Resolver)进行视图管理;支持本地化/国际化(Locale)解析及文件上传等;提供了非常灵活的数据验证、格式化和数据绑定机制;提供了强大的约定大于配置(惯例优先原则)的契约式编程支持。

Spring MVC能帮我们做什么?

  1. 让我们能非常简单的设计出干净的Web层;

  2. 进行更简洁的Web层的开发;

  3. 天生与Spring框架集成(如IOC容器、AOP等);

  4. 提供强大的约定大于配置的契约式编程支持;

  5. 能简单的进行Web层的单元测试;

  6. 支持灵活的URL到页面控制器的映射;

  7. 非常容易与其他视图技术集成,如jsp、Velocity、FreeMarker等等,因为模型数据不放在特定的API里,而是放在一个Model里(Map数据结构实现,因此很容易被其他框架使用);

  8. 非常灵活的数据验证、格式化和数据绑定机制,能使用任何对象进行数据绑定,不必实现特定框架的API;

  9. 支持灵活的本地化等解析;

  10. 更加简单的异常处理;

  11. 对静态资源的支持;

  12. 支持Restful风格。

SpringMVC请求流程 &环境搭建

Spring MVC 请求处理流程分析

image-20250103150051939

​ Spring MVC框架也是一个基于请求驱动的Web框架,并且使用了前端控制器模式(是用来提供一个集中的请求处理机制,所有的请求都将由一个单一的处理程序处理来进行设计,再根据请求映射规则分发给相应的页面控制器(动作/处理器)进行处理。首先让我们整体看一下Spring MVC处理请求的流程:

  1. 首先用户发送请求,请求被SpringMvc前端控制器(DispatherServlet)捕获;

  2. 前端控制器(DispatherServlet)对请求URL解析获取请求URI,根据URI, 调用HandlerMapping;

  3. 前端控制器(DispatherServlet)获得返回的HandlerExecutionChain(包括Handler对象以及Handler对象对应的拦截器);

  4. DispatcherServlet 根据获得的HandlerExecutionChain,选择一个合适的HandlerAdapter。(附注:如果成功获得HandlerAdapter后,此时将开始执行拦截器的preHandler(…)方法);

  5. HandlerAdapter根据请求的Handler适配并执行对应的Handler;HandlerAdapter(提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller)。 在填充Handler的入参过程中,根据配置,Spring将做一些额外的工作:

​ HttpMessageConveter: 将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定的响应信息。

​ 数据转换:对请求消息进行数据转换。如String转换成Integer、Double等数据格式化:

​ 数据格式化。 如将字符串转换成格式化数字或格式化日期等

​ 数据验证: 验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中)

  1. Handler执行完毕,返回一个ModelAndView(即模型和视图)给HandlerAdaptor

  2. HandlerAdaptor适配器将执行结果ModelAndView返回给前端控制器。

  3. 前端控制器接收到ModelAndView后,请求对应的视图解析器。

  4. 视图解析器解析ModelAndView后返回对应View

  5. 渲染视图并返回渲染后的视图给前端控制器。

  6. 最终前端控制器将渲染后的页面响应给用户或客户端

Spring MVC优势

  1. 清晰的角色划分:前端控制器(DispatcherServlet)、请求到处理器映射(HandlerMapping)、处理器适配器(HandlerAdapter)、视图解析器(ViewResolver)、处理器或页面控制器(Controller)、验证器( Validator)、命令对象(Command 请求参数绑定到的对象就叫命令对象)、表单对象(Form Object 提供给表单展示和提交到的对象就叫表单对象)。

  2. 分工明确,而且扩展点相当灵活,可以很容易扩展,虽然几乎不需要;

  3. 和Spring 其他框架无缝集成,是其它Web框架所不具备的;

  4. 可适配,通过HandlerAdapter可以支持任意的类作为处理器;

  5. 可定制性,HandlerMapping、ViewResolver等能够非常简单的定制;

  6. 功能强大的数据验证、格式化、绑定机制;

  7. 利用Spring提供的Mock对象能够非常简单的进行Web层单元测试;

  8. 本地化、主题的解析的支持,使我们更容易进行国际化和主题的切换。

  9. 强大的JSP标签库,使JSP编写更容易。

​ 还有比如RESTful(一种软件架构风格,设计风格而不是标准,只是提供了一组设计原则和约束条件。它主要用于客户端和服务器交互类的软件,目前了解即可)风格的支持、简单的文件上传、约定大于配置的契约式编程支持、基于注解的零配置支持等等。

Spring MVC环境搭建

开发环境

​ Idea + Maven + Jdk1.8 + Jetty

新建Maven webApp

​ Idea 下创建 springmvc01 工程

pom.xml坐标添加

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
    <!-- spring web -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>5.2.4.RELEASE</version>
    </dependency>
    <!-- spring mvc -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.2.4.RELEASE</version>
    </dependency>
    <!-- web servlet -->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>3.0.1</version>
    </dependency>
  </dependencies>
  <build>
    <finalName>springMVC01</finalName>
    <plugins>
      <!-- 编译环境插件 -->
      <plugin>
      <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>2.3.2</version>
        <configuration>
          <source>1.8</source>
          <target>1.8</target>
          <encoding>UTF-8</encoding>
        </configuration>
      </plugin>
      <!-- jetty插件 -->
      <plugin>
        <groupId>org.eclipse.jetty</groupId>
        <artifactId>jetty-maven-plugin</artifactId>
        <version>9.4.27.v20200227</version>
        <configuration>
          <scanIntervalSeconds>10</scanIntervalSeconds>
          <!-- 设置端口 -->
          <httpConnector>
            <port>8080</port>
          </httpConnector>
          <!-- 设置项目路径 -->
          <webAppConfig>
            <contextPath>/springmvc01</contextPath>
          </webAppConfig>
        </configuration>
      </plugin>
    </plugins>
  </build>

配置web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="3.0"
         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_3_0.xsd">
  <!-- 编码过滤 utf-8 -->
  <filter>
    <description>char encoding filter</description>
    <filter-name>encodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <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>
  <!-- servlet请求分发器 -->
  <servlet>
    <servlet-name>springMvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:servlet-context.xml</param-value>
    </init-param>
    <!-- 表示启动容器时初始化该Servlet -->
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>springMvc</servlet-name>
    <!-- 这是拦截请求, "/"代表拦截所有请求,"*.do"拦截所有.do请求 -->
     <url-pattern>/</url-pattern>
<!--    <url-pattern>*.do</url-pattern>-->
  </servlet-mapping>
</web-app>

​ 要想启动我们的 SpringMVC 环境,目前对于 mvc 框架的配置还未进行。以上在 web.xml 中引用了servlet-context.xml 文件。

servlet-context.xml配置

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
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">
    <!-- 开启扫描器 -->
    <context:component-scan base-package="com.msb.springmvc.controller"/>
    <!-- 使用默认的 Servlet 来响应静态文件 -->
    <mvc:default-servlet-handler/>
    <!-- 开启注解驱动-->
    <mvc:annotation-driven/>
    <!-- 配置视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
        <!-- 前缀:在WEB-INF目录下的jsp目录下 -->
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <!-- 后缀:以.jsp结尾的资源 -->
        <property name="suffix" value=".jsp"/>
    </bean>
</beans>

页面控制器的编写

@Controller
public class HelloController {
    /**
    * 请求映射地址 /hello.do
    * @return
    */
    @RequestMapping("/hello")
    public ModelAndView hello(){
        ModelAndView mv=new ModelAndView();
        mv.addObject("hello", "hello spring mvc");
        mv.setViewName("hello");
        return mv;
    }
}

添加视图页面

​ 在 WEB-INF 下新建 jsp 文件夹 ,并在文件夹下新建 hello.jsp

<%@page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
    String path = request.getContextPath();
    String basePath =
            request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"
            ;
%>
<!DOCTYPE HTML>
<html>
<head>
    <base href="<%=basePath %>">
    <title>My JSP 'hello.jsp' starting page</title>
    <meta http-equiv="pragma" content="no-cache">
    <meta http-equiv="cache-control" content="no-cache">
    <meta http-equiv="expires" content="0">
    <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
    <meta http-equiv="description" content="This is my page">
</head>
<body>
<!-- el表达式接收参数值 -->
${msg}
</body>
</html>

启动jetty服务器

image-20250102200327944

​ 访问地址 http://localhost:8080/springmvc01/hello.do

image-20250102200250591

​ 至此,springmvc 环境搭建完毕

URL地址映射配置&参数绑定

URL地址映射配置之@RequestMapping

​ 通过注解 @RequestMapping 将请求地址与方法进行绑定,可以在类级别和方法级别声明。类级别的注解负责将一个特定的请求路径映射到一个控制器上,将 url 和类绑定;通过方法级别的注解可以细化映射,能够将一个特定的请求路径映射到某个具体的方法上,将 url 和类的方法绑定。

映射单个URL

​ @RequestMapping("") 或 @RequestMapping(value="")

/**
     * 声明在方法上,映射单个 URL
     *    使用方式:
     *      1.@RequestMapping("/test01")
     *      2.@RequestMapping(value="/test01")
     *    访问地址:
     *      http://localhost:8080/springmvc01/test01
     * @return
     */
//    @RequestMapping("/test01")
    @RequestMapping(value = "/test01")
    public ModelAndView test01() {
        ModelAndView mav = new ModelAndView();
        mav.addObject("msg", "test01");
        mav.setViewName("hello");
        return mav;
    }

    /**
     * 路径开头是否添加斜杠“/”均可
     *      建议加上@RequestMapping("/test02")
     *      访问地址:
     *          http://localhost:8080/springmvc01/test02
     * @return
     */
    @RequestMapping("test02")
    public ModelAndView test02() {
        ModelAndView mav = new ModelAndView();
        mav.addObject("msg", "test02");
        mav.setViewName("hello");
        return mav;
    }

映射多个URL

​ @RequestMapping({"",""}) 或 @RequestMapping(value={"",""})

    /**
     *  声明在方法上,映射多个 URL
     *      支持一个方法绑定多个url的操作
     *      使用方式:
     *          1.@RequestMapping({"/test03_01","/test03_02"})
     *          2.@RequestMapping(value={"/test03_01","/test03_02"})
     *       访问地址:
     *          http://localhost:8080/springmvc01/test03_01
     *          http://localhost:8080/springmvc01/test03_02
     * @return
     */
    //@RequestMapping({"/test03_01","/test03_02"})
    @RequestMapping(value = {"/test03_01","/test03_02"})
    public ModelAndView test03() {
        ModelAndView mav = new ModelAndView();
        mav.addObject("msg", "test03");
        mav.setViewName("hello");
        return mav;
    }

映射URL在控制器(类)上

​ 用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。

/**
     * 声明在类上,类中的方法都要以该路径作为父路径
     *      声明级别:
     *          类级别+方法级别(/类路径/方法路径)
     *      访问路径:
     *          http://localhost:8080/springmvc01/url/test04
     * @return
     */
    @RequestMapping("/test04")
    public ModelAndView test04() {
        ModelAndView mav = new ModelAndView();
        mav.addObject("msg", "test04");
        mav.setViewName("hello");
        return mav;
    }

设置URL映射的请求方式

​ 默认没有设置请求方式,在HTTP 请求中最常用的请求方法是 GET、POST,还有其他的一些方法,如:DELET、PUT、HEAD 等。

​ 可以通过 method 属性设置支持的请求方式,如 method=RequestMethod.POST;如设置多种请求方式,以大括号包围,逗号隔开即可。

/**
     * 设置 URL 映射的请求方式,默认没有
     *      通过method属性设置请求方式,默认get和post请求方都支持
     *      如果设置了请求方式,则必须通过指定请求方式访问
     *      访问路径:(只能通过post请求方式访问)
     *             http://localhost:8080/springmvc01/url/test06
     *
     * @return
     */
    @RequestMapping(value = "test06",method = RequestMethod.OPTIONS)
    public ModelAndView test06() {
        ModelAndView mav = new ModelAndView();
        mav.addObject("msg", "test06");
        mav.setViewName("hello");
        return mav;
    }

通过参数名称映射 URL

    /**
     * 通过参数名称映射 URL(通过参数名称访问)
     *      通过参数形式访问
     *      访问地址:
     *          http://localhost:8080/springmvc01/url?test05
     * @return
     */
    @RequestMapping(params = "test05")
    public ModelAndView test05() {
        ModelAndView mav = new ModelAndView();
        mav.addObject("msg", "test05");
        mav.setViewName("hello");
        return mav;
    }

参数绑定

​ 客户端请求的参数到控制器功能处理方法上的参数的绑定,对于参数绑定非常灵活。

基本数据类型

/**
     * 基本数据类型绑定
     *      参数值必须存在,如果参数值不存在,也没有设置默认值,则会报500异常
     * @param age
     * @param money
     */
    @RequestMapping("/data1")
    public void data1(int age,double money){
        System.out.println("age:"+age+",money:"+money);
    }

    /**
     *  基本数据类型绑定
     *      通过注解@RequestParam标记一个形参作为请求参数。(注解声明在形参前面)
     *      可以通过注解的属性设置相关内容
     *          设置参数的默认值 defaultValue属性(避免基本类型的数据为空时的500异常)
     *          设置参数的参数名(参数的别名) name属性
     * @param age
     * @param money
     */
    @RequestMapping("/data2")
    public void data2(@RequestParam(defaultValue = "18") int age,@RequestParam(defaultValue = "20.0") double money){
        System.out.println("age:"+age+",money:"+money);
    }


    /**
     *  基本数据类型绑定
     *      通过注解@RequestParam标记一个形参作为请求参数。(注解声明在形参前面)
     *      可以通过注解的属性设置相关内容
     *          设置参数的默认值 defaultValue属性
     *          设置参数的参数名(参数的别名) name属性(如果设置了别名,客户端传递的参数名要与别名保持一致)
     * @param age
     * @param money
     */
    @RequestMapping("/data3")
    public void data3(@RequestParam(defaultValue = "18",name = "userAge") int age,@RequestParam(defaultValue = "20.0",name = "userMoney") double money){
        System.out.println("age:"+age+",money:"+money);
    }

包装数据类型

/**
     * 包装类型的数据绑定
     *      请求的参数名要与方法的形参名保持一致(在未设置别名的情况下),默认值为null
     *      可以通过注解@RequestParam的属性设置 默认值 和 别名
     * @param age
     * @param money
     */
    @RequestMapping("/data4")
    public void data4(Integer age,Double money){
        System.out.println("age:"+age+",money:"+money);
    }

字符串类型

/**
     * 字符串类型的数据绑定
     *      请求的参数名要与方法的形参名保持一致(在未设置别名的情况下),默认值为null
     *      可以通过注解@RequestParam的属性设置 默认值 和 别名
     * @param userName
     * @param userPwd
     */
    @RequestMapping("/data5")
    public void data5(String userName,String userPwd){
        System.out.println("userName:"+userName+",userPwd:"+userPwd);
    }

数组类型

/**
     * 数组类型数据绑定
     *      客户端传参形式:hobbys=sing&hobbys=dance&hobbys=rap
     * @param hobbys
     */
    @RequestMapping("/data6")
    public void data6(String[] hobbys){
        for (String hobby : hobbys) {
            System.out.println(hobby);
        }
    }

JavaBean类型

/**
     * JavaBean类型 数据绑定
     *      客户端请求的参数名与JavaBean对象的属性字段名保持一致
     * @param user
     */
    @RequestMapping("/data7")
    public void data7(User user){
        System.out.println(user);
    }

​ User.java

package com.msb.springmvc.po;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class User {
    private Integer id;
    private String userName;
    private String userPwd;

    

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getUserPwd() {
        return userPwd;
    }

    public void setUserPwd(String userPwd) {
        this.userPwd = userPwd;
    }


    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", userName='" + userName + '\'' +
                ", userPwd='" + userPwd + '\'' +
                '}';
    }
}

List类型

​ 此时 User 实体需要定义对应 list 属性。(对于集合的参数绑定,一般需要使用 JavaBean 对象进行包装)

package com.msb.springmvc.po;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class User {
    private Integer id;
    private String userName;
    private String userPwd;

    //添加list集合属性
    private List<Integer> ids;
    private List<Phone> phones = new ArrayList<Phone>();


    public List<Integer> getIds() {
        return ids;
    }

    public void setIds(List<Integer> ids) {
        this.ids = ids;
    }

    public List<Phone> getPhones() {
        return phones;
    }

    public void setPhones(List<Phone> phones) {
        this.phones = phones;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getUserPwd() {
        return userPwd;
    }

    public void setUserPwd(String userPwd) {
        this.userPwd = userPwd;
    }


    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", userName='" + userName + '\'' +
                ", userPwd='" + userPwd + '\'' +
                ", ids=" + ids +
                ", phones=" + phones +
                '}';
    }
}

​ Phone实体

package com.msb.springmvc.po;

public class Phone {
    private String num;
    public String getNum() {
        return num;
    }
    public void setNum(String num) {
        this.num = num;
    }
    @Override
    public String toString() {
        return "Phone [num=" + num + "]";
    }
}

​ JSP页面

<form action="data8" method="post">
    <%-- list集合   --%>
<%--    <input name="ids" value="123456" />--%>
<%--    <input name="ids" value="4576" />--%>
    <input name="ids[0]" value="123456" />
    <input name="ids[1]" value="4576" />
    <input name="phones[0].num" value="123456" />
    <input name="phones[1].num" value="4576" />
    <button type="submit"> 提交</button>
</form>

​ Controller 方法

    /**
     * List集合类型 数据绑定
     *      注意:对于集合类型的数据绑定,一般需要使用 JavaBean 对象进行包装
     *
     * @param user
     */
    @RequestMapping("/data8")
    public void data8(User user){
        System.out.println(user);

    }

Set类型

​ Set 和 List 类似,也需要绑定在对象上,而不能直接写在 Controller 方法的参数中。但是,绑定Set数据时,必须先在Set对象中add相应的数量的模型对象。

public class User {
    private int id;
    private String userName;
    private String userPwd;
    private Set<Phone> phones = new HashSet<Phone>();
    public User() {
        phones.add(new Phone());
        phones.add(new Phone());
        phones.add(new Phone());
        }
        /*public List<Phone> getPhones() {
        return phones;
        }
        public void setPhones(List<Phone> phones) {
        this.phones = phones;
        }*/
    public int getId() {
    	return id;
    }
    public void setId(int id) {
    	this.id = id;
    }
    public String getUserName() {
    	return userName;
    }
    public void setUserName(String userName) {
    	this.userName = userName;
    }
    public String getUserPwd() {
    	return userPwd;
    }
    public void setUserPwd(String userPwd) {
    	this.userPwd = userPwd;
    }
    public Set<Phone> getPhones() {
    	return phones;
    }
    public void setPhones(Set<Phone> phones) {
    	this.phones = phones;
    }
}

​ Controller 方法:

@RequestMapping("/data09")
public void data09(User user){
	System.out.println(user);
}

​ 表单页面

<form action="data09" method="post">
    <input name="phones[0].num" value="123456" />
    <input name="phones[1].num" value="4576" />
    <input name="phones[2].num" value="4576" />
    <button type="submit"> 提交</button>
</form>

Map类型

​ Map最为灵活,它也需要绑定在对象上,而不能直接写在Controller方法的参数中。

public class User {
    private int id;
    private String userName;
    private String userPwd;
    private Set<Phone> phones = new HashSet<Phone>();
    private Map<String, Phone> map=new HashMap<String, Phone>();
    // private List<Phone> phones=new ArrayList<Phone>();
    public User() {
        phones.add(new Phone());
        phones.add(new Phone());
        phones.add(new Phone());
        }
        /*public List<Phone> getPhones() {
        return phones;
        }
        public void setPhones(List<Phone> phones) {
        this.phones = phones;
        }*/
    public int getId() {
    	return id;
    }
    public void setId(int id) {
    	this.id = id;
    }
    public String getUserName() {
    	return userName;
    }
    public void setUserName(String userName) {
    	this.userName = userName;
    }
    public String getUserPwd() {
    	return userPwd;
    }
    public void setUserPwd(String userPwd) {
    	this.userPwd = userPwd;
    }
    public Set<Phone> getPhones() {
    	return phones;
    }
    public void setPhones(Set<Phone> phones) {
    	this.phones = phones;
    }
    public Map<String, Phone> getMap() {
		return map;
	}
	public void setMap(Map<String, Phone> map) {
		this.map = map;
	}
}

​ Controller 方法:

@RequestMapping("/data10")
public void data10(User user){
	Set<Entry<String, Phone>> set = user.getMap().entrySet();
	for(Entry<String, Phone> entry:set){
		System.out.println(entry.getKey()+"--"+entry.getValue().getNum());
	}
}

​ 表单页面

<form action="data10" method="post">
    <input name="map['1'].num" value="123456" />
    <input name="map['2'].num" value="4576" />
    <input name="map['3'].num" value="4576" />
	<button type="submit"> 提交</button>
</form>

请求域对象设置 API

​ 请求域对象设置 API的五种对象(方式):ModelAndView、ModelMap、Model、Map、HttpServletRequest。

ModelAndView对象

	/**
     * ModelAndView对象
     * @return
     */
    @RequestMapping("/model01")
    public ModelAndView model01(){
        /**
         * 数据模型 model
         * 视图 view
         */
        ModelAndView mv = new ModelAndView();
        //设置数据模型(请求域对象的数据)
        mv.addObject("msg","hello model01");
        //设置视图
        mv.setViewName("hello");
        //返回视图
        return mv;
    }

ModelMap对象

	/**
     * ModelMap对象
     * @param modelMap
     * @return
     */
    @RequestMapping("/model02")
    public String model02(ModelMap modelMap){

        //设置请求域对象
        modelMap.addAttribute("msg","hello model02");

        return "hello";
    }

Model对象

	/**
     * Model对象
     * @param model
     * @return
     */
    @RequestMapping("/model03")
    public String model03(Model model){

        //设置请求域对象
        model.addAttribute("msg","hello model03");

        return "hello";
    }

Map对象

	/**
     * Map对象
     * @param map
     * @return
     */
    @RequestMapping("/model04")
    public String model04(Map map){

        //设置请求域对象
        map.put("msg","hello model04");

        return "hello";
    }

HttpServletRequest对象

   /**
     * HttpServletRequest对象
     * @param request
     * @return
     */
    @RequestMapping("/model05")
    public String model05(HttpServletRequest request){

        //设置请求域对象
        request.setAttribute("msg","hello model05");

        return "hello";
    }

请求转发与重定向的问题

重定向

​ 重定向是发一个302的状态码给浏览器,浏览器自己去请求跳转的网页。地址栏会发生改变。

重定向以redirect:开头

重定向到jsp页面

	/**
     * 重定向到jsp页面
     * @return
     */
    @RequestMapping("/view01")
    public String view01(){
        return "redirect:view.jsp";
    }

重定向到jsp页面,传递参数

/**
     * 重定向到jsp页面
     *      传递参数
     * @return
     */
    @RequestMapping("/view02")
    public String view02(){
        return "redirect:view.jsp?uname=admin&upwd=123456";
    }

重定向到jsp页面,传递中文参数(会出现乱码)

	/**
     * 重定向到jsp页面
     *      传递中文参数(会出现乱码)
     * @return
     */
    @RequestMapping("/view03")
    public String view03(){
        return "redirect:view.jsp?uname=张三&upwd=123456";
    }

重定向到jsp页面,传递RedirectAttributes设置的参数

​ 通过RedirectAttributes对象设置重定向的参数避免中文乱码问题

    /**
     * 重定向到jsp页面
     *      传递参数(通过RedirectAttributes对象设置重定向的参数避免中文乱码问题)
     * @param attributes
     * @return
     */
    @RequestMapping("/view04")
    public String view04(RedirectAttributes attributes){
        //设置参数
        attributes.addAttribute("uname", "张三");
        attributes.addAttribute("upwd", "123456");
        return "redirect:view.jsp";
    }

重定向到jsp页面,返回ModelAndView对象

    /**
     * 重定向到jsp页面
     *      返回的是ModelAndView对象
     * @param modelAndView
     * @return
     */
    @RequestMapping("/view05")
    public ModelAndView view05(ModelAndView modelAndView){
        //设置模型数据
        modelAndView.addObject("uname", "李四");
        modelAndView.addObject("upwd", "123456");
        //设置视图
        modelAndView.setViewName("redirect:view.jsp");

        return modelAndView;
    }

重定向到Controller,返回ModelAndView对象

	/**
     * 重定向到Controller
     *      返回ModelAndView对象
     * @param modelAndView
     * @return
     */
    @RequestMapping("/view06")
    public ModelAndView view06(ModelAndView modelAndView){
        //设置模型数据
        modelAndView.addObject("uname", "王五");
        modelAndView.addObject("upwd", "admin");
        //设置视图
        modelAndView.setViewName("redirect:test");

        return modelAndView;
    }

页面获取参数值

${param.参数名}

请求转发(默认)

​ 请求转发,直接调用跳转的页面,让它返回。对于浏览器来说,它无法感觉服务器有没有forward。地址栏不发生改变。可以获取请求域中的数据。

请求转发以 forward: 开头(可省略)

请求转发到jsp页面

	/**
     * 请求转发到jsp页面
     * @return
     */
    @RequestMapping("/view07")
    public String view07(){
        return "forward:view.jsp";
    }

请求转发到jsp页面,传递参数(中文不会乱码)

    /**
     * 请求转发到jsp页面
     *      传递参数(中文不会乱码)
     * @return
     */
    @RequestMapping("/view08")
    public String view08(){
        return "forward:view.jsp?uname=张三&upwd=123456";
    }

请求转发到jsp页面,设置请求域

    /**
     * 请求转发到jsp页面
     *      设置请求域
     * @return
     */
    @RequestMapping("/view09")
    public String view09(Model model){
        //设置请求域
        model.addAttribute("name","管理员");
        return "forward:view.jsp?uname=张三&upwd=123456";
    }

请求转发到jsp页面(默认),return “页面名”;

​ 默认会去指定的目录下查找jsp页面(配置文件中设置的)

    /**
     *  请求转发到jsp页面(默认)
     *      默认会去指定的目录下查找jsp页面(配置文件中设置的)
     * @param model
     * @return
     */
    @RequestMapping("/view10")
    public String view10(Model model){
        //设置请求域
        model.addAttribute("name","管理员");
        return "../../view";
    }

请求转发到Controller

    /**
     * 请求转发到Controller
     * @param modelAndView
     * @return
     */
    @RequestMapping("/view11")
    public  ModelAndView view11(ModelAndView modelAndView){
        modelAndView.setViewName("forward:test");
        return modelAndView;
    }

请求转发到Controller,传递参数

	/**
     * 请求转发到Controller
     *      传递参数
     * @param modelAndView
     * @return
     */
    @RequestMapping("/view12")
    public  ModelAndView view12(ModelAndView modelAndView){
        modelAndView.setViewName("forward:test?uname=admin&upwd=123456");
        return modelAndView;
    }

页面中获取数据

获取传递的参数:${param.参数名}
获取请求域的数据:${请求域中设置的名称}

SpringMVC之JSON数据开发

JSON的优点

	JSON是一个纯文本格式,是独立于语言和平台的,生成和解析比较简单,读写速度很快,方便前台做解析,对于后端和客户端都非常方便。

基本概念

​ Json 在企业开发中已经作为通用的接口参数类型,在页面(客户端)解析很方便。SpringMVC 对于json提供了良好的支持,这里需要修改相关配置,添加 json 数据支持功能。

@ResponseBody

​ 该注解用于将 Controller 的方法返回的对象,通过适当的HttpMessageConverter 转换为指定格式后,写入到 Response 对象的 body 数据区。

​ 返回的数据不是 html 标签的页面,而是其他某种格式的数据时(如 json、xml 等)使用(通常用于ajax 请求)。

@RequestBody

​ 该注解用于读取 Request 请求的 body 部分数据,使用系统默认配置的 HttpMessageConverter 进行解析,然后把响应的数据绑定到要返回的对象上 ,再把 HttpMessageConverter 返回的对象数据绑定到controller 中方法的参数上。

使用配置

pom.xml添加json相关坐标

<!-- 添加json 依赖jar包 -->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
    <version>2.10.0</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.10.0</version>
    </dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-annotations</artifactId>
    <version>2.10.0</version>
</dependency>

修改servlet-context.xml

<!-- mvc 请求映射 处理器与适配器配置 -->
<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>

注解使用

@ResponseBody
	/**
     * @ResponseBody 返回的数据是JSON格式(返回JavaBean对象)
     *      注解设置在方法体上
     * @return
     */
    @RequestMapping("/query01")
    @ResponseBody
    public User  queryUser01(){
        User user = new User();
        user.setId(1);
        user.setUserName("zhangsan");
        user.setUserPwd("123456");
        return user;
    }

    /**
     * @ResponseBody 返回的数据是JSON格式(返回JavaBean对象)
     *      注解设置在方法返回对象前,修饰符后
     * @return
     */
    @RequestMapping("/query02")
    public @ResponseBody User  queryUser02(){
        User user = new User();
        user.setId(2);
        user.setUserName("lisi");
        user.setUserPwd("123321");
        return user;
    }

    /**
     * @ResponseBody 返回的数据是JSON格式(返回list集合)
     * @return
     */
    @RequestMapping("/query03")
    @ResponseBody
    public List<User> queryUser03(){
        List<User> users = new ArrayList<User>();

        User user = new User();
        user.setId(1);
        user.setUserName("zhangsan");
        user.setUserPwd("123456");

        User user02 = new User();
        user02.setId(2);
        user02.setUserName("lisi");
        user02.setUserPwd("123321");

        users.add(user);
        users.add(user02);
        return users;
    }

    /**
     * 当ajax请求时,@ResponseBody会将数据转化为json格式,响应给ajax的回调函数
     * @return
     */
    @RequestMapping("/query04")
    @ResponseBody
    public User queryUser04(){
        User user = new User();
        user.setId(3);
        user.setUserName("王五");
        user.setUserPwd("123456");
        return user;
    }
<%--
  Created by IntelliJ IDEA.
  User: MyraZ
  Date: 2025/1/3
  Time: 10:44
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>JSON处理</title>
    <%-- 引入Jquery核心js文件   --%>
    <script type="text/javascript" src="js/jquery-3.4.1.js"></script>
</head>
<body>
    <input type="button" value="响应json数据" onclick="test01()">

    <script type="text/javascript">
        /**
         * 通过ajax访问后台的方法,获取返回的数据
         */
        function test01(){
            //发送ajax请求
            $.ajax({
                //请求方式GET/POST
                type:"post",
                //请求路径
                url:"user/query04",
                //设置参数
                data:{
                    "userName":"zhangsan",
                    "userPwd":"123456"
                },
                //回调函数,接收服务端返回的结果(函数中的形参用来接收返回的数据)
                success:function (data) {
                    console.log(data)
                }
            })
        }

    </script>
</body>
</html>
@RequestBody

​ @RequestBody 注解常用来处理 content-type 不是默认的 application/x-www-form-urlcoded 类型的内容,比如说:application/json 或application/xml 等。一般情况下来说常用其来处理 application/json类型。@RequestBody接受的是一个 json 格式的字符串,一定是一个字符串

​ 通过 @RequestBody 可以将请求体中的 JSON 字符串绑定到相应的 bean 上,当然,也可以将其分别绑定到对应的字符串上。

 /**
     * @RequestBody 规定请求的参数必须是json格式的字符串
     *      注解设置在形参前面
     *
     *      1.@RequestBody 处理不是默认的类型(application/x-www-form-urlcoded)的内容
     *      2.请求的参数一定是json格式的字符串,一定是字符串
     * @param user
     * @return
     */
    @RequestMapping("/query05")
    @ResponseBody
    public User queryUser05(@RequestBody User user){
        System.out.println(user);
        return user;
    }
<%--
  Created by IntelliJ IDEA.
  User: MyraZ
  Date: 2025/1/3
  Time: 10:44
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>JSON处理</title>
    <%-- 引入Jquery核心js文件   --%>
    <script type="text/javascript" src="js/jquery-3.4.1.js"></script>
</head>
<body>
    <input type="button" value="响应json数据" onclick="test01()">
    <input type="button" value="设置请求json" onclick="test02()">
    <script type="text/javascript">
        /**
         * 通过ajax访问后台的方法,获取返回的数据
         */
        function test01(){
            //发送ajax请求
            $.ajax({
                //请求方式GET/POST
                type:"post",
                //请求路径
                url:"user/query04",
                //设置参数
                data:{
                    "userName":"zhangsan",
                    "userPwd":"123456"
                },
                //回调函数,接收服务端返回的结果(函数中的形参用来接收返回的数据)
                success:function (data) {
                    console.log(data)
                }
            })
        }

        /**
         *  传递json格式的参数
         */
        function test02(){
            //发送ajax请求
            $.ajax({
                //请求方式GET/POST
                type:"post",
                //请求路径
                url:"user/query05",
                //设置服务器请求参数的类型为application/json
                contentType:"application/json",
                //设置参数
                data:'{"userName":"zhangsan", "userPwd":"123456"}',
                //回调函数,接收服务端返回的结果(函数中的形参用来接收返回的数据)
                success:function (data) {
                    console.log(data)
                }
            })
        }
    </script>
</body>
</html>

拦截器

基本概念

​ SpringMVC 中的 Interceptor 拦截器也是相当重要和相当有用的,它的主要作用是拦截用户的请求并进行相应的处理。比如通过它来进行权限验证,或者是来判断用户是否登陆等操作。对于SpringMVC 拦截器的定义方式有两种:

  1. 实现接口:org.springframework.web.servlet.HandlerInterceptor
  2. 继承适配器:org.springframework.web.servlet.handler.HandlerInterceptorAdapter

拦截器实现

实现HandlerInterceptor接口

  • 接口实现类
/**
 * 2025-1-4 SpringMVC
 * 拦截器的实现
 *  实现HandlerInterceptor接口
 */
public class MyInterceptor01 implements HandlerInterceptor {
    /**
     * 在 目标Handler(方法)执行前 执行
     *      返回true,执行Handler方法
     *      返回false,阻止目标Handler执行
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("在 目标Handler(方法)执行前执行MyInterceptor01 --> preHandle方法 ");
        return true;
    }

    /**
     * 在 目标Handler(方法)执行后,视图生成前 执行
     * @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("在 目标Handler(方法)执行后,试图生成前执行MyInterceptor01 --> postHandle方法");
    }

    /**
     * 在 目标Handler(方法)执行后,视图生成后 执行
     * @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("在 目标Handler(方法)执行后,试图生成后执行MyInterceptor01 --> afterCompletion方法");
    }
}
  • 拦截器配置

方式一:

<!-- 拦截器配置:方式一 -->
<mvc:interceptors>
    <!--
        使用bean定义一个Interceptor
        直接定义在mvc:interceptors根下面的Interceptor将拦截所有的请求
    -->
    <bean class="com.xxxx.springmvc.interceptor.MyInterceptor01"/>
</mvc:interceptors>

方式二(推荐使用):

<!-- 拦截器配置:方式二 (推荐使用) -->
<mvc:interceptors>
    <!--
        定义在 mvc:interceptor 下面,可以自定义需要拦截的请求
        如果有多个拦截器满足拦截处理的要求,则依据配置的先后顺序来执行
    -->
    <mvc:interceptor>
        <!-- 通过 mvc:mapping 配置需要拦截的资源。支持通配符。可配置多个。 -->
        <mvc:mapping path="/**"/> <!-- "/**"表示拦截所有的请求。 -->
        <!-- 通过 mvc:mapping 配置不需要被拦截的资源。支持通配符。可配置多个。 -->
        <mvc:exclude-mapping path="/url/*"/> <!-- "/url/*"表示放行url路径下的请求。 -->
        <bean class="com.xxxx.springmvc.interceptor.MyInterceptor01"/>
    </mvc:interceptor>
</mvc:interceptors>

继承HandlerInterceptorAdapter

实际上最终还是 HandlerInterceptor 接口实现。

  • 子类实现类
/**
 * 2025-1-4 SpringMVC
 * 拦截器实现
 *      继承HandlerInterceptorAdapter适配器
 */
public class MyInterceptor02 extends HandlerInterceptorAdapter {
    /**
     * 在 目标Handler(方法)执行前 执行
     *      返回true,执行Handler方法
     *      返回false,阻止目标Handler执行
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("在 目标Handler(方法)执行前执行MyInterceptor02 --> preHandle方法 ");
        return true;
    }

    /**
     * 在 目标Handler(方法)执行后,视图生成前 执行
     * @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("在 目标Handler(方法)执行后,试图生成前执行MyInterceptor02 --> postHandle方法");
    }

    /**
     * 在 目标Handler(方法)执行后,视图生成后 执行
     * @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("在 目标Handler(方法)执行后,试图生成后执行MyInterceptor02 --> afterCompletion方法");
    }
}
  • 拦截器配置
    <mvc:interceptors>
        <mvc:interceptor>
            <!--拦截的资源-->
            <mvc:mapping path="/**"/>
            <!--放行的资源-->
            <mvc:exclude-mapping path="/url/test01"/>
            <mvc:exclude-mapping path="/url/test02"/>
            <bean class="com.msb.springmvc.interceptor.MyInterceptor02"/>
        </mvc:interceptor>
    </mvc:interceptors>

多个拦截器实现

SpringMVC 框架支持多个拦截器配置,从而构成拦截器链,对客户端请求进行多次拦截操作。

  • 拦截器代码实现

​ 这里参考MyInterceptor01、MyInterceptor02代码

  • 拦截器xml配置

    <mvc:interceptors>
        <!--
              拦截器链(多个拦截器)
                  如果有多个拦截器满足拦截要求,则会根据配置的先后顺序执行
                  先配置的拦截器的preHandle方法,先执行
                  先配置的拦截器的postHandle、afterCompletion方法,后执行
    
            -->
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <bean class="com.msb.springmvc.interceptor.MyInterceptor01"/>
        </mvc:interceptor>
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <bean class="com.msb.springmvc.interceptor.MyInterceptor02"/>
        </mvc:interceptor>
    </mvc:interceptors>
    

拦截器应用 - 非法请求拦截

使用拦截器完成用户是否登录请求验证功能

用户控制器

UserInfoController 定义

/**
 * 用户操作模拟实现
 *  用户登录(无需登录)
 *  用户添加(需要登录)
 *  用户修改(需要登录)
 *  用户删除(需要登录)
 */
@Controller
@RequestMapping("/userInfo")
public class UserInfoController {

    /**
     * 用户登录
     * @return
     */
    @RequestMapping("/login")
    public ModelAndView userLogin() {

        System.out.println("用户登录...");
        ModelAndView mv = new ModelAndView();
        //设置视图
        mv.setViewName("success");
        return mv;
    }

    /**
     * 用户添加
     * @return
     */
    @RequestMapping("/add")
    public ModelAndView userAdd() {

        System.out.println("用户添加...");
        ModelAndView mv = new ModelAndView();
        //设置视图
        mv.setViewName("success");
        return mv;
    }

    /**
     * 用户更新
     * @return
     */
    @RequestMapping("/update")
    public ModelAndView userUpdate() {

        System.out.println("用户更新...");
        ModelAndView mv = new ModelAndView();
        //设置视图
        mv.setViewName("success");
        return mv;
    }

    /**
     * 用户删除
     * @return
     */
    @RequestMapping("/delete")
    public ModelAndView userDelete(HttpSession session) {

        System.out.println("用户删除...");
        ModelAndView mv = new ModelAndView();
        //设置视图
        mv.setViewName("success");

        //如果用户登录成功,则设置用户对想到session作用域中
        User user=new User();
        user.setId(1);
        user.setUserName("admin");
        user.setUserPwd("123456");
        session.setAttribute("user", user);

        return mv;
    }
}

页面定义

success.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
    <head>
    	<title>Title</title>
    </head>
    <body>
    	<h3>欢迎登录!</h3>
    </body>
</html>

非法请求拦截器定义

LoginInterceptor 定义

public class LoginInterceptor extends HandlerInterceptorAdapter {
    /**
     * 在目标方法执行前 执行
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        //获取session作用域中的user对象
        User user = (User) request.getSession().getAttribute("user");
        //判断session作用域中的user对象是否为空
        if (user == null) {//如果为空,表示用户未登录
            //拦截请求并跳转到登陆界面
            response.sendRedirect(request.getContextPath()+"/login.jsp");
            //不执行目标方法
            return false;
        }
        //执行目标方法
        return true;

    }
}

拦截器xml配置

servlet-context.xml 配置

    <!--非法访问拦截 拦截器-->
    <mvc:interceptors>
        <mvc:interceptor>
            <!--拦截所有请求-->
            <mvc:mapping path="/**"/>
            <!--放行资源-->
            <mvc:exclude-mapping path="/userInfo/login"/>
            <mvc:exclude-mapping path="/uploadFile"/>
            <mvc:exclude-mapping path="/uploadFiles"/>
            <bean class="com.msb.springmvc.interceptor.LoginInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>

文件上传

环境配置

pom.xml

    <!-- 添加 commons-fileupload 依赖 -->
    <dependency>
      <groupId>commons-fileupload</groupId>
      <artifactId>commons-fileupload</artifactId>
      <version>1.3.2</version>
    </dependency>

servlet-context.xml修改

    <!-- 文件上传 -->
    <bean id="multipartResolver"
          class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <!-- 允许文件上传的最大尺寸 -->
        <property name="maxUploadSize">
            <value>104857600</value>
        </property>
        <!--
        设置文件放入临时文件夹的最大大小限制。
        此值是阈值,低于此值,则保存在内存中,如高于此值,则生成硬盘上的临时文件。
        -->
        <property name="maxInMemorySize">
            <value>4096</value>
        </property>
    </bean>

代码实现

单文件上传

前端页面表单
  • input 的type设置为file

  • form 表单的method设为post,

  • form 表单的enctype设置为multipart/form-data,以二进制的形式传输数据

    <form method="post" action="uploadFile" enctype="multipart/form-data">
        <input type="file" name="file">
        <button>提交</button>
    </form>

代码实现

/**
 * 2025-1-4 SpringMVC
 * 文件上传
 */
@Controller
public class FileController {

    /**
     * 单文件上传
     * 		使用MultipartFile对象作为参数,接收前端发送过来的文件
     * @param request
     * @return
     */
    @RequestMapping("/uploadFile")
    public String uploadFile(HttpServletRequest request, @RequestParam("file") MultipartFile file) {
        /*//转换成MultipartHttpServletRequest对象
        MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
        //获取上传的文件
        MultipartFile file = multipartRequest.getFile("file");*/
        //判断文件是否为空,如果不为空则进行对应的文件上传操作
        if (!file.isEmpty()) {
            try {
                //获取项目所在的路径(绝对路径)
                String path = request.getSession().getServletContext().getRealPath("/");
                //设置上传文件存放的目录
                File uploadFile=new File(path+"/upload");
                //判断文件目录是否存在,如果不存在则新建对应的目录
                if (!uploadFile.exists()) {
                    //新建目录
                    uploadFile.mkdirs();
                }
                //获取上传文件的文件名
                String fileName = file.getOriginalFilename();
                //获取文件的后缀名
                String suffix = fileName.substring(fileName.lastIndexOf("."));
                //通过系统当前时间的毫秒数,生成随机的文件名
                String newFileName = System.currentTimeMillis() + suffix;
                //上传文件(转存文件到指定目录)
                file.transferTo(new File(uploadFile,newFileName));

                //如果上传成功,设置作用域
                request.setAttribute("msg", "文件上传成功");
            } catch (IOException e) {
                e.printStackTrace();
                //如果上传失败,设置作用域
                request.setAttribute("msg", "文件上传失败");
            }

        }else{
            //如果上传文件不存在,设置作用域
            request.setAttribute("msg", "文件上传不存在");
        }

        return  "result";
    }
}

多文件上传

前端页面表单
    <form method="post" action="uploadFiles" enctype="multipart/form-data">
        <input type="file" name="files">
        <input type="file" name="files">
        <button>提交</button>
    </form>
代码实现
/**
 * 2025-1-4 SpringMVC
 * 文件上传
 */
@Controller
public class FileController {

    /**
     * 单文件上传
     *      使用MultipartFile对象作为参数,接收前端发送过来的文件
     * @param request
     * @return
     */
    @RequestMapping("/uploadFile")
    public String uploadFile(HttpServletRequest request, @RequestParam("file") MultipartFile file) {
        /*//转换成MultipartHttpServletRequest对象
        MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
        //获取上传的文件
        MultipartFile file = multipartRequest.getFile("file");*/
        //上传文件
        saveFile(file, request);

        return  "result";
    }

    /**
     * 多文件上传
     * @param files
     * @param request
     * @return
     */
    @RequestMapping("/uploadFiles")
    public String uploadFiles(@RequestParam("files") List<MultipartFile> files, HttpServletRequest request) {
        //判断文件集合是否为空
        if(files!=null || files.size()>=0){
            for (MultipartFile file : files) {
                //上传文件
                saveFile(file, request);
            }
        }

        return  "result";
    }

    /**
     * 上传文件
     * @param file
     * @param request
     */
    public void  saveFile(MultipartFile file,HttpServletRequest request){
        //判断文件是否为空,如果不为空则进行对应的文件上传操作
        if (!file.isEmpty()) {
            try {
                //获取项目所在的路径(绝对路径)
                String path = request.getSession().getServletContext().getRealPath("/");
                //设置上传文件存放的目录
                File uploadFile=new File(path+"/upload");
                //判断文件目录是否存在,如果不存在则新建对应的目录
                if (!uploadFile.exists()) {
                    //新建目录
                    uploadFile.mkdirs();
                }
                //获取上传文件的文件名
                String fileName = file.getOriginalFilename();
                //获取文件的后缀名
                String suffix = fileName.substring(fileName.lastIndexOf("."));
                //通过系统当前时间的毫秒数,生成随机的文件名
                String newFileName = System.currentTimeMillis() + suffix;
                //上传文件(转存文件到指定目录)
                file.transferTo(new File(uploadFile,newFileName));

                //如果上传成功,设置作用域
                request.setAttribute("msg", "文件上传成功");
            } catch (IOException e) {
                e.printStackTrace();
                //如果上传失败,设置作用域
                request.setAttribute("msg", "文件上传失败");
            }

        }else{
            //如果上传文件不存在,设置作用域
            request.setAttribute("msg", "文件上传不存在");
        }
    }

}

SSM框架整合

环境配置

IDEA下创建项目

创建Maven对应的Web项目

配置pom.xml

修改JDK版本
<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
</properties>
添加坐标依赖
  <dependencies>
    <!-- junit 测试 -->
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
    </dependency>
    <!-- spring 核心jar -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.2.4.RELEASE</version>
    </dependency>
    <!-- spring 测试jar -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-test</artifactId>
      <version>5.2.4.RELEASE</version>
    </dependency>
    <!-- spring jdbc -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>5.2.4.RELEASE</version>
    </dependency>
    <!-- spring事物 -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-tx</artifactId>
      <version>5.2.4.RELEASE</version>
    </dependency>
    <!-- aspectj切面编程的jar -->
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>1.9.5</version>
    </dependency>
    <!-- c3p0 连接池 -->
    <dependency>
      <groupId>com.mchange</groupId>
      <artifactId>c3p0</artifactId>
      <version>0.9.5.2</version>
    </dependency>
    <!-- mybatis -->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.5.3</version>
    </dependency>
    <!-- 添加mybatis与Spring整合的核心包 -->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis-spring</artifactId>
      <version>2.0.3</version>
    </dependency>
    <!-- mysql 驱动包 -->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>8.0.19</version>
    </dependency>
    <!-- 日志打印相关的jar -->
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-log4j12</artifactId>
      <version>1.7.2</version>
    </dependency>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>1.7.2</version>
    </dependency>
    <!-- 分页插件 -->
    <dependency>
      <groupId>com.github.pagehelper</groupId>
      <artifactId>pagehelper</artifactId>
      <version>5.1.10</version>
    </dependency>
    <!-- spring web -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>5.2.4.RELEASE</version>
    </dependency>
    <!-- spring mvc -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.2.4.RELEASE</version>
    </dependency>
    <!-- web servlet -->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>3.0.1</version>
    </dependency>
    <!-- 添加json 依赖jar包(注意版本问题) -->
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-core</artifactId>
      <version>2.10.0</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.10.0</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-annotations</artifactId>
      <version>2.10.0</version>
    </dependency>
    <!-- commons-fileupload -->
    <dependency>
      <groupId>commons-fileupload</groupId>
      <artifactId>commons-fileupload</artifactId>
      <version>1.3.2</version>
    </dependency>

  </dependencies>
设置资源目录和插件
<build>
    <finalName>ssm</finalName>

    <!--
        Maven 项目:如果源代码(src/main/java)存在xml、properties、tld 等文件
        Maven 默认不会自动编译该文件到输出目录,如果要编译源代码中xml properties tld 等文件
        需要显式配置 resources 标签
	-->
    <resources>
      <resource>
        <directory>src/main/resources</directory>
      </resource>
      <resource>
        <directory>src/main/java</directory>
        <includes>
          <include>**/*.xml</include>
          <include>**/*.properties</include>
          <include>**/*.tld</include>
        </includes>
        <filtering>false</filtering>
      </resource>
    </resources>
    <plugins>
    <!-- 编译环境插件 -->
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-compiler-plugin</artifactId>
      <version>2.3.2</version>
      <configuration>
        <source>1.8</source>
        <target>1.8</target>
        <encoding>UTF-8</encoding>
      </configuration>
    </plugin>
      <!-- jetty插件 -->
      <plugin>
        <groupId>org.eclipse.jetty</groupId>
        <artifactId>jetty-maven-plugin</artifactId>
        <version>9.4.27.v20200227</version>
        <configuration>
          <scanIntervalSeconds>10</scanIntervalSeconds>
          <!-- 设置端口 -->
          <httpConnector>
            <port>8080</port>
          </httpConnector>
          <!-- 设置项目路径 -->
          <webAppConfig>
            <contextPath>/ssm</contextPath>
          </webAppConfig>
        </configuration>
      </plugin>
    </plugins>
  </build>

配置web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="3.0"
         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_3_0.xsd">
  <!-- 启动spring容器-->
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:spring.xml</param-value>
  </context-param>
  <!-- 设置监听器 -->
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
  <!-- 编码过滤 utf-8 -->
  <filter>
      <description>char encoding filter</description>
      <filter-name>encodingFilter</filter-name>
      <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
      <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>
  <!-- servlet请求分发器 -->
  <servlet>
    <servlet-name>springMvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:servlet-context.xml</param-value>
    </init-param>
    <!-- 表示启动容器时初始化该Servlet -->
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>springMvc</servlet-name>
    <!-- 这是拦截请求, "/"代表拦截所有请求,"*.do"拦截所有.do请求 -->
    <url-pattern>/</url-pattern>
    <!--<url-pattern>*.do</url-pattern>-->
  </servlet-mapping>
  
</web-app>

配置servlet-context.xml

在项目的 src/main/resources 下创建 servlet-context.xml 文件, 内容如下

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
    http://www.springframework.org/schema/mvc
    http://www.springframework.org/schema/mvc/spring-mvc.xsd
    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">
    <!-- 开启扫描器 -->
    <context:component-scan base-package="com.msb.ssm.controller" />
    <!-- mvc 注解驱动 并添加json 支持 -->
    <mvc:annotation-driven>
        <mvc:message-converters>
            <!-- 返回信息为字符串时 处理 -->
            <bean
                    class="org.springframework.http.converter.StringHttpMessageConverter"/>
            <!-- 将对象转换为json 对象 -->
            <bean
                    class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"/>
        </mvc:message-converters>
    </mvc:annotation-driven>
    <!-- 使用默认的 Servlet 来响应静态文件 -->
    <mvc:default-servlet-handler/>
    <!-- 配置视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
          id="internalResourceViewResolver">
        <!-- 前缀:在WEB-INF目录下的jsp目录下 -->
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <!-- 后缀:以.jsp结尾的资源 -->
        <property name="suffix" value=".jsp"/>
    </bean>
    <!-- 文件上传 -->
    <bean id="multipartResolver"
          class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <!-- 允许文件上传的最大尺寸 -->
        <property name="maxUploadSize">
            <value>104857600</value>
        </property>
        <!--
        设置文件放入临时文件夹的最大大小限制。
        此值是阈值,低于此值,则保存在内存中,如高于此值,则生成硬盘上的临时文件。
        -->
        <property name="maxInMemorySize">
            <value>4096</value>
        </property>
    </bean>

</beans>

配置spring.xml

在项目的 src/main/resources 下创建 spring.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:aop="http://www.springframework.org/schema/aop"
       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/aop
    http://www.springframework.org/schema/aop/spring-aop.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx.xsd">
    <!-- 扫描基本包 -->
    <context:component-scan base-package="com.msb.ssm" >
        <!-- context:exclude-filter标签:排除对某个注解的扫描 (过滤controller层) -->
        <context:exclude-filter type="annotation"
                                expression="org.springframework.stereotype.Controller"
        />
    </context:component-scan>
    <!-- 加载properties 配置文件 -->
    <context:property-placeholder location="classpath:db.properties" />
    <!-- aop -->
    <aop:aspectj-autoproxy />
    <!-- 配置c3p0 数据源 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driver}"></property>
        <property name="jdbcUrl" value="${jdbc.url}"></property>
        <property name="user" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>
    <!-- 配置事务管理器 -->
    <bean id="txManager"
          class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!-- 设置事物增强 -->
    <tx:advice id="txAdvice" transaction-manager="txManager">
        <tx:attributes>
            <tx:method name="add*" propagation="REQUIRED" />
            <tx:method name="insert*" propagation="REQUIRED" />
            <tx:method name="update*" propagation="REQUIRED" />
            <tx:method name="delete*" propagation="REQUIRED" />
        </tx:attributes>
    </tx:advice>
    <!-- aop 切面配置 -->
    <aop:config>
        <aop:pointcut id="servicePointcut"
                      expression="execution(* com.msb.ssm.service..*.*(..))" />
        <aop:advisor advice-ref="txAdvice" pointcut-ref="servicePointcut" />
    </aop:config>
    <!-- 配置 sqlSessionFactory -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"></property>
        <property name="configLocation" value="classpath:mybatis.xml" />
        <property name="mapperLocations" value="classpath:com/msb/ssm/mapper/*.xml"/>
    </bean>
    <!-- 配置扫描器 -->
    <bean id="mapperScanner"
          class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <!-- 扫描com.xxxx.ssm.dao这个包以及它的子包下的所有映射接口类 -->
        <property name="basePackage" value="com.msb.ssm.dao" />
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
    </bean>
</beans>

配置mybatis.xml

在项目的 src/main/resources 下创建 mybatis.xml 文件, 内容如下

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <typeAliases>
        <package name="com.msb.ssm.po"/>
    </typeAliases>
    <plugins>
        <plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
    </plugins>
</configuration>

配置db.properties

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssm?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false
jdbc.username=root
jdbc.password=root

添加log4j.properties

在项目的 src/main/resources 下创建 log4j.properties 文件,内容如下

log4j.rootLogger=DEBUG, Console
# Console
log4j.appender.Console=org.apache.log4j.ConsoleAppender
log4j.appender.Console.layout=org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern=%d [%t] %-5p [%c] - %m%n
log4j.logger.java.sql.ResultSet=INFO
log4j.logger.org.apache=INFO
log4j.logger.java.sql.Connection=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG

添加源码

添加包

在项目的 src/main/java 下创建对应的包结构

​ com.msb.ssm.controller

​ com.msb.ssm.service

​ com.msb.ssm.mapper

​ com.msb.ssm.dao

​ com.msb.ssm.po

添加User.java

在 com.msb.ssm.po 包下创建 JavaBean 文件 User.java (数据库字段对应如下)

image-20250104161545156

public class User {
    private Integer userId;
    private String userName;
    private String userPwd;
    private String userEmail;
    private Date createDate;
    private Date updateDate;
    /**
    set get 方法省略
    **/
}

添加UserDao.java接口

com.msb.ssm.dao 包下创建 UserDao.java 文件,提供对应的用户详情查询功能

public interface UserDao {
	User queryUserByUserId(Integer userId);
}

添加UserMapper.xml映射文件

com.msb.ssm.mapper 包下创建 UserMapper.xml 文件,提供select 查询标签配置

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.msb.ssm.dao.UserDao">
    <select id="queryUserByUserId" parameterType="Integer"
            resultType="com.msb.ssm.po.User">
        select user_id as userId,user_name as userName,user_pwd as userPwd
        from tb_user
        where user_id = #{userId}
    </select>
</mapper>

添加UserService.java

com.msb.ssm.service 包下创建UserService.java 文件,提供用户详情查询方法

@Service
public class UserService {
    //注入userDao
    @Autowired
    private UserDao userDao;

    public User queryUserByUserId(Integer userId){
        /*if (1==1){
            throw new ParamsException();
            //throw new BusinessException();
        }*/
        return userDao.queryUserByUserId(userId);
    }
}

添加HelloController.java

在 com.msb.ssm.controller 包下创建 UserController.java 文件

@Controller
public class UserController extends BaseController {
    //注入UserService
    @Autowired
    private UserService userService;

    @RequestMapping("/hello")
    public String hello(Model model){

        //设置异常
        int i=1/0;

        //自定义异常
        /*if (1==1){
            throw new ParamsException();
            //throw new BusinessException();
        }*/

        //调用userService层的方法
        User user = userService.queryUserByUserId(1);
        //将数据存到model对象中
        model.addAttribute("user", user);
        return "hello";
    }
}

添加 hello.jsp视图文件

在src/main/webapp/WEB-INF 创建jsp 目录,并在该目下创建hello.jsp ,展示查询的用户信息

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>Title</title>
  </head>
  <body>
        <h3>${user.userName},欢迎登录</h3>
  </body>
</html>

执行测试

Idea下配置jetty启动命令

image-20250104162844277

启动jetty浏览器访问http://localhost:8080/ssm/hello查看结果

RESTFUL URL

基本概念

​ 模型 - 视图 - 控制器(MVC)是一个众所周知的以设计界面应用程序为基础的设计思想。

​ Restful 风格的 API 是一种软件架构风格,设计风格而不是标准,只是提供了一组设计原则和约束条件。它主要用于客户端和服务器交互类的软件。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。

​ 在Restful 风格中,用户请求的 url 使用同一个 url,用请求方式:get,post,delete,put…等方式对请求的处理方法进行区分,这样可以在前后台分离式的开发中使得前端开发人员不会对请求的资源地址产生混淆和大量的检查方法名的麻烦,形成一个统一的接口。

​ 在 Restful 风格中,现有规定如下:

  • GET(SELECT):从服务器查询,可以在服务器通过请求的参数区分查询的方式。

  • POST(CREATE):在服务器端新建一个资源,调用 insert 操作。

  • PUT(UPDATE):在服务器端更新资源,调用 update 操作。

  • PATCH(UPDATE):在服务器端更新资源(客户端提供改变的属性)。(目前 jdk7 未实现,tomcat7不支持)。

  • DELETE(DELETE):从服务器端删除资源,调用 delete 语句。

SpringMVC支持RestFul URL风格设计

​ 案例:如何使用 Java 构造没有扩展名的RESTful url,如 /forms/1?

​ SpringMVC 是通过 @RequestMapping 及 @PathVariable 注解提供的。通过如@RequestMapping(value="/blog /{id}", method = RequestMethod.DELETE),即可处理 /blog/1 的delete 请求。

RestFul URL映射地址配置

准备环境

添加Account

​ 在 src/resources/java 对应的 com.xxxx.ssm.po 目录下新建 Account.java 实体类

public class Account {
private Integer id;
private String aname;
private String type;
private Double money;
private Integer userId;
private Date createTime;
private Date updateTime;
private String remark;
/* get set 方法省略 */
}
添加AccountDao

​ 在 src/resources/java 对应的 com.xxxx.ssm.dao 目录下新建 AccountDao.java 接口类

public interface AccountDao {
    public Account selectById(Integer id);
    public int save(Account account);
    public int update(Account account);
    public int delete(Integer id);
}
添加AccountMapper

​ 在 src/resources/java 对应的 com.xxxx.ssm.mapper 目录下新建 AccountMapper.xml 映射文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.xxxx.ssm.dao.AccountDao" >
<resultMap id="BaseResultMap" type="com.xxxx.ssm.po.Account" >
    <id column="id" property="id" jdbcType="INTEGER" />
    <result column="aname" property="aname" jdbcType="VARCHAR" />
    <result column="type" property="type" jdbcType="VARCHAR" />
    <result column="money" property="money" jdbcType="DOUBLE" />
    <result column="user_id" property="userId" jdbcType="INTEGER" />
    <result column="create_time" property="createTime" jdbcType="DATE" />
    <result column="update_time" property="updateTime" jdbcType="DATE" />
    <result column="remark" property="remark" jdbcType="VARCHAR" />
</resultMap>
    <sql id="Base_Column_List" >
    id, aname, type, money, user_id, create_time, update_time, remark
    </sql>
	<!-- 查询操作 -->
    <select id="selectById" resultMap="BaseResultMap" parameterType="java.lang.Integer">
        select
        <include refid="Base_Column_List" />
        from tb_account
        where id = #{id,jdbcType=INTEGER}
	</select>
    <!-- 删除操作 -->
    <delete id="delete" parameterType="java.lang.Integer" >
        delete from tb_account
        where id = #{id,jdbcType=INTEGER}
    </delete>
    <!-- 添加操作 -->
    <insert id="save" parameterType="com.xxxx.ssm.po.Account" >
    	insert into tb_account
    	<trim prefix="(" suffix=")" suffixOverrides="," >
    		<if test="id != null" >
    			id,
    		</if>
            <if test="aname != null" >
            	aname,
            </if>
            <if test="type != null" >
            	type,
            </if>
            <if test="money != null" >
            	money,
            </if>
            <if test="userId != null" >
            	user_id,
            </if>
            <if test="createTime != null" >
            	create_time,
            </if>
            <if test="updateTime != null" >
            	update_time,
            </if>
            <if test="remark != null" >
            	remark,
            </if>
        </trim>
        <trim prefix="values (" suffix=")" suffixOverrides="," >
            <if test="id != null" >
            	#{id,jdbcType=INTEGER},
            </if>
            <if test="aname != null" >
            	#{aname,jdbcType=VARCHAR},
            </if>
            <if test="type != null" >
            	#{type,jdbcType=VARCHAR},
            </if>
            <if test="money != null" >
            	#{money,jdbcType=DOUBLE},
            </if>
            <if test="userId != null" >
            	#{userId,jdbcType=INTEGER},
    		</if>
            <if test="createTime != null" >
            	#{createTime,jdbcType=DATE},
            </if>
            <if test="updateTime != null" >
            	#{updateTime,jdbcType=DATE},
            </if>
            <if test="remark != null" >
            	#{remark,jdbcType=VARCHAR},
            </if>
        </trim>
	</insert>
    <!-- 更新操作 -->
    <update id="update" parameterType="com.xxxx.ssm.po.Account" >
    	update tb_account
    	<set >
            <if test="aname != null" >
            	aname = #{aname,jdbcType=VARCHAR},
            </if>
            <if test="type != null" >
            	type = #{type,jdbcType=VARCHAR},
            </if>
            <if test="money != null" >
            	money = #{money,jdbcType=DOUBLE},
            </if>
            <if test="userId != null" >
            	user_id = #{userId,jdbcType=INTEGER},
            </if>
            <if test="createTime != null" >
            	create_time = #{createTime,jdbcType=DATE},
            </if>
            <if test="updateTime != null" >
            	update_time = #{updateTime,jdbcType=DATE},
            </if>
            <if test="remark != null" >
            	remark = #{remark,jdbcType=VARCHAR},
            </if>
        </set>
    	where id = #{id,jdbcType=INTEGER}
    </update>
</mapper>
添加AccountService

​ 在 src/resources/java 对应的 com.xxxx.ssm.service 目录下新建AccountService.java

@Service
public class AccountService {
    @Autowired
    private AccountDao accountDao;
    
    public Account selectById(Integer id){
    	return accountDao.selectById(id);
    }
    
    public int saveAccount(Account account){
        return accountDao.save(account);
    }
    
    public int updateAccount(Account account){
    	return accountDao.update(account);
    }
    
    public int delAccount(Integer id){
    	return accountDao.delete(id);
    }
}

URL映射地址配置

Get请求配置
    /**
     * restful --> get 请求,执行查询操作
     *      传统的URL 访问
     *          @RequestMapping("/account/queryAccountById")
     *          http://localhost:8080/ssm/account/queryAccountById?id=1
     *      RESTFUL URL 访问
     *          @GetMapping("/account/{id}")
     *          http://localhost:8080/ssm/account/{id}
     *      @PathVariable 将形参设置为参数路径,声明在形参前面
     *
     * @param id
     * @return
     */
    //@RequestMapping("/account/queryAccountById")
    @GetMapping("/account/{id}")
    @ResponseBody
    public Account queryAccountById(@PathVariable Integer id) {
        return accountService.selectById(id);
    }
Delete请求配置
/**
     * restful-->delete 请求 执行删除操作
     * @param id
     * @return
     */
    @DeleteMapping("/account/{id}")
    @ResponseBody
    public Map<String,String> deleteAccountById(@PathVariable Integer id) {
        //调用service层的删除方法,返回受影响的行数
        int row= accountService.delAccount(id);

        //判断受影响的行数是否大于0
        Map<String,String> map = new HashMap<>();
        if(row>0){
            //删除成功
            map.put("code","200");
            map.put("msg","删除成功");
        }else {
            //删除失败
            map.put("code","500");
            map.put("msg","删除失败");
        }
        return map;
    }
Post请求配置
/**
     * restful --> post 请求,执行添加操作
     * @param account
     * @return
     */
    @PostMapping("/account")
    @ResponseBody
    public Map<String,String> addAccount(@RequestBody Account account) {
        Map<String,String> map = new HashMap<>();

        //调用service层的添加方法,返回受影响的行数
        int row=accountService.saveAccount(account);
        //判断受影响的行数是否大于0
        if(row>0){
            //添加成功
            map.put("code","200");
            map.put("msg","添加成功");
        }else {
            //添加失败
            map.put("code","500");
            map.put("msg","添加失败");
        }
        return map;
    }
Put请求配置
/**
     * restful-->put 请求执行更新操作
     * @param account
     * @return
     */
    @PutMapping("/account")
    @ResponseBody
    public Map<String,String> updateAccount(@RequestBody Account account) {
        Map<String,String> map = new HashMap<>();

        //调用service层的添加方法,返回受影响的行数
        int row=accountService.updateAccount(account);
        //判断受影响的行数是否大于0
        if(row>0){
            //更新成功
            map.put("code","200");
            map.put("msg","更新成功");
        }else {
            //添加失败
            map.put("code","500");
            map.put("msg","更新失败");
        }
        return map;
    }

全局异常

全局异常概念

​ 在 JavaEE 项目的开发中,不管是对底层的数据库操作过程,还是业务层的处理过程,还是控制层的处理过程,都不可避免会遇到各种可预知的、不可预知的异常需要处理。每个过程都单独处理异常,系统的代码耦合度高,工作量大且不好统一,维护的工作量也很大。

​ SpringMVC 对于异常处理这块提供了支持,通过 SpringMVC 提供的全局异常处理机制,能够将所有类型的异常处理从各处理过程解耦出来,既保证了相关处理过程的功能较单一,也实现了异常信息的统一处理和维护。

​ 全局异常实现方式 Spring MVC 处理异常有 3 种方式:

  1. 使用 Spring MVC 提供的简单异常处理器 SimpleMappingExceptionResolver

  2. 实现 Spring 的异常处理接口 HandlerExceptionResolver 自定义自己的异常处理器

  3. 使用 @ExceptionHandler 注解实现异常处理

异常处理实现

全局异常处理方式一

配置简单异常处理器

​ 配置 SimpleMappingExceptionResolver 对象

<!--
        全局异常统一处理
            1. 使用 Spring MVC 提供的简单异常处理器 SimpleMappingExceptionResolver
            2. 实现 Spring 的异常处理接口 HandlerExceptionResolver 自定义自己的异常处理器
            3. 使用 @ExceptionHandler 注解实现异常处理
    -->
    <!--配置全局异常统一处理的bean对象(简单异常处理器) 处理的是视图的异常(json格式的异常不能处理)-->
    <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
        <!--页面在转发时出现异常,设置默认的错误页面-->
        <property name="defaultErrorView" value="error"/>
        <!--异常发生时,设置异常的变量名-->
        <property name="exceptionAttribute" value="ex"/>
        <!--设置自定义异常与页面的映射-->
        <!--<property name="exceptionMappings">
        <props>
            &lt;!&ndash;key代表的是自定义异常的路径,标签中设置的是具体的页面&ndash;&gt;
            <prop key="com.msb.ssm.exception.ParamsException">params_error</prop>
            <prop key="com.msb.ssm.exception.BusinessException">business_error</prop>

        </props>
    </property>-->
    </bean>

​ 可以在处理异常的页面获取异常信息

${ex}
使用自定义异常

​ 参数异常

/**
 * 自定义异常:参数异常
 */
public class ParamsException extends RuntimeException {
    private Integer code = 300;
    private String msg = "参数异常!";
    public ParamsException() {
        super("参数异常!");
    }
    public ParamsException(String msg) {
        super(msg);
        this.msg = msg;
    }
    public ParamsException(Integer code) {
        super("参数异常!");
        this.code = code;
    }
    public ParamsException(Integer code, String msg) {
        super(msg);
        this.code = code;
        this.msg = msg;
    }
    public Integer getCode() {
        return code;
    }
    public void setCode(Integer code) {
        this.code = code;
    }
    public String getMsg() {
        return msg;
    }
    public void setMsg(String msg) {
        this.msg = msg;
    }
}

​ 业务异常

/**
 * 自定义异常:业务异常
 */
public class BusinessException extends RuntimeException {
    private Integer code=400;
    private String msg="业务异常!";
    public BusinessException() {
        super("业务异常!");
    }
    public BusinessException(String msg) {
        super(msg);
        this.msg = msg;
    }
    public BusinessException(Integer code) {
        super("业务异常!");
        this.code = code;
    }
    public BusinessException(Integer code, String msg) {
        super(msg);
        this.code = code;
        this.msg = msg;
    }
    public Integer getCode() {
        return code;
    }
    public void setCode(Integer code) {
        this.code = code;
    }
    public String getMsg() {
        return msg;
    }
    public void setMsg(String msg) {
        this.msg = msg;
    }
}
设置自定义异常与页面的映射
<!--设置自定义异常与页面的映射-->
        <property name="exceptionMappings">
            <props>
                <!--key代表的是自定义异常的路径,标签中设置的是具体的页面-->
                <prop key="com.msb.ssm.exception.ParamsException">params_error</prop>
                <prop key="com.msb.ssm.exception.BusinessException">business_error</prop>

            </props>
        </property>

​ 使用 SimpleMappingExceptionResolver 进行异常处理,具有集成简单、有良好的扩展性、对已有代码没有入侵性等优点,但该方法仅能获取到异常信息,若在出现异常时,对需要获取除异常以外的数据的情况不适用。

​ 如果Controller返回的是json格式的数据时(非视图页面时),当出现异常时,会转发到异常页面,该方式改变了Controller方法的返回类型。

全局异常处理方式二(推荐)

实现HandlerExceptionResolver接口
//@Component
public class GlobalExceptionResolver implements HandlerExceptionResolver {
    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        ModelAndView modelAndView = new ModelAndView("error");
        modelAndView.addObject("ex","默认的错误信息");     
        return modelAndView;
    }
}
自定义异常处理
//@Component
public class GlobalExceptionResolver implements HandlerExceptionResolver {
    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        ModelAndView modelAndView = new ModelAndView("error");

        modelAndView.addObject("ex","默认的错误信息");

        //自定义异常
        if(ex instanceof ParamsException) {
            //设置参数异常的页面
            modelAndView.setViewName("params_error");
            ParamsException exception = (ParamsException) ex;
            modelAndView.addObject("ex",exception.getMsg());
        }

        //自定义异常处理
        if(ex instanceof BusinessException) {
            //设置参数异常的页面
            modelAndView.setViewName("business_error");
            BusinessException exception = (BusinessException) ex;
            modelAndView.addObject("ex",exception.getMsg());
        }

        /*//处理json格式
        *//**
         * 判断handler返回的结果,视图/json
         *//*
        response.getWriter().write("json");
        return  null;*/

        return modelAndView;
    }
}

​ 使用实现 HandlerExceptionResolver 接口的异常处理器进行异常处理,具有集成简单、有良好的扩展性、对已有代码没有入侵性等优点,同时,在异常处理时能获取导致出现异常的对象,有利于提供更详细的异常处理信息。

全局异常处理方式三

​ 页面处理器继承 BaseController

/**
 * 父类,页面处理器需要继承父类(父类中处理异常的方法需要添加注解)
 */
public class BaseController {

    @ExceptionHandler
    public String exc(HttpServletRequest req, HttpServletResponse response, Exception ex){

        req.setAttribute("ex", ex);

        //自定义异常
        if(ex instanceof ParamsException){
            return "params_error";
        }

        if(ex instanceof BusinessException){
            return "business_error";
        }

        return "error";
    }
}

​ 使用 @ExceptionHandler 注解实现异常处理,具有集成简单、有扩展性好(只需要将要异常处理的Controller 类继承于 BaseController 即可)、不需要附加Spring 配置等优点,但该方法对已有代码存在入侵性(需要修改已有代码,使相关类继承于 BaseController),在异常处理时不能获取除异常以外的数据。

未捕获异常的处理

​ 对于 Unchecked Exception 而言,由于代码不强制捕获,往往被忽略,如果运行期产生了Unchecked Exception,而代码中又没有进行相应的捕获和处理,则我们可能不得不面对尴尬的 404、 500……等服务器内部错误提示页面。

此时需要一个全面而有效的异常处理机制。目前大多数服务器也都支持在 web.xml 中通过(Websphere/Weblogic)或者(Tomcat)节点配置特定异常情况的显示页面。修改 web.xml 文件,增加以下内容:

 <!--异常处理-->
  <!-- 出错页面定义 -->
  <error-page>
    <exception-type>java.lang.Throwable</exception-type>
    <location>/500.jsp</location>
  </error-page>
  <error-page>
    <error-code>404</error-code>
    <location>/404.jsp</location>
  </error-page>
  <error-page>
    <error-code>500</error-code>
    <location>/500.jsp</location>
  </error-page>

© 2024 - 2025 雪中烛

 

我是谁

我是MyraZ.

👉百度

Me

Cut out summary from your post content here.

The remaining content of your post.

Desc2

Cut out summary from your post content here.

The remaining content of your post.