目 录CONTENT

文章目录

SpringMVC 学习笔记

Nicholas
2024-05-14 / 0 评论 / 0 点赞 / 9 阅读 / 65296 字

SpringMVC学习笔记

SpringMVC官方文档】https://docs.spring.io/spring-framework/reference/web/webmvc.html

一、SpringMVC简介

Spring MVCSpring Framework的一部分,是基于Java实现MVC的轻量级Web框架。

Spring MVC的特点:

  • 轻量级,简单易学
  • 高效,基于请求响应的MVC框架
  • Spring兼容性好,无缝结合
  • 约定优于配置
  • 功能强大:RESTful、数据验证、格式化、本地化、主题等
  • 简洁灵活

Springweb框架围绕DispatcherServlet设计。SpringMVC 工作原理图如下**【重要!!】**。

20240514131337_415037.png

1、DispatcherServlet表示前置控制器,是整个SpringMVC的控制中心。用户发出请求,DispatcherServlet接收请求并拦截请求。

  • 我们假设请求的url为:http://localhost:8080/SpringMVC/hello
  • url拆分成三部分:http://localhost:8080为服务器域名;SpringMVC为部署在服务器上的web站点;hello为控制器
  • 因此,如上url表示:请求位于服务器localhost:8080上的SpringMVC站点的hello控制器。

2、HandlerMapping为处理器映射。DispatcherServlet调用HandlerMappingHandlerMapping根据请求url查找Handler

3、DispatcherServlet 调用 HandlerAdapter适配器执行 Handler

4、Handler 完成对用户请求的处理后,会返回一个 ModelAndView 对象给DispatcherServletModelAndView 顾名思义,包含了数据模型以及相应的视图的信息。Model 是返回的数据对象,View 是个逻辑上的 View

5、ViewResolver 会根据逻辑 View 查找实际的 View

6、DispaterServlet 把返回的 Model 传给 View(视图渲染)。

7、把 View 返回给请求者(浏览器)。

二、第一个SpringMVC项目

2.1、配置版本

项目的总体结构如下。

20240514131337_437143.png

2.1.1、创建module

新建一个modulespringmvc-02-hellomvc,添加web支持。

确定导入了SpringMVC的依赖。

<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.1.9.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>servlet-api</artifactId>
        <version>2.5</version>
    </dependency>
    <dependency>
        <groupId>javax.servlet.jsp</groupId>
        <artifactId>jsp-api</artifactId>
        <version>2.2</version>
    </dependency>
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>jstl</artifactId>
        <version>1.2</version>
    </dependency>
</dependencies>

2.1.2、配置web.xml

配置web.xml,注册DispatcherServlet

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <!-- Register a DispatcherServlet: SpringMVC's core component -->
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

        <!-- associate a springmvc's configuration file:【servlet-name】- servlet.xml-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc-servlet.xml</param-value>
        </init-param>
        <!-- start grade - 1 : together with the server -->
        <load-on-startup>1</load-on-startup>
    </servlet>

    <!-- 
		/ fit all requests (exclude .jsp pages)
         /* fit all requests (include .jsp pages) 
		一般使用 / 即可,因为我们不希望匹配 .jsp 的时候再加前后缀(变成 .jsp.jsp)
	-->
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <!-- filter supplied by Spring -->
    <filter>
        <filter-name>encoding</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>encoding</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>

2.1.3、servlet配置文件

编写SpringMVC的配置文件。名称:springmvc-servlet.xml[servletname]-servlet.xml

【说明】这里的命名格式是按照官方的。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 处理映射器(可省略,即使用默认) -->
    <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
    <!-- 处理适配器(可省略,即使用默认) -->
    <bean  class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>

    <!-- 视图解析器 : DispatcherServlet 给它 ModelAndView
		1、获取 ModelAndView
		2、解析 ModelAndView 中视图名字
		3、拼接视图名字,找到对应的视图
		4、将数据渲染到视图
	-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
          id="InternalResourceViewResolver">
        <!-- prefix -->
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <!-- postfix -->
        <property name="suffix" value=".jsp"/>
    </bean>

    <!-- Handler (BeanNameUrlHandlerMapping 需要的 bean) -->
    <bean id="/hello" class="com.wzq.controller.HelloController"/>
</beans>

2.1.4、编写controller

可以使用实现Controller接口,或者使用注解这两种方式。

这里使用实现接口的方式(缺点是一个控制器中只有一个方法)。

package com.wzq.controller;

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

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

// 实现 Controller 接口,说明这就是一个控制器
public class HelloController implements Controller {
    // 处理请求并返回一个 ModelAndView 对象
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
        // ModelAndView 模型和视图
        ModelAndView mv = new ModelAndView();
        // encapsulate Object, put into ModelAndView
        mv.addObject("msg", "HelloSpringMVC!");
        // encapsulate the View we need to jump to, put into ModelAndView
        mv.setViewName("hello"); //: /WEB-INF/jsp/hello.jsp
        return mv;
    }
}

2.1.5、编写jsp页面

编写jsp页面,并显示ModelandView存放的数据。

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    ${msg}
</body>
</html>

2.1.6、运行结果

tomcat中添加当前模块并配置访问路径。

20240514131337_453130.png

运行结果如下所示。

20240514131337_468130.png

【注】如果出现404问题,可以检查一下IDEA的项目结构中,是不是缺少了lib目录和其中的依赖,如果缺少了的话可以手动添加。然后clean一下重新启动即可。

20240514131337_529319.png

2.2、注解版本

项目的总体结构如下。

20240514131337_483130.png

2.2.1、创建module

新建一个modulespringmvc-04-annotation,添加web支持。

确定导入了SpringMVC的依赖。

<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.1.9.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>servlet-api</artifactId>
        <version>2.5</version>
    </dependency>
    <dependency>
        <groupId>javax.servlet.jsp</groupId>
        <artifactId>jsp-api</artifactId>
        <version>2.2</version>
    </dependency>
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>jstl</artifactId>
        <version>1.2</version>
    </dependency>
</dependencies>

2.2.2、配置web.xml

配置web.xml,注册DispatcherServlet

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <!-- Register a DispatcherServlet: SpringMVC's core component -->
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

        <!-- associate a springmvc's configuration file:【servlet-name】- servlet.xml-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc-servlet.xml</param-value>
        </init-param>
        <!-- start grade - 1 : together with the server -->
        <load-on-startup>1</load-on-startup>
    </servlet>

    <!-- 
		/ fit all requests (exclude .jsp pages)
         /* fit all requests (include .jsp pages) 
		一般使用 / 即可,因为我们不希望匹配 .jsp 的时候再加前后缀(变成 .jsp.jsp)
	-->
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <!-- filter supplied by Spring -->
    <filter>
        <filter-name>encoding</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>encoding</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>

2.2.3、servlet配置文件

编写SpringMVC的配置文件。名称:springmvc-servlet.xml[servletname]-servlet.xml

【说明】这里的命名格式是按照官方的。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        https://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!-- 自动扫描指定的包,下面所有注解类交给 IOC 容器统一管理 -->
    <context:component-scan base-package="com.wzq.controller"/>
    <!-- 设置 SpringMVC 不处理静态资源(可省略,即使用默认) -->
    <mvc:default-servlet-handler />
    <!-- 注解驱动(可省略,即使用默认)
         在 spring 中一般采用 @RequestMapping 注解来完成映射关系
         要想使 @RequestMapping 注解生效
         必须向上下文中注册 DefaultAnnotationHandlerMapping
         和 AnnotationMethodHandlerAdapter 实例
         这两个实例分别在类级别和方法级别处理。
         而 annotation-driven 配置帮助我们自动完成上述两个实例的注入
    -->
    <mvc:annotation-driven />

    <!-- 视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/" />
        <property name="suffix" value=".jsp" />
    </bean>
</beans>

在视图解析器中我们把所有的视图都存放在/WEB-INF/目录下,这样可以保证视图安全,因为这个目录下的文件客户端不能直接访问。

2.2.4、编写controller

可以使用实现Controller接口,或者使用注解这两种方式。这里使用实现接口的方式。

package com.wzq.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("/hello")
public class HelloController {

    @RequestMapping("/h1")
    public String hello(Model model){
        // 封装数据(也可以使用 ModelMap 类)
        model.addAttribute("msg", "Hello, SpringMVC Annotation!");
        return "hello";  // 视图名字,会被视图解析器处理
    }
}

@Controller是为了让Spring IOC容器初始化时自动扫描到。(Spring可以使用扫描机制来找到应用程序中所有基于注解的控制器类,为了保证Spring能找到你的控制器,需要在配置文件中声明component-scan)。

@RequestMapping是为了映射请求路径,这里因为类与方法上都有映射所以访问时应该是/hello/h1

2.2.5、编写jsp页面

编写jsp页面,并显示从Controller带回的信息(通过EL表达式)。

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    ${msg}
</body>
</html>

2.2.6、运行结果

运行结果如下所示。

20240514131337_498567.png

三、RestFul风格(重要)

RestFul就是一个资源定位及资源操作的风格。不是标准也不是协议,只是一种风格。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。

package com.wzq.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;

@Controller
public class RestFulController {
    // 原来: http://localhost:8080/add?a=1&b=2
    @RequestMapping("/add")
    public String test1(int a, String b, Model model){
        String res = a + b;
        model.addAttribute("msg", "结果为" + res);
        return "test";
    }

    // Restful: http://localhost:8080/add/1/2
    // @RequestMapping(value = "/add/{a}/{b}", method = RequestMethod.GET)
    @GetMapping("/add/{a}/{b}")
    public String test2(@PathVariable int a, @PathVariable String b, Model model){
        String res = a + b;
        model.addAttribute("msg", "结果1为" + res);
        return "test";
    }

    // @RequestMapping(value = "/add/{a}/{b}", method = RequestMethod.POST)
    @PostMapping("/add/{a}/{b}")
    public String test3(@PathVariable int a, @PathVariable String b, Model model){
        String res = a + b;
        model.addAttribute("msg", "结果2为" + res);
        return "test";
    }
}

SpringMVC中可以使用 @PathVariable 注解,让方法参数的值对应绑定到一个URL模板变量上。

20240514131337_514079.png

四、转发和重定向

4.1、Servlet API

通过使用ServletAPI进行转发和重定向,不需要视图解析器。

@Controller
public class ResultGo {
    // 输出
    @RequestMapping("/result/t1")
    public void test1(HttpServletRequest req, HttpServletResponse rsp)
        throws IOException {
        rsp.getWriter().println("Hello, Spring by servlet API");
    }
    // 重定向
    @RequestMapping("/result/t2")
    public void test2(HttpServletRequest req, HttpServletResponse rsp)
        throws IOException {
        rsp.sendRedirect("/index.jsp");
    }
    // 转发
    @RequestMapping("/result/t3")
    public void test3(HttpServletRequest req, HttpServletResponse rsp)
        throws Exception {  
        req.setAttribute("msg", "/result/t3");
        req.getRequestDispatcher("/WEB-INF/jsp/test.jsp").forward(req, rsp);
    }
}

4.2、SpringMVC

4.2.1、无视图解析器版

通过SpringMVC来实现转发和重定向——无需视图解析器版。

@Controller
public class ResultSpringMVC {
    @RequestMapping("/rsm/t1")
    public String test1(){
        // 转发
        return "/index.jsp";
    }
    @RequestMapping("/rsm/t2")
    public String test2(){
        // 转发二
        return "forward:/index.jsp";
    }
    @RequestMapping("/rsm/t3")
    public String test3(){
        // 重定向
        return "redirect:/index.jsp";
    }
}

4.2.2、需视图解析器版

通过SpringMVC来实现转发和重定向——需要视图解析器版。

@Controller
public class ResultSpringMVC2 {
    @RequestMapping("/rsm2/t1")
    public String test1(){
        // 转发
        return "test";
    }
    @RequestMapping("/rsm2/t2")
    public String test2(){
        // 重定向(和无需视图解析器版一样)
        return "redirect:/index.jsp";
    }
}

五、数据处理

接收前端传递参数的两种方式:

  • 1、接收前端用户传递的参数,判断参数的名字,假设名字直接在方法上,可以直接使用。
  • 2、假设传递的是一个对象User,匹配User对象中的字段名。如果名字一致则OK,否则匹配不到的字段为null
package com.wzq.controller;

import com.wzq.pojo.User;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
@RequestMapping("/user")
public class UserController {
    // localhost:8080/user/t1?username=xxx
    @GetMapping("/t1")
    public String test1(@RequestParam("username") String name, Model model){
        // 接收前端参数
        System.out.println("接收到前端的参数为: " + name);
        // 将返回的结果传递给前端
        model.addAttribute("msg", name);
        // 视图跳转
        return "test";
    }

    // localhost:8080/user/t2?id=x&name=xxx&age=xx
    @GetMapping("/t2")
    public String test2(User user){
        System.out.println(user);  // User(id=8, name=little, age=24)
        return "test";
    }
}

User类的定义如下。

package com.wzq.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private int id;
    private String name;
    private int age;
}

六、过滤器

以下给出使用过滤器解决乱码问题的示例。

前端表单提交页面如下。

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>

<form action="/e/t1" method="post">
    <input type="text" name="name">
    <input type="submit">
</form>

</body>
</html>

6.1、自拟过滤器

package com.wzq.filter;

import javax.servlet.*;
import java.io.IOException;

public class EncodingFilter implements Filter {
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        request.setCharacterEncoding("utf-8");
        response.setCharacterEncoding("utf-8");
        chain.doFilter(request, response);
    }

    public void destroy() {
    }
}

6.2、注册过滤器

web.xml文件中,注册过滤器。此时过滤器可以过滤到所有的请求和jsp页面。

<!-- 注册自拟过滤器 -->
<!--    <filter>-->
<!--        <filter-name>encoding</filter-name>-->
<!--        <filter-class>com.wzq.filter.EncodingFilter</filter-class>-->
<!--    </filter>-->
<!--    <filter-mapping>-->
<!--        <filter-name>encoding</filter-name>-->
<!--        <url-pattern>/*</url-pattern>-->
<!--    </filter-mapping>-->

<!-- 注册 Spring 提供的过滤器 -->
<filter>
    <filter-name>encoding</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>encoding</filter-name>
    <!-- 注意,使用 /* 才能过滤到 jsp 页面 -->
    <url-pattern>/*</url-pattern>
</filter-mapping>

七、JSON数据返回

JSONJavaScript Object NotationJS 对象标记)是一种轻量级的数据交换格式,使用特别广泛。

  • 采用完全独立于编程语言的文本格式来存储和表示数据。
  • 简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。
  • 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。

7.1、jackson使用

导入jackson依赖。

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.16.1</version>
</dependency>

乱码问题配置(springmvc-servlet.xml文件中)。

<!-- JSON error code problem solve -->
<mvc:annotation-driven>
    <mvc:message-converters register-defaults="true">
        <bean class="org.springframework.http.converter.StringHttpMessageConverter">
            <constructor-arg value="UTF-8"/>
        </bean>
        <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
            <property name="objectMapper">
                <bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
                    <property name="failOnEmptyBeans" value="false"/>
                </bean>
            </property>
        </bean>
    </mvc:message-converters>
</mvc:annotation-driven>

也可以直接通过@RequestMapingproduces属性来解决乱码。

// produces: 指定响应体返回类型和编码
@RequestMapping(value = "/j11", produces ="application/json;charset=utf-8")

Controller中使用。

package com.wzq.controller;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.wzq.pojo.User;
import com.wzq.utils.JsonUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@RestController   // 此类中所有的方法返回的都是字符串,不走视图解析器
public class UserController {

    @RequestMapping("/j11")
    // @ResponseBody  // 不走视图解析器,会直接返回一个字符串(配合 Controller 使用)
    public String json11() throws JsonProcessingException {
        User user = new User("你好", 3, "male");
        return user.toString();
    }

    @RequestMapping("/j12")
    public String json12() throws JsonProcessingException {
        // 使用 jackson 包的 ObjectMapper 类
        ObjectMapper mapper = new ObjectMapper();
        User user = new User("你好", 3, "male");
        return mapper.writeValueAsString(user);
    }

    @RequestMapping("/j2")
    public String json2() throws JsonProcessingException {
        ObjectMapper mapper = new ObjectMapper();

        List<User> userList = new ArrayList<User>();
        User user1 = new User("你好1", 3, "male");
        User user2 = new User("你好2", 3, "male");
        User user3 = new User("你好3", 3, "male");
        User user4 = new User("你好4", 3, "male");

        userList.add(user1);
        userList.add(user2);
        userList.add(user3);
        userList.add(user4);

        return mapper.writeValueAsString(userList);
    }

    @RequestMapping("/j3")
    public String json3() throws JsonProcessingException {
        ObjectMapper mapper = new ObjectMapper();

        // 使用 ObjectMapper 来格式化输出,关闭时间戳
        mapper.configure(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS, false);
        // 自定义日期格式
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        mapper.setDateFormat(sdf);

        return mapper.writeValueAsString(new Date());
    }
}

7.2、fastjson2使用

导入fastjson2依赖。

<!-- https://mvnrepository.com/artifact/com.alibaba.fastjson2/fastjson2 -->
<dependency>
    <groupId>com.alibaba.fastjson2</groupId>
    <artifactId>fastjson2</artifactId>
    <version>2.0.47</version>
</dependency>

Controller中使用。

@RequestMapping("/j6")
public String json6() {
    List<User> userList = new ArrayList<User>();
    User user1 = new User("你好1", 3, "male");
    User user2 = new User("你好2", 3, "male");
    User user3 = new User("你好3", 3, "male");
    User user4 = new User("你好4", 3, "male");

    userList.add(user1);
    userList.add(user2);
    userList.add(user3);
    userList.add(user4);

    return JSON.toJSONString(userList);  // 转化成 JSON 字符串格式
}

八、SSM框架整合案例(重点)

整体最终项目结构如下。

SSMbuild
- src
  -- main
    --- java
      * com.wzq
        ** controller
          *** BookController.java   // controller 实现
        ** dao
          *** BookMapper.java     // 数据库表操作接口
          *** BookMapper.xml      // 数据库表操作实现
        ** pojo
          *** Books.java          // 实体类,对应数据库 books 表
        ** service
          *** BookService.java        // service 接口
          *** BookServiceImpl.java    // service 实现类
    --- resources
      * applicationContext.xml    // Spring 核心配置文件
      * database.properties           // 数据库配置文件
      * mybatis-config.xml        // mybatis 核心配置文件
      * spring-dao.xml                // spring 连接 dao 层配置
      * spring-mvc.xml                // spring-mvc 配置
      * spring-service.xml            // spring 连接 service 层配置
  -- test
    --- java
      * MyTest.java    // 单元测试类
- web
  -- statics
    --- bootstrap-3.3.7.min.css
  -- WEB-INF
    --- jsp
      * addBook.jsp
      * allBook.jsp
      * updateBook.jsp
    --- web.xml
  -- index.jsp     // 首页
- pom.xml    // maven 项目文件

8.1、数据库环境搭建

数据库创建,详见ssm_build.sql文件。

book_id  book_name  book_counts  detial
1        Java       1            Java核心技术
2        MySQL      4            MySQL核心技术
3        Linux      10           操作系统技术

8.2、项目基本环境搭建

创建Maven项目,导入依赖,并配置静态文件过滤。

<!-- dependency -->
<dependencies>
    <!-- junit -->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
    <!-- database driver -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.47</version>
    </dependency>
    <!-- database connect pool: c3p0 -->
    <dependency>
        <groupId>com.mchange</groupId>
        <artifactId>c3p0</artifactId>
        <version>0.9.5.4</version>
    </dependency>
    <!-- servlet -->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>servlet-api</artifactId>
        <version>2.5</version>
    </dependency>
    <dependency>
        <groupId>javax.servlet.jsp</groupId>
        <artifactId>jsp-api</artifactId>
        <version>2.2</version>
    </dependency>
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>jstl</artifactId>
        <version>1.2</version>
    </dependency>
    <!-- Mybatis -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.2</version>
    </dependency>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
        <version>2.0.2</version>
    </dependency>
    <!-- Spring -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.1.9.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>5.1.9.RELEASE</version>
    </dependency>
    <!-- Lombok -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.16.10</version>
    </dependency>
    <!-- aop -->
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.8.13</version>
    </dependency>
</dependencies>

<!-- static source output problem -->
<build>
    <resources>
        <resource>
            <directory>src/main/java</directory>
            <includes>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <filtering>false</filtering>
        </resource>
        <resource>
            <directory>src/main/resources</directory>
            <includes>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <filtering>false</filtering>
        </resource>
    </resources>
</build>

8.3、Mybatis部分实现

8.3.1、数据库配置文件

数据库配置文件编写。

jdbc.Driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/ssm_build?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
jdbc.username=root
jdbc.password=******

8.3.2、实体类

实体类Books.java创建(可使用lombok简化)。

package com.wzq.pojo;

public class Books {

    private int bookID;
    private String bookName;
    private int bookCounts;
    private String detail;

    public Books() {
    }

    public Books(int bookID, String bookName, int bookCounts, String detail) {
        this.bookID = bookID;
        this.bookName = bookName;
        this.bookCounts = bookCounts;
        this.detail = detail;
    }

    public int getBookID() {
        return bookID;
    }

    public void setBookID(int bookID) {
        this.bookID = bookID;
    }

    public String getBookName() {
        return bookName;
    }

    public void setBookName(String bookName) {
        this.bookName = bookName;
    }

    public int getBookCounts() {
        return bookCounts;
    }

    public void setBookCounts(int bookCounts) {
        this.bookCounts = bookCounts;
    }

    public String getDetail() {
        return detail;
    }

    public void setDetail(String detail) {
        this.detail = detail;
    }

    @Override
    public String toString() {
        return "Books{" +
                "bookID=" + bookID +
                ", bookName='" + bookName + '\'' +
                ", bookCounts=" + bookCounts +
                ", detail='" + detail + '\'' +
                '}';
    }
}

8.3.3、数据库操作接口

创建数据库操作接口BookMapper.java

package com.wzq.dao;

import com.wzq.pojo.Books;
import org.apache.ibatis.annotations.Param;

import java.util.List;

public interface BookMapper {

    // 增加一本书
    int addBook(Books books);

    // 删除一本书
    int deleteBookById(@Param("bookID") int id);

    // 更新一本书
    int updateBook(Books books);

    // 查询一本书
    Books queryBookById(@Param("bookID") int id);

    // 查询全部的书
    List<Books> queryAllBook();

    // 根据书名查询一本书
    Books queryBookByName(@Param("bookName") String bookName);
}

8.3.4、Mapper实现

创建BookMapper.xml文件,编写实际的SQL语句。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.wzq.dao.BookMapper">

    <insert id="addBook" parameterType="Books">
        insert into ssm_build.books (book_name, book_counts, detail)
        VALUES (#{bookName}, #{bookCounts}, #{detail});
    </insert>

    <delete id="deleteBookById" parameterType="int">
        delete from ssm_build.books where book_id = #{bookID};
    </delete>

    <update id="updateBook" parameterType="Books">
        update ssm_build.books
        set book_name = #{bookName}, book_counts = #{bookCounts}, detail = #{detail}
        where book_id = #{bookID};
    </update>

    <select id="queryBookById" resultType="Books">
        select * from ssm_build.books where book_id = #{bookID};
    </select>

    <select id="queryAllBook" resultType="Books">
        select * from ssm_build.books;
    </select>

    <select id="queryBookByName" resultType="Books">
        select * from ssm_build.books where book_name = #{bookName};
    </select>
</mapper>

8.3.5、Mybatis配置文件

Mybatis核心配置文件编写,注册mapper

<?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">

<!-- Mybatis' core configuration file-->
<configuration>

    <!-- config data source (now let Spring to do) -->

    <!--  设置部分  -->
    <settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
        <setting name="mapUnderscoreToCamelCase" value="true" />
    </settings>

    <!--  别名设置  -->
    <typeAliases>
        <package name="com.wzq.pojo"/>
    </typeAliases>

    <!--  mapper 注册  -->
    <mappers>
        <mapper class="com.wzq.dao.BookMapper"/>
    </mappers>

</configuration>

8.3.6、Service

编写业务层接口BookService.java

package com.wzq.service;

import com.wzq.pojo.Books;

import java.util.List;

public interface BookService {

    // 增加一本书
    int addBook(Books books);

    // 删除一本书
    int deleteBookById(int id);

    // 更新一本书
    int updateBook(Books books);

    // 查询一本书
    Books queryBookById(int id);

    // 查询全部的书
    List<Books> queryAllBook();

    // 根据书名查询一本书
    Books queryBookByName(String bookName);
}

编写业务层实现类BookServiceImpl.java

package com.wzq.service;

import com.wzq.dao.BookMapper;
import com.wzq.pojo.Books;

import java.util.List;

public class BookServiceImpl implements BookService{
    // service 层调 dao 层:使用组合
    private BookMapper bookMapper;
    public void setBookMapper(BookMapper bookMapper) {
        this.bookMapper = bookMapper;
    }

    public int addBook(Books books) {
        return bookMapper.addBook(books);
    }

    public int deleteBookById(int id) {
        return bookMapper.deleteBookById(id);
    }

    public int updateBook(Books books) {
        return bookMapper.updateBook(books);
    }

    public Books queryBookById(int id) {
        return bookMapper.queryBookById(id);
    }

    public List<Books> queryAllBook() {
        return bookMapper.queryAllBook();
    }

    public Books queryBookByName(String bookName) {
        return bookMapper.queryBookByName(bookName);
    }
}

8.4、Spring部分实现

8.4.1、spring整合dao

编写spring-dao.xml文件,用于整合dao层。

<?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:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 1 关联数据库配置文件 -->
    <context:property-placeholder location="classpath:database.properties"/>

    <!-- 2 数据库连接池:
        dbcp: 半自动化操作,不能自动连接
        c3p0: 自动化操作,自动化加载配置文件,并且可以自动设置到对象中
        druid, hikari -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.Driver}"/>
        <property name="jdbcUrl" value="${jdbc.url}"/>
        <property name="user" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>

        <!-- c3p0 连接池的其他属性 -->
        <property name="maxPoolSize" value="30"/>
        <property name="minPoolSize" value="10"/>
        <property name="autoCommitOnClose" value="false"/>
        <property name="checkoutTimeout" value="10000"/>
        <property name="acquireRetryAttempts" value="2"/>
    </bean>

    <!-- 3 sqlSessionFactory -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <!--  绑定 Mybatis 的配置文件 -->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
    </bean>

    <!-- 4 配置 dao 接口扫描包,动态实现 dao 接口注入到 Spring 容器中
		(也可以根据 Spring 整合 Mybatis 笔记中的方式,手动编写实现类来实现) -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <!-- 注入 sqlSessionFactory -->
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
        <!-- 需要扫描的 dao 包 -->
        <property name="basePackage" value="com.wzq.dao"/>
    </bean>

</beans>

8.4.2、spring整合service

编写spring-service.xml文件,用于整合service层。

<?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:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd">

    <!-- 1 扫描 service 包 -->
    <context:component-scan base-package="com.wzq.service"/>

    <!-- 2 将所有的 service 类, 注入到 Spring, 可以通过配置或注解实现
 		(如果通过注解实现,只需要在 BookServiceImpl 类上添加 @Service,在 bookMapper 属性上添加 @Autowired 即可)-->
    <bean id="BookServiceImpl" class="com.wzq.service.BookServiceImpl">
        <property name="bookMapper" ref="bookMapper"/>
    </bean>

    <!-- 3 声明式事务配置 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!-- 注入数据源 -->
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!-- 4 aop 事务支持 (可以不加,Spring 会自动处理事务) -->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="*" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>

    <aop:config>
        <aop:pointcut id="txPointCut" expression="execution(* com.wzq.dao.*.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
    </aop:config>
</beans>

8.5、Spring-MVC部分实现

8.5.1、web.xml文件

编写web.xml文件。

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <!-- DispatcherServlet -->
    <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:applicationContext.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>SpringMVC</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <!-- 乱码过滤 -->
    <filter>
        <filter-name>encoding</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>encoding</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <!-- Session -->
    <session-config>
        <session-timeout>15</session-timeout>
    </session-config>
</web-app>

8.5.2、spring-mvc配置文件

编写spring-mvc.xml文件。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        https://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!-- 自动扫描包,让特定包中的注解生效,通过 IOC 容器管理 -->
    <context:component-scan base-package="com.wzq.controller"/>
    <!-- 让 Spring MVC 不处理静态资源 -->
    <mvc:default-servlet-handler />
    <!-- 注解驱动 -->
    <mvc:annotation-driven />

    <!-- JSON error code problem solve -->
<!--    <mvc:annotation-driven>-->
<!--        <mvc:message-converters register-defaults="true">-->
<!--            <bean class="org.springframework.http.converter.StringHttpMessageConverter">-->
<!--                <constructor-arg value="UTF-8"/>-->
<!--            </bean>-->
<!--            <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">-->
<!--                <property name="objectMapper">-->
<!--                    <bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">-->
<!--                        <property name="failOnEmptyBeans" value="false"/>-->
<!--                    </bean>-->
<!--                </property>-->
<!--            </bean>-->
<!--        </mvc:message-converters>-->
<!--    </mvc:annotation-driven>-->

    <!-- 视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/" />
        <property name="suffix" value=".jsp" />
    </bean>

    <!--    <bean id="/t1" class="com.wzq.controller.ControllerTest1"/>-->
</beans>

8.4.4、spring配置整合文件

通过spring核心配置文件,整合上述配置文件。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- Spring's core configuration file-->
    <import resource="classpath:spring-dao.xml"/>
    <import resource="classpath:spring-service.xml"/>
    <import resource="classpath:spring-mvc.xml"/>

</beans>

8.6、功能实现

8.6.1、首页编写

创建首页文件index.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
  <title>首页</title>

  <style>
    a{
      text-decoration: none;
      color: black;
      font-size: 18px;
    }
    h3{
      width: 180px;
      height: 38px;
      margin: 100px auto;
      text-align: center;
      line-height: 38px;
      background: cornflowerblue;
      border-radius: 5px;
    }
  </style>

</head>
<body>

<h3>
  <a href="${pageContext.request.contextPath}/book/allBook">进入书籍页面</a>
</h3>

</body>
</html>

8.6.2、书籍列表页

编写书籍列表页前端allBook.jsp

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>书籍展示页面</title>

    <%-- BootStrap --%>
    <link href="${pageContext.request.contextPath}/statics/bootstrap-3.3.7.min.css" rel="stylesheet">
</head>

<body>
<div class="container">
    <div class="row clearfix">
        <div class="col-md-12 column">
            <div class="page-header">
                <h1>
                    <small>书籍列表 —— 显示所有书籍</small>
                </h1>
            </div>
        </div>

        <div class="row">
            <div class="col-md-4 column">
                <a class="btn btn-primary" href="${pageContext.request.contextPath}/book/toAddBook">新增书籍</a>
                <a class="btn btn-primary" href="${pageContext.request.contextPath}/book/allBook">显示全部书籍</a>
            </div>
            <div class="col-md-8 column">
                <%-- 查询书籍 --%>
                <form class="form-inline" action="${pageContext.request.contextPath}/book/queryBook" 
                      method="post" style="float: right;">
                    <span style="color: #ff834e; font-weight: bold;">${error}</span>
                    <input type="text" name="queryBookName" class="form-control" placeholder="请输入要查询的书籍名称">
                    <input type="submit" value="查询" class="btn btn-primary">
                </form>
            </div>
        </div>
    </div>

    <div class="row clearfix">
        <div class="col-md-12 column">
            <table class="table table-hover table-striped">
                <thead>
                    <tr>
                        <th>书籍编号</th>
                        <th>书籍名称</th>
                        <th>书籍数量</th>
                        <th>书籍详情</th>
                        <th>操作</th>
                    </tr>
                </thead>

                <%-- 书籍从数据库中查询出来,遍历 foreach--%>
                <tbody>
                    <c:forEach var="book" items="${list}">
                        <tr>
                            <td>${book.bookID}</td>
                            <td>${book.bookName}</td>
                            <td>${book.bookCounts}</td>
                            <td>${book.detail}</td>
                            <td>
                                <a href="${pageContext.request.contextPath}/book/toUpdate?id=${book.bookID}">修改</a>
                                &nbsp; | &nbsp;
                                <a href="${pageContext.request.contextPath}/book/deleteBook/${book.bookID}">删除</a>
                            </td>
                        </tr>
                    </c:forEach>
                </tbody>
            </table>
        </div>
    </div>
</div>
</body>
</html>

8.6.3、controller编写

创建BookController.java文件。

package com.wzq.controller;

import com.wzq.pojo.Books;
import com.wzq.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

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

@Controller
@RequestMapping("/book")
public class BookController {
    // controller 层调用 service 层
    @Autowired
    @Qualifier("BookServiceImpl")
    private BookService bookService;

    // 查询全部的书籍,并且返回到一个书籍展示页面
    @RequestMapping("/allBook")
    public String list(Model model){
        List<Books> list = bookService.queryAllBook();
        model.addAttribute("list", list);
        return "allBook";
    }

    // 跳转到增加书籍页面
    @RequestMapping("/toAddBook")
    public String toAddPaper(){
        return "addBook";
    }

    // 添加书籍的请求
    @RequestMapping("/addBook")
    public String addBook(Books books){
        System.out.println("addBook=>" + books);
        bookService.addBook(books);
        return "redirect:/book/allBook";  // 重定向到 @RequestMapping("/allBook") 请求
    }

    // 跳转到修改页面
    @RequestMapping("/toUpdate")
    public String toUpdateBook(int id, Model model){
        Books books = bookService.queryBookById(id);
        model.addAttribute("qBooks", books);
        return "updateBook";
    }

    // 修改书籍
    @RequestMapping("updateBook")
    public String updateBook(Books books){
        System.out.println("updateBook=>" + books);
        bookService.updateBook(books);
        return "redirect:/book/allBook";
    }

    // 删除书籍
    @RequestMapping("/deleteBook/{bookId}")
    public String deleteBook(@PathVariable("bookId") int id){
        bookService.deleteBookById(id);
        return "redirect:/book/allBook";
    }

    // 查询书籍
    @RequestMapping("/queryBook")
    public String queryBook(String queryBookName, Model model){
        Books books = bookService.queryBookByName(queryBookName);

        List<Books> list = new ArrayList<Books>();
        list.add(books);

        if (books == null){
            list = bookService.queryAllBook();
            model.addAttribute("error", "未查到");
        }

        model.addAttribute("list", list);
        return "allBook";
    }
}

九、拦截器

SpringMVC的处理器拦截器类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理。开发者可以自己定义一些拦截器来实现特定的功能。

**过滤器与拦截器的区别:**拦截器是AOP思想的具体应用。拦截器只会拦截访问的控制器方法, 如果访问的是jsp/html/css/image/js是不会进行拦截的。

9.1、拦截器编写

package com.wzq.config;

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 {
    
    // 在请求处理的方法之前执行(如果返回 true 执行下一个拦截器,如果返回 false 就不继续执行下一个拦截器)
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
                             Object handler) throws Exception {
        System.out.println("=========处理前=========");
        return true;  // 放行,进入下一个拦截器
    }

    // 在请求处理方法执行之后执行
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response,
                           Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("=========处理后=========");
    }

    // 在 dispatcherServlet 处理后执行,做清理工作
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
                                Object handler, Exception ex) throws Exception {
        System.out.println("=========清理=========");
    }
}

9.2、配置拦截器

springmvc的配置文件中配置拦截器。

<!-- 拦截器配置 -->
<mvc:interceptors>
    <mvc:interceptor>
        <!-- /** 表示拦截这个请求下的所有请求(包括路径及其子路径) -->
	    <!-- /admin/* 拦截的是 /admin/add 等, /admin/add/user 不会被拦截 -->
	    <!-- /admin/** 拦截的是 /admin/ 下的所有 -->
        <mvc:mapping path="/**"/>
        <!-- bean 配置的就是拦截器 -->
        <bean class="com.wzq.config.MyInterceptor"/>
    </mvc:interceptor>
</mvc:interceptors>

9.3、测试效果

编写一个controller如下。

package com.wzq.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestController {

    @GetMapping("t1")
    public String test(){
        System.out.println("Test Controller Running");
        return "OK";
    }
}

控制台的输出结果如下。

=========处理前=========
Test Controller Running
=========处理后=========
=========清理=========

十、文件上传和下载

文件上传是项目开发中最常见的功能之一,SpringMVC 可以很好的支持文件上传,但是 SpringMVC 上下文中默认没有装配 MultipartResolver,因此默认情况下其不能处理文件上传工作。如果想使用 Spring 的文件上传功能,则需要在上下文中配置 MultipartResolver

前端表单要求:为了能上传文件,必须将表单的 method 设置为 POST,并将 enctype 设置为 multipart/form-data。只有在这样的情况下,浏览器才会把用户选择的文件以二进制数据发送给服务器。

对表单中的 enctype 属性的详细说明:

  • application/x-www=form-urlencoded:默认方式,只处理表单域中的 value 属性值,采用这种编码方式的表单会将表单域中的值处理成 URL 编码方式。
  • multipart/form-data:这种编码方式会以二进制流的方式来处理表单数据,这种编码方式会把文件域指定文件的内容也封装到请求参数中,不会对字符编码。
  • text/plain:除了把空格转换为 "+" 号外,其他字符都不做编码处理,这种方式适用直接通过表单发送邮件。

10.1、文件上传

10.1.1、依赖导入

导入文件上传的jar包,commons-fileuploadMaven会自动帮我们导入它的依赖包 commons-io 包。

<dependencies>
    <!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
    <dependency>
        <groupId>commons-fileupload</groupId>
        <artifactId>commons-fileupload</artifactId>
        <version>1.5</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>4.0.1</version>
        <scope>provided</scope>
    </dependency>
</dependencies>

10.1.2、配置Bean

applicationContext.xml文件中,配置bean(注意这个beanid 必须为 multipartResolver, 否则上传文件会报 400 的错误)。

<!-- 文件上传配置 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <!-- 请求的编码格式,必须和 jsp 的 pageEncoding 属性一致,以便正确读取表单内容,默认为 ISO-8859-1 -->
    <property name="defaultEncoding" value="utf-8" />
    <!-- 上传文件大小上限,单位字节(10485760 = 10M) -->
    <property name="maxUploadSize" value="10485760" />
    <property name="maxInMemorySize" value="40960" />
</bean>

10.1.3、前端页面

前端页面编写如下。

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<form action="/upload" enctype="multipart/form-data" method="post">
    <input type="file" name="file">
    <input type="submit" value="upload">
</form>
</body>
</html>

10.1.4、Controller编写

Controller代码编写如下。

package com.wzq.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.commons.CommonsMultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.\*;
import java.net.URLEncoder;

@Controller
public class FileController {

    // @RequestParam("file") 将 name = file 得到的文件封装成 CommonsMultipartFile 对象
    // 批量上传 CommonsMultipartFile 则为数组即可
    @RequestMapping("/upload")
    public String fileUpload(@RequestParam("file") CommonsMultipartFile file,
                             HttpServletRequest request) throws IOException {

        // 获取文件名: file.getOriginalFilename();
        String uploadFileName = file.getOriginalFilename();

        // 如果文件名为空,直接回到首页!
        if ("".equals(uploadFileName)) {
            return "redirect:/index.jsp";
        }
        System.out.println("上传文件名: " + uploadFileName);

        // 上传路径保存设置
        String path = request.getServletContext().getRealPath("/upload");

        // 如果路径不存在,创建一个
        File realPath = new File(path);
        if (!realPath.exists()) {
            realPath.mkdir();
        }
        System.out.println("上传文件保存地址:" + realPath);

        InputStream is = file.getInputStream();  // 文件输入流
        OutputStream os = new FileOutputStream(new File(realPath, uploadFileName)); // 文件输出流

        // 读取写出
        int len = 0;
        byte[] buffer = new byte[1024];
        while ((len = is.read(buffer)) != -1) {
            os.write(buffer, 0, len);
            os.flush();
        }
        os.close();
        is.close();
        return "redirect:/index.jsp";
    }

    // 采用 file.transferTo 来保存上传的文件
    @RequestMapping("/upload2")
    public String fileUpload2(@RequestParam("file") CommonsMultipartFile file,
                              HttpServletRequest request) throws IOException {

        // 上传路径保存设置
        String path = request.getServletContext().getRealPath("/upload");
        File realPath = new File(path);
        if (!realPath.exists()){
            realPath.mkdir();
        }
        // 上传文件地址
        System.out.println("上传文件保存地址:" + realPath);

        // 通过 CommonsMultipartFile 的方法直接写文件(注意这个时候)
        file.transferTo(new File(realPath + "/" + file.getOriginalFilename()));
        return "redirect:/index.jsp";
    }
}

CommonsMultipartFile 的常用方法:

  • String getOriginalFilename():获取上传文件的原名。
  • InputStream getInputStream():获取文件流。
  • void transferTo(File dest):将上传文件保存到一个目录文件中。

10.2、文件下载

文件下载步骤:

  • 1、设置 response 响应头。
  • 2、读取文件 —— InputStream
  • 3、写出文件 —— OutputStream
  • 4、执行操作
  • 5、关闭流 (先开后关)

对应的Controller代码如下。

@RequestMapping(value = "/download")
    public String downloads(HttpServletResponse response, HttpServletRequest
                            request) throws Exception {

        // 要下载的图片地址
        String path = request.getServletContext().getRealPath("/upload");
        String fileName = "demo.png";

        // 1、设置 response 响应头
        response.reset();  // 设置页面不缓存,清空 buffer
        response.setCharacterEncoding("UTF-8"); // 字符编码
        response.setContentType("multipart/form-data"); // 二进制传输数据
        // 设置响应头
        response.setHeader("Content-Disposition",
                           "attachment;fileName=" + URLEncoder.encode(fileName, "UTF-8"));

        File file = new File(path, fileName);
        // 2、 读取文件——输入流
        InputStream input = new FileInputStream(file);
        // 3、 写出文件——输出流
        OutputStream out = response.getOutputStream();

        byte[] buff = new byte[1024];
        int index = 0;
        // 4、执行 写出操作
        while ((index = input.read(buff)) != -1) {
            out.write(buff, 0, index);
            out.flush();
        }

        out.close();
        input.close();
        return null;
    }
0

评论区