前言

1个最简化版的web框架应具备以下3大功能:

  • 接收客户端http请求-
  • 获取http请求的参数
  • 响应客户端字符串

SpringMVC是Spring对Web框架的1个解决方案

  • SpringMVC提供了一个总的前端控制器Servlet,用于接收Tomcat的http请求;
  • 定义了一套路由策略(URL--->Handler的映射)和处理器(handler);
  • 将handler处理的结果使用视图解析技术响应给前端;

SpringMVC替代并细化了之前Servler的功能,SpringMVC把之前自己写的Servlet也就是Controller层,拆分成了2大部分

  • 前端分发器(Dispatcher):本质是1个由springMVC提供Servlet,对前端请求进行统一分发
  • 处理器:                                我们自己写的1个类,用于接收前端分发器分发的HTTP请求,处理业务逻辑;

 

一、SpringMVC入门

1.介绍

SpringMVC本质是1个增强版的Servlet:

  • Tomcat接收到用户请求通过读取web.xml配置文件,锁定1个DispatcherServlet分发器;
  • 分发器将Tomcat接收到的所有http请求统一起来,通过@RequestMapping注解,匹配到各个处理器,分发不同的http请求;(路由系统)
  • 各个处理类通过@RequestMapping 就是处理业务逻辑的类;

 

2.入门案例

发送一个请求name=Martin&age=18,后台接收参数,封装一个User对象返回json数据

2.1.引入pom依赖

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

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>spring</artifactId>
        <groupId>com.zhanggen</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>day05-mvc</artifactId>
    <packaging>war</packaging>

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

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.7</maven.compiler.source>
        <maven.compiler.target>1.7</maven.compiler.target>
    </properties>

    <dependencies>
        <!--springmvc核心-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.1.6.RELEASE</version>
        </dependency>
        <!--jackson-->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.9.10</version>
        </dependency>
        <!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.20</version>
        </dependency>
    </dependencies>

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

 

2.2.配置前端控制器(DispatcherServlet

springMVC是三层架构中web层的解决方案,所以springMVC的配置文件是由Tomcat读取的;

配置Tomcat的配置文件(web.xml)声明1个前端分发器dispatcherServlet,明确这个分发器可以分发的URL;

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://java.sun.com/xml/ns/javaee"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         version="2.5">

  <!--Tomcat配置文件中声明servlet(前端控制器)-->
  <servlet>
    <servlet-name>dispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:spring-mvc.xml</param-value>
    </init-param>
  </servlet>
  <servlet-mapping>
    <servlet-name>dispatcherServlet</servlet-name>
    <!--在当前项目中就表示拦截所有-->
    <url-pattern>/</url-pattern>
  </servlet-mapping>
</web-app>
web.xml

------------------------------------------------------------------------------------

spring mvc的前端配置器配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       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/mvc
        https://www.springframework.org/schema/mvc/spring-mvc.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">
    <!--SpringMvc的配置文件-->
    <!--注解扫描-->
    <context:component-scan base-package="com.zhanggen.controller"/>
    <!--注解驱动-->
    <mvc:annotation-driven/>

</beans>
spring-mvc.xml

 

2.3.controller层(处理器)

package com.zhanggen.controller;

import com.zhanggen.domain.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller  //将当前类创建的对象放到spring的IOC容器
public class UserController {
    //给当前方法绑定1个访问路径:http://127.0.0.1:8080/user/demo1?name=zhanggen&age=18
    @RequestMapping("/user/demo1")
    //将当前对象的返回值,转换成json格式返回
    @ResponseBody
    public User demo1(String name, Integer age) {
        //直接向spring Mvc要(不在接收参数)
        User user = new User(name, age);
        //直接返回对象
        return user;

    }
}
UserController.java

 

3.Spring Mcv执行流程

 

 

4.释放静态资源

现在SpringMVC的前端控制器拦截路径是/,在目前项目中,代表拦截所有请求,然后按照请求路径去Controller中寻找方法

但是对应一些静态文件,比如js、html、css、jpg等,是不需要由springMVC的dispatcher分发器进行路由分发的,而是直接定位到对应的静态资源,

所以我们要在SpringMVC中添加配置项,将不需要dispatcher分发器进行路由转发的静态资源路径指定出来 ,进行放行;

4.1.修改springMvc配置文件

通过修改springMVC的配置文件告诉springMVC哪些是静态资源的URL路径,当用户访问这些路径时进行放行;

 <!--释放静态资源-->
    <mvc:resources mapping="/index.html" location="/"/>
    <mvc:resources mapping="/css/*" location="/css/"/>
    <mvc:resources mapping="/js/*" location="/js/"/>
    <mvc:resources mapping="/fonts/*" location="/fonts/"/>
    <mvc:resources mapping="/img/*" location="/img/"/>
    <mvc:resources mapping="/js/*" location="/js/"/>
spring-mvc.xml

 

5.@RequestMapping注解

@RequestMapping注解用于建立请求URL和处理方法之间的对应关系, 常见属性如下:

  • value: 等同于path,用于为当前方法绑定访问路径

  • method:用于限制请求类型,如果省略此选项,代表不对请求类型做限制

注意:此注解可以标注在方法上,也可以标注在类上,标注在类上代表类中的所有方法都可以共用一段URL

package com.zhanggen.controller;

import com.zhanggen.domain.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller  //将当前类创建的对象放到spring的IOC容器
public class UserController {
    //给当前方法绑定1个访问路径:http://127.0.0.1:8080/user/demo1?name=zhanggen&age=18
    @RequestMapping( path ={"/user/demo1"})
    //将当前对象的返回值,转换成json格式返回
    @ResponseBody
    public User demo1(String name, Integer age) {
        //直接向spring Mvc要(不在接收参数)
        User user = new User(name, age);
        //直接返回对象
        return user;

    }
    @ResponseBody
    //支持为1个方法绑定多个URL路径
    @RequestMapping(path = {"/user/demo2","/user/demo3"})
    public User demo2(String name, Integer age) {
        //直接向spring Mvc要(不在接收参数)
        User user = new User(name, age);
        //直接返回对象
        return user;
    }

    @ResponseBody
    //method:用于现在当前方法可以处理,哪种请求方式
    //method:默认不写可以支持所有请求
    @RequestMapping(path = "/user/demo3",method = {RequestMethod.GET,RequestMethod.POST})
    public User demo3(String name, Integer age) {
        //直接向spring Mvc要(不在接收参数)
        User user = new User(name, age);
        //直接返回对象
        return user;
    }


}
UserController.java

 

二、接收和响应请求

在SpringMVC中,可以使用多种数据类型作为处理器的参数来接收前端传入的参数;

前端代码如下

package com.zhanggen.controller;

import com.zhanggen.domain.User;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.util.Date;
import java.util.List;

@Controller  //将当前类创建的对象放到spring的IOC容器
public class UserController {
    //给当前方法绑定1个访问路径:http://127.0.0.1:8080/user/demo1?name=zhanggen&age=18
    @RequestMapping(path = {"/user/demo1"})
    //将当前对象的返回值,转换成json格式返回
    @ResponseBody
    public User demo1(String name, Integer age) {
        //直接向spring Mvc要(不在接收参数)
        User user = new User(name, age, null);
        //直接返回对象
        return user;

    }

    @ResponseBody
    //支持为1个方法绑定多个URL路径
    @RequestMapping(path = {"/user/demo2", "/user/demo3"})
    public User demo2(String name, Integer age) {
        //直接向spring Mvc要(不在接收参数)
        User user = new User(name, age, null);
        //直接返回对象
        return user;
    }

    @ResponseBody
    //method:用于现在当前方法可以处理,哪种请求方式
    //method:默认不写可以支持所有请求
    @RequestMapping(path = "/user/demo3", method = {RequestMethod.GET, RequestMethod.POST})
    public User demo3(String name, Integer age) {
        //直接向spring Mvc要(不在接收参数)
        User user = new User(name, age, null);
        //直接返回对象
        return user;
    }

    //接收简单类型参数:/user/demo5?name=张三&age=18
    @ResponseBody
    @RequestMapping(path = "/user/demo5")
    public String demo5(String name, Integer age) {
        System.out.println(name);
        System.out.println(age);
        return "ok";
    }

    //接收参数--对象类型参数:/user/demo6?name=张三&age=18
    @ResponseBody
    @RequestMapping(path = "/user/demo6")
    public String demo6(User user) {
        System.out.println(user);
        return "ok";
    }

    //接收数组类型的参数
    @ResponseBody
    @RequestMapping(path = "/user/demo7")
    public String demo7(String[] names) {
        for (String name : names) {
            System.out.println(name);
        }
        return "ok";
    }

    //接收时间类型的参数-注解版
    @ResponseBody
    @RequestMapping(path = "/user/demo8")
    public String demo8(@DateTimeFormat(pattern = "yyyy-MM-dd") Date myDate) {
        System.out.println(myDate);
        return "ok";
    }

    //接收时间类型的参数-xml版本
    @ResponseBody
    @RequestMapping(path = "/user/demo9")
    public String demo9(Date myDate) {
        System.out.println(myDate);
        return "ok";
    }

    @ResponseBody
    @RequestMapping(path = "/user/demo10", method = {RequestMethod.GET, RequestMethod.POST})
    public String demo10(MultipartFile picture1) throws IOException {
        System.out.println("-----------" + picture1);
        //定义好目标文件
        File file = new File("D:/upload/" + picture1.getOriginalFilename());
        System.out.println(file);
        //文件转存
        picture1.transferTo(file);
        return "ok";
    }

//    //接收集合参数:把集合作为对象的1个属性
//    @ResponseBody
//    @RequestMapping(path = "/user/demo11")
//    public String demo11(User user) {
//        System.out.println(user);
//        return "ok";
//    }

    //可以不用把集合参数作为对象的1个属性,指定1个接收参数名称
    @ResponseBody
    @RequestMapping(path = "/user/demo11")
    public String demo12(@RequestParam List<String> hobby) {
        for (String s : hobby) {
            System.out.println(s);
        }
        return "ok";
    }


    // @RequestParam注解使用
    @ResponseBody
    @RequestMapping("/user/demo12")
    public String demo12(
            //后端修改前端参数的名称usERName为userName
            @RequestParam("usERName") String userName,
            // 设置password参数的默认值为123
            @RequestParam(defaultValue = "123") String password) {
        System.out.println(userName);
        System.out.println(password);
        return "ok";
    }

    //接收请求体中包含参数信息Ajax请求
    @ResponseBody
    @RequestMapping("/user/demo13")
    public String demo13(String name, Integer age) {
        System.out.println(name);
        System.out.println(age);
        return "ok";
    }

    //接收请求体中不包含参数信息的Ajax请求
    @ResponseBody
    @RequestMapping("/user/demo14")
    public String demo14(@RequestBody User user) {
        System.out.println(user);
        return "ok";
    }

}
index.html

 

1.接收简单类型参数

需要保证前端传递的参数名称跟实体类的属性名称一致;

 //接收简单类型参数:/user/demo5?name=张三&age=18
    @ResponseBody
    @RequestMapping(path ="/user/demo5")
    public String demo5(String name,Integer age){
        System.out.println(name);
        System.out.println(age);
        return "ok";
    }
接收简单类型参数

 

2.接收对象类型参数

需要保证前端传递的参数名称跟实体类的属性名称一致;

    //接收参数--对象类型参数:/user/demo6?name=张三&age=18
    @ResponseBody
    @RequestMapping(path = "/user/demo6")
    public String demo6(User user){
        System.out.println(user);
        return "ok";
    }
接收对象类型参数

 

3.接收数组类型参数

需要保证前端传递的参数名称跟方法中的数组形参名称一致;

  //接收数组类型的参数
    @ResponseBody
    @RequestMapping(path = "/user/demo7")
    public String demo7(String[] names) {
        for (String name : names) {
            System.out.println(name);
        }
        return "ok";
    }
接收数组类型的参数

 

4.接收日期类型参数

对于一些常见的类型, SpringMVC内置了类型转换器

对于一些格式比较灵活的参数,它无法完成类型转换,这时候就必须自定义类型转换器

4.1.注解方式

    @GetMapping("/order/page")
    public ResultInfo findByPage(
            @RequestParam("page") Integer pageNum,
            @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date beginTime,
            @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date endTime,
            Integer pageSize,
            Long number
            ) {
        Page<Order> page = orderService.findByPage(pageNum, pageSize, number, beginTime, endTime);
        return ResultInfo.success(page);

4.2.xml配置方式

通过@DateTimeFormat注解的方式转换时间格式,每次都要在方法中反复声明注解,xml可以一劳永逸地解决这种问题;

4.2.1.自定义转换类

package com.zhanggen.convert;

import org.springframework.core.convert.converter.Converter;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;


//自定义类型转换器,要求实现Converter<原始类型,目标类型>接口,并重写convert方法
public class DateConvert implements Converter<String, Date> {
    @Override
    public Date convert(String s) {
        Date date = null;
        try {
            date = new SimpleDateFormat("yyyy-MM-dd").parse(s);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return date;
    }
}
DateConvert.java

4.2.2.将转换类配置到spring

 <!--注解驱动-->
    <mvc:annotation-driven conversion-service="conversionService1"/>
    <!--自定义时间转换服务-->
    <bean id="conversionService1" class="org.springframework.context.support.ConversionServiceFactoryBean">
        <property name="converters">
            <bean class="com.zhanggen.convert.DateConvert"></bean>
        </property>
    </bean>
spring-mvc.xml

 

5.文件类型参数

文件上传是项目开发中最常见的功能,使用SpringMVC可以轻松完成文件的上传功能;

在SpringBoot之后可直接上传文件,无需配置文件上传解析器;

5.1.添加pom依赖

  <!--上传文件-->
        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.4</version>
        </dependency>

5.2.配置上传解析器

 <!--配置文件上传解析器对象:注意这个id必须是multipartResolver-->
    <bean id="multipartResolver"
          class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <!--单次上传文件大小限制(10Mb=10*1024*1024=1048576),单位是字节-->
        <property name="maxUploadSize" value="10048576"/>
    </bean>
spring-mvc.xml

5.3.接收文件

  @ResponseBody
    @RequestMapping(path ="/user/demo10",method = {RequestMethod.GET, RequestMethod.POST} )
    public String demo10(MultipartFile picture1) throws IOException {
        System.out.println("-----------"+picture1);
        //定义好目标文件
        File file = new File("D:/upload/" + picture1.getOriginalFilename());
        System.out.println(file);
        //文件转存
        picture1.transferTo(file);
        return "ok";
    }
接收文件参数

 

6.获取集合类型参数

获取集合参数时,需要将集合参数包装到一个实体中才可以;

6.1.实体类封装集合属性

package com.zhanggen.domain;

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

import java.util.List;

@NoArgsConstructor
@AllArgsConstructor
@Data
public class User {
    private String name;
    private Integer age;
    private List<String> hobby;
}
User.java

 

6.2.配置中文乱码过滤器

  <!--中文乱码过滤器-->
  <filter>
    <filter-name>characterEncodingFilter</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>characterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
web.xml

 

6.3.接收集合参数

//接收集合参数
    @ResponseBody
    @RequestMapping(path = "/user/demo11")
    public String demo11(User user){
        System.out.println(user);
        return "ok";
    }
接收集合参数

 

7.@RequestParam

@RequestParam标注在方法参数之前,用于对传入的参数做一些限制,常见属性如下:

  • value:默认属性,用于指定前端传入的参数名称

  • defaultValue:当参数为非必传参数且前端没有传入参数时,指定一个默认值

7.1.指定获取前端参数

可以不用把集合参数作为对象的1个属性,指定1个接收参数名称;

  //可以不用把集合参数作为对象的1个属性,指定1个接收参数名称
    @ResponseBody
    @RequestMapping(path ="/user/demo11")
    public String demo12(@RequestParam List<String> hobby) {
        for (String s : hobby) {
            System.out.println(s);
        }
        return "ok";
    }
指定前端参数名称

 

7.2.参数别名和设置默认值

处理器在接收前端参数的时候,必须和前端参数名称保持一致;

通过RequestParam注解可以给修改前端参数设置参数别名,及设置前端参数的默认值

// @RequestParam注解使用
    @ResponseBody
    @RequestMapping("/user/demo12")
    public String demo12(
            //后端修改前端参数的名称usERName为userName
            @RequestParam("usERName") String userName,
            // 设置password参数的默认值为123
            @RequestParam(defaultValue = "123") String password) {
        System.out.println(userName);
        System.out.println(password);
        return "ok";
    }
别名和默认值

 

7.3.分页

    //菜品列表分页查询:/dish/page?page=1&pageSize=10
    @GetMapping("/dish/page")
    public ResultInfo findByPage(@RequestParam(value = "page", defaultValue = "1") Integer pageNum,
                                 @RequestParam(defaultValue = "10") Integer pageSize,
                                 String name) {
        //调用dishService层返回page对象
        Page<Dish> dishPage = dishService.findByPage(name, pageNum, pageSize);
        //把dishPage对象封装到ResultInfo中
        return ResultInfo.success(dishPage);
    }

 

 

 

8.接收Ajax提交的参数

使用Ajax提交请求参数时,如果请求体中包含参数信息,后端需要使用@RequestBody接收

注意@RequestBody注解后面只能跟 对象和map;

    @PostMapping("/dish")
    public ResultInfo save(@RequestBody Dish dish) {
        dishService.save(dish);
        return ResultInfo.success(null);
    }

 

9.通过servlet的HttpServletRequest接收参数

   //根据ID查询
    @GetMapping("/getById")
    public AjaxResult findById(Long id, HttpServletRequest request) {
        System.out.println(request.getParameter("id"));
        Review review = reviewService.findById(id);
        return AjaxResult.success(review);
    }

 

10.模板引擎

借助freemarker和Thymeleaf模板引擎, 对HTML模板进行数据渲染响应给前端;

1.pom依赖

引入spring-boot-starter-freemarker

dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-freemarker</artifactId>
        </dependency>
         <!--apache 对 java io 的封装工具库-->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-io</artifactId>
            <version>1.3.2</version>
        </dependency>
    </dependencies>
pom.xml

2.application.yml

配置freemarker

server:
  port: 8881 #服务端口
spring:
  application:
    name: freemarker-demo #指定服务名
  freemarker:
    cache: false  #关闭模板缓存,方便测试
    settings:
      template_update_delay: 0 #检查模板更新延迟时间,设置为0表示立即检查,如果时间大于0会有缓存不方便进行模板测试
    suffix: .ftl               #指定Freemarker模板文件的后缀名
application.yml

3.controller

使用@controller注解响应给前端的是逻辑视图文件的文件名称

使用@RestController注解响应的是逻辑视图和数据渲染之后的物理视图;

注意把@RestController改成@Controller

package com.heima.freemarker.demo.controller;

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

import java.util.*;

/**
 * @author zhanggen
 * @since 2022-07-04
 */
//@RestController=@Controller+@ResponseBody功能
@Controller
public class HelloController {
    @GetMapping("/hello")
    public String hello(Model model) {
        model.addAttribute("name","张根");
        Map<String,Object> stuMap1=new HashMap<>();
        stuMap1.put("name","Martin");
        stuMap1.put("age",19);
        model.addAttribute("stu",stuMap1);

        Map<String,Object> stuMap2=new HashMap<>();
        stuMap2.put("name","Tom");
        stuMap2.put("age",20);
        List<Map<String,Object>> studentList=new ArrayList<>();
        studentList.add(stuMap1);
        studentList.add(stuMap2);
        model.addAttribute("studentList",studentList);

        // 新增日期字段
        model.addAttribute("today", new Date());
        return "01-basic";
    }
}
HelloController.java

 

4.模板

把模板文件放到resources/templates

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Hello World!</title>
</head>
<body>
<b>普通文本 String 展示:</b><br><br>
Hello ${name} <br>
<hr>
<b>对象Student中的数据展示:</b><br/>
姓名:${stu.name}<br/>
年龄:${stu.age}
<hr>

<#--1.集合遍历-->
<#--2.if..else分支判断-->
<#list studentList as student>
    <#if (student.age < 20) || (student.name="Martin")>
        <p style="color: aquamarine"> ${student.name}<---->${student.age}<br/></p>
    <#else>
        <p style="color: aqua">${student.name}<---->${student.age+6}</p>
    </#if>
</#list>


<#--3.判空、size方法、集合遍历-->
<#if studentList??>
   <p>列表的总长度:${studentList?size} </p>
    <#list studentList as student>
        <#if (student.age < 20) || (student.name="Martin")>
        <p style="color: aquamarine"> ${student.name}<---->${student.age}<br/></p>
        <#else>
        <p style="color: aqua">${student.name}<---->${student.age+6}</p>
        </#if>
    </#list>
</#if>


<#--4.日期函数-->
<#--5.内建函数 -->
${today?date}
<br/>
${today?time}
<br/>
${today?datetime}
<br/>
${today?string("yyyy年MM月")}

</body>
</html>
01-basic.ftl

 

 

 

5.显示效果

 

 

三、统一异常处理

在SpringMVC中提供了一个通用的异常处理机制,它提供了一个成熟、简洁并且清晰的异常处理方案

系统的Dao(mapper)、Service、Controller出现了异常,都可以通过throws Exception向上抛出;

最后SpringMVC的前端控制器把抛出的交给统一异常处理器,进行异常统一处理,返回给前端

最终由springMVC统一转交给自定义的统一异常处理类来处理,实现异常的统一处理;

1.后台异常分类

全局异常处理类,处理的异常主要分为以下3类:

  • 可预测的异常,对于他们,我们直接捕获,给前端提示;(可以预测的异常
  • 自定义的业务异常,对于他们,我们直接throw出去,将业务报错,返回给前端;(throw自定义的异常
  • 无预测的异常 对于他们,我们直接捕获,捕获完了,记录日志, 给前端一个假提示;(不可预测的异常-Exception)

2.自定义统一异常处理器 

package com.itheima.reggie.handler;

import com.itheima.reggie.common.CustomException;
import com.itheima.reggie.common.ResultInfo;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

//全局异常处理器
@ControllerAdvice
@ResponseBody
public class GlobalExceptionHandler {

    //1、处理唯一值重复的异常
    @ExceptionHandler(DuplicateKeyException.class)
    public ResultInfo handlerDuplicateKeyException(Exception e) {
        //1. 打印日志
        e.printStackTrace();

        if (e.getMessage().contains("idx_category_name")) {
            return ResultInfo.error("分类名称的值重复");
        }

        //2. 给前端提示
        return ResultInfo.error("填写的值重复");
    }

    //处理自定义异常
    @ExceptionHandler(CustomException.class)
    public ResultInfo handlerCustomException(Exception e) {
        //1. 打印日志
        e.printStackTrace();

        //2. 给前端提示
        return ResultInfo.error(e.getMessage());
    }

    //非预期异常
    @ExceptionHandler(Exception.class)
    public ResultInfo handlerException(Exception e) {
        //1. 打印日志
        e.printStackTrace();

        //2. 给前端提示
        return ResultInfo.error("服务器开小差了,请联系管理员");
    }

}
GlobalExceptionHandler.java

3.使用自定义异常处理器  

  //单条or批量删除
    @Override
    public void bathDelete(String ids) throws CustomException {
        String[] idList = ids.split(",");
        for (String id : idList) {
            long disgID = Long.parseLong(id);
            Dish dish = dishMapper.selectById(disgID);
            //1.只允许删除停售状态的
            if (dish.getStatus() != 0) {
                //使用自定义的异常处理器抛出异常
                throw new CustomException("无法删除启售状态的菜品");
            }
            //2.删除菜品的同时需要删除菜品的口味信息
            //LambdaQueryWrapper构造查询条件
            LambdaQueryWrapper<DishFlavor> dishFlavorQueryWrapper = new LambdaQueryWrapper<>();
            dishFlavorQueryWrapper.eq(dish.getId() != null, DishFlavor::getDishId, dish.getId());
            dishFlavorMapper.delete(dishFlavorQueryWrapper);
            dishMapper.deleteById(disgID);

        }

    }

 

 

四、拦截器

拦截器是SpringMVC提供的一种技术,它的功能似于过滤器过滤器,会在以下时机执行;

  • 进入controller之前
  • 离开controller之后
  • 响应离开服务时

 

1.引入拦截器依赖

  <!--servlet-->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
        </dependency>
pom.xml

 

2.定义拦截器类

package com.zhanggen.interceptor;

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

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

public class MyInterceptor implements HandlerInterceptor {
    @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("离开处理器之后");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("请求即将离开服务器");
    }
}
MyInterceptor.java

 

3.配置拦截器

  <!--拦截器配置-->
    <!--拦截器链-->
    <mvc:interceptors>
        <mvc:interceptor>
            <!--设置拦截规则-->
            <!--拦截全部-->
            <mvc:mapping path="/**"/>
            <!--排除拦截-->
            <mvc:exclude-mapping path="/login"/>
            <bean class="com.zhanggen.interceptor.MyInterceptor"></bean>
        </mvc:interceptor>
    </mvc:interceptors>
spring-mvc.xml

 

 

五、SpringMVC执行流程

1.SpringMVC的4大组件

前端控制器:     相当于1个大管家,负责分发请求(老大)

处理器映射器: 负责根据URL寻找对应的处理器方法(小弟1)

处理器适配器: 负责真正的去调用某个处理器方法(小弟2)

视图解析器:    负责将逻辑视图(/WEB-INF/success.jsp)转换成物理视图(JSP对象)(小弟3)

 

2.SpringMVC执行流程

1. 用户通过浏览器发送请求至DispatcherServlet
2. DispatcherServlet收到请求调用HandlerMapping
3. HandlerMapping找到具体的处理器链返回给DispatcherServlet
4. DispatcherServlet会根据返回的处理器链调用HandlerAdapter
5. HandlerAdapter经过适配调用具体的Handler(controller)
6. Controller执行完成返回一个执行结果
7. HandlerAdapter将Handler的结果ModelAndView对象返回给DispatcherServlet
8. DispatcherServlet将ModelAndView对象传给ViewReslover
9. ViewReslover解析后得到具体View,并返回给DispatcherServlet
10. DispatcherServlet根据View进行视图渲染(即将模型数据填充至视图中)
11. DispatcherServlet会将渲染后的视图响应给浏览器

 

 

 

六、Restful风格API

REST是一种软件架构风格,其强调HTTP应当以资源为中心[==在请求地址中尽量的不要出现动词==]。

REST规范了HTTP请求动作,使用四个词语分别表示对资源的CRUD操作: GET(获取)、POST(新建)、PUT(更新)、DELETE(删除) 

 原来Restful
保存 /saveUser POST /user
修改 /updateUser?id=1 PUT /user/id/1
删除 /deleteUser?id=1 DELETE /user/id/1
查询所有 /findAllUser GET /user
查询一个 /findByUserId?id=1 GET /user/id/1

 

1.标识请求方法

使用以下注解可以表示当前处理器可以处理的请求方法;

  • @RequestMapping(value = "/user", method = RequestMethod.POST) :此注解标注的方法的URL路径
  • @GetMapping :此注解标注的方法只接收get请求
  • @PostMapping:此注解标注的方法只接收post请求
  • @DeleteMapping:此注解标注的方法只接收Delete请求
  • @PutMapping:此注解标注的方法只接收put请求

 

package com.itheima.web.controller;

import com.itheima.domain.User;
import org.springframework.web.bind.annotation.*;

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

@RestController
public class UserController {
    //保存
    @ResponseBody
//    @RequestMapping(value = "/user", method = RequestMethod.POST)
    @PostMapping("/user") //此注解标注的方法只接收post请求
    public User save(@RequestBody User user) {
        System.out.println("请求到达了save方法");
        System.out.println(user);
        user.setAge(180);
        return user;
    }

    //查询所有
    @ResponseBody
//    @RequestMapping(value = "/user", method = RequestMethod.GET)
    @GetMapping("/user")  //此注解标注的方法只接收post请求
    public List<User> findAll() {
        System.out.println("请求到达了findAll方法");
        List<User> userList = new ArrayList<User>();
        userList.add(new User(1, "张弢", 56));
        userList.add(new User(2, "张启樵", 43));
        return userList;
    }

}
UserController.java

 

2.接收动态URL路径变量

REST 风格的API提倡把查询参数,体现在URL路径上;

我们可以使用接收动态的URL路径变量呢?

2.1.接收1个动态路径变量

/user/1

/user/2

    // 根据id查询:/user/1或/user/2或者/user/3
    @ResponseBody
    @GetMapping("/user/{myId}")
    public User findById(@PathVariable("myId") int id) {
        System.out.println(id);
        User user = new User(id, "张根", 12);
        return user;
    }
根据id查询

 

2.1.接收1个动态路径变量

/user/name/zhangsan/age/18

/user/name/lisi/age/28

  //根据主键+其他条件查询:/user/name/zhangsan/age/18或者/user/name/lisi/age/28
    @ResponseBody
    @GetMapping("/user/name/{name}/age/{age}")
    public User findById(
            @PathVariable("name") String name,
            @PathVariable("age") Integer age
    ) {
        System.out.println(name);
        System.out.println(age);
        User user = new User(1, name, age);
        return user;
    }
根据多个条件查询

 

2.3.示例

    //回显菜品信息 /dish/198
    @GetMapping("/dish/{id}")
    public ResultInfo findById(@PathVariable("id") Long id) {
        Dish dish = dishService.findById(id);
        return ResultInfo.success(dish);
    }

 

3.Springmvc常见注解总结

@RequestMapping(value = "/user", method = RequestMethod.POST) :此注解标注的方法和URL的关系

@GetMapping :此注解标注的方法只接收get请求

@PostMapping:此注解标注的方法只接收post请求

@DeleteMapping:此注解标注的方法只接收delete请求

@PutMapping:此注解标注的方法只接收put请求

@RequestBody :此注解标注的方法可以接收请求体包含参数信息的Ajax请求

@ResponseBody:此注解标注的方法或者类可以响应JSON格式信息;

@Controller :如果要返回ModelAndView,则需要单独使用@Controller配合视图解析器InternalResourceViewResolver进行视图和模型的数据渲染(模板渲染项目)

@RestController    该注解标注在类上,相当于@Controller+@ResponseBody功能,返回JSON数据;(前后端分离项目)

@PathVariable:  接收动态URL路径中包含的变量信息

@RequestParam:指定接收某1个集合参数、设置参数默认值和别名;

 

 

参考

posted on 2022-05-20 23:58  Martin8866  阅读(82)  评论(0编辑  收藏  举报