SpringMVC介绍

SSM框架构建起单体项目的技术栈需求!其中的SpringMVC负责表述层(控制层)实现简化!
SpringMVC的作用主要覆盖的是表述层,例如:
请求映射、数据输入、视图界面、请求分发、表单回显、会话控制、过滤拦截、异步交互、文件上传、文件下载、数据校验、类型转换,等等等。
总结来说就是:简化前端参数接收(形参列表),简化后端数据响应(返回值)…

创建springwebmvc项目要记得转成maven/web程序

  • 改变项目打包方式,pom.xml:<packaging>war</packaging>
  • 并创建这些目录:(src/main/webapp/WEB-INF/web.xml)

或者idea中直接搜索并安装JBLJavaToWeb插件,右键项目点击JBLJavaToWeb即可自动改成web项目格式

快速了解(入门案例)

  1. 准备项目
    a.创建项目
    springmvc-quickstart
    注意将项目转成:maven/web程序
    b.导入依赖
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    <properties>
    <spring.version>6.0.6</spring.version>
    <servlet.api>9.1.0</servlet.api>
    <maven.compiler.source>17</maven.compiler.source>
    <maven.compiler.target>17</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
    <!-- springioc相关依赖 -->
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>${spring.version}</version>
    </dependency>

    <!-- web相关依赖 -->
    <!--
    在 Spring Web MVC 6 中,Servlet API 迁移到了 Jakarta EE API,因此在配置 DispatcherServlet 时需要使用
    Jakarta EE 提供的相应类库和命名空间。错误信息 “‘org.springframework.web.servlet. DispatcherServlet’
    is not assignable to ‘javax.servlet.Servlet,jakarta.servlet.Servlet’” 表明你 使用了旧版本的
    Servlet API,没有更新到 Jakarta EE 规范。
    -->
    <!-- 在 pom.xml 中引入 Jakarta EE Web API 的依赖 -->
    <dependency>
    <groupId>jakarta.platform</groupId>
    <artifactId>jakarta.jakartaee-web-api</artifactId>
    <version>${servlet.api}</version>
    <scope>provided</scope>
    </dependency>

    <!-- springwebmvc相关依赖 -->
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>${spring.version}</version>
    </dependency>

    </dependencies>
  2. Controller层
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    @Controller
    public class HelloController {
    //handlers
    /**
    * handler就是controller内部的具体方法
    * @RequestMapping("/springmvc/hello") 就是用来向handlerMapping中注册的方法注解!
    * @ResponseBody 代表向浏览器直接返回数据!不去找视图解析器
    */
    @RequestMapping("/springmvc/hello")
    @ResponseBody
    public String hello(){
    System.out.println("HelloController.hello");
    return "hello springmvc!!";
    }
    }
  3. SpringMVC核心配置类
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    @Configuration//配置类
    @ComponentScan(basePackages = "cn.xnj.controller") //扫描controller包
    public class MvcConfig {

    @Bean
    public RequestMappingHandlerMapping handlerMapping(){
    return new RequestMappingHandlerMapping();
    }

    @Bean
    public RequestMappingHandlerAdapter handlerAdapter(){
    return new RequestMappingHandlerAdapter();
    }
    }
  4. SpringMVC环境搭建
    对于基于java的Spring配置的应用程序,建议这样做,如下示例:
    创建一个类并固定继承AbstractAnnotationConfigDispatcherServletInitializer
    它可以被web项目加载,会初始化ioc容器,会设置dispatherServlet的地址
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    // SpringMVC提供的接口,是替代web.xml的方案,更方便实现完全注解方式ssm处理!
    // Springmvc框架会自动检查当前类的实现类,会自动加载 getRootConfigClasses / getServletConfigClasses 提供的配置类
    //TODO: getServletMappings 返回的地址 设置DispatherServlet对应处理的地址
    public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

    /**
    * 指定service / mapper层的配置类
    */
    @Override
    protected Class<?>[] getRootConfigClasses() {
    return null;
    }

    /**
    * 指定springmvc的配置类
    * @return
    */
    @Override
    protected Class<?>[] getServletConfigClasses() {
    return new Class<?>[] { MvcConfig.class };
    }

    /**
    * 设置dispatcherServlet的处理路径!
    * 一般情况下为 / 代表处理所有请求!
    */
    @Override
    protected String[] getServletMappings() {
    return new String[] { "/" };
    }
    }
  5. 启动测试
    a.点击idea上方CurrentFile(当前文件)
    b.点击(Edit Configurations)当前配置
    c.在弹出窗口点击左上角+号,选择(Tomcat Server)Tomcat服务器 (Local)本地
    d.点击右下角红色感叹号(Fix)修复,部署当前项目springmvc-quickstart:war exploded
    e.点击OK,然后运行当前项目
    f.在浏览器输入:localhost:8080/springmvc/hello
    g.能看到界面hello springmvc!!说明成功。

    注意tomcat应该为10+的版本!才支持Jakarta EE API!

SpringMVC接收数据

访问路径设置—@RequestMapping()

@RequestMapping注解的作用就是将请求的 URL 地址和处理请求的方式(handler方法)关联起来,建立映射关系。SpringMVC 接收到指定的请求,就会来找到在映射关系中对应的方法来处理这个请求。

以前写@WebServlet("")时,里面的路径要求必须用/开头,@RquestMapping("")则并不强制要求,你可以/开头:/user/login、也可以不用/开头:user/login

  1. 精准地址
    在@RequestMapping中,可以支持一个或多个地址:{"地址1","地址2"}

    • 一个地址:@RequestMapping("/springmvc/hello")
    • 多个地址:@RequestMapping({"/user/login","/user/regist"})
  2. 模糊地址
    在@RequestMapping中,地址可以支持模糊路径匹配,通过使用通配符,匹配多个类似的地址。

    • *:表示任意一层字符串
      • /user/* 可以表示: user/a 或 user/aaa ,但不能表示: /user/a/b
      • /a/*/b 表示地址以a开头,中间模糊,后面b结尾,如:/a/c/b 但不能表示:/a/c/d/b
    • **:任意层任意字符串
      • /user/** 既可以表示 user/a 和 user/a/b 还可以表示更多层如:/user/a/b/c/d
  3. 类和方法上的区别
    加在类上用于提取通用的访问地址:
    提取前:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    @Controller
    public class UserController {

    @RequestMapping("/user/login")
    @ResponseBody
    public String login(){return "login";}

    @RequestMapping("/user/regist")
    @ResponseBody
    public String register(){return "regist";}
    }

    提取后:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    @Controller
    @RequestMapping("/user")
    public class UserController {

    @RequestMapping("login")
    @ResponseBody
    public String login(){return "login";}

    @RequestMapping("regist")
    @ResponseBody
    public String register(){return "regist";}
    }

    加在方法上就是具体的handler地址,访问:类地址+方法地址

  4. 请求方式指定_1
    客户端的http请求分为多种类型:(get | post | put | delete)
    默认情况使用@RequestMapping(“login”),只要地址正确,任何请求方式都可以访问
    我们可以使用注解的同时指定请求的方式:@RequestMapping(value=”地址”,method=”请求方式”)

    • get:@RequestMapping(value = "",method = RequestMethod.GET)
    • post:@RequestMapping(value = "",method = RequestMethod.POST)
    • put:@RequestMapping(value = "",method = RequestMethod.PUT)
    • delete:@RequestMapping(value = "",method = RequestMethod.DELETE)
    • 如果有不符合请求方式的请求过来,会报405异常
  5. 请求方式指定_2
    我们也可以使用@RequestMapping的HTTP方法特定快捷方式变体:

    • get:@GetMapping("地址")
    • post;@PostMapping("地址")
    • put:@PutMapping("地址")
    • delete:@DeleteMapping("地址")
    • 注意:这几个注解不能用在类上!!

接收参数

param和json参数比较

Param: key=value & key=value
JSON: {key:value,key:value}

  1. 参数编码:
    param 类型的参数会被编码为 ASCII 码。例如,假设 name=john doe,则会被编码为 name=john%20doe。而 JSON 类型的参数会被编码为 UTF-8。
  2. 参数顺序:
    param 类型的参数没有顺序限制。但是,JSON 类型的参数是有序的。JSON 采用键值对的形式进行传递,其中键值对是有序排列的。
  3. 数据类型:
    param 类型的参数仅支持字符串类型、数值类型和布尔类型等简单数据类型。而 JSON 类型的参数则支持更复杂的数据类型,如数组、对象等。
  4. 嵌套性:
    param 类型的参数不支持嵌套。但是,JSON 类型的参数支持嵌套,可以传递更为复杂的数据结构。
  5. 可读性:
    param 类型的参数格式比 JSON 类型的参数更加简单、易读。但是,JSON 格式在传递嵌套数据结构时更加清晰易懂。

总结:单一的数据传递适合使用param类型参数,复杂的数据传递适合使用json,我们常常对get请求使用param参数,post请求使用json参数。

param参数接收

  1. 直接接收
    客户端请求:localhost:8080/param/data?name=zhangsan&age=18
    handler接收参数:
    只要形参数名和类型与传递参数相同,即可自动接收!,可以不传递值,不会报错。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @Controller
    @RequestMapping("/param")
    public class ParamController {

    @RequestMapping("/data")
    @ResponseBody
    public String data(String name,Integer age){
    return "name:"+name+",age:"+age;
    }
    }
  2. @RequestParam注解指定
    使用@RequestParam()可以:

    • 指定任意的请求参数名:value = "指定参数名"
    • 要求请求参数必须传递,要求不必须传递:required = true | false,默认true必须传
    • 为请求参数提供默认值:defaultValue = "默认值"
    • 如果传递参数名错误,必须传的参数没传:400异常

    客户端请求:localhost:8080/param/data1?name=zhangsan&page=1,且name必须传递,page可以不传递,如果不传递值,默认值为1
    handler接收参数:

    1
    2
    3
    4
    5
    6
    7
    8
    @RequestMapping("/data1")
    @ResponseBody
    public String data1(
    @RequestParam(value = "name") String username,
    @RequestParam(required = false,defaultValue = "1") int page){

    return "username:"+username+",page:"+page;
    }
  3. 特殊场景接收参数
    a.一名多值
    前端多选框,递交数据传递的参数就是一个key对应多个值,可以使用集合来接收
    客户端请求:http://localhost:8080/param/data2?hobby=sing&hobby=dance&hobby=ball
    handler接收参数:

    1
    2
    3
    4
    5
    6
    @RequestMapping("/data2")
    @ResponseBody
    public String hobbies(@RequestParam("hobby") List<String> hobbies){
    System.out.println("hobbies:"+hobbies);
    return "oK";
    }

    b.实体对象接收:要求,实体属性名=前端传递参数名
    客户端请求:localhost:8080/data3?name=zhangsan&age=18
    定义一个实体类

    1
    2
    3
    4
    5
    6
    public class User {
    private String name; //实体属性名=前端传递参数名
    private int aage;

    //添加get,set方法
    }

    handler接收参数:

    1
    2
    3
    4
    5
    6
    @RequestMapping("data3")
    @ResponseBody
    public String data3(User user){
    System.out.println(user);
    return user.toString();
    }

路径参数接收

@PathVariable 注解允许将 URL 中的占位符映射到控制器方法中的参数
使用步骤:1.设置动态路径、2.接收动态路口参数。如现在需要将/user/{id}路径的{id}作为参数映射到controller的一个方法参数中:【@PathVariable(value=”方法参数名”),应于路径参数名相同】

1
2
3
4
5
@RequestMapping("user/{id}")
@ResponseBody
public String getUser(@PathVariable Integer id){
return "getUser";
}

json参数接收

前端传递 JSON 数据时,Spring MVC 框架可以使用 @RequestBody 注解来将 JSON 数据转换为 Java 对象。@RequestBody 注解表示当前方法参数的值应该从请求体中获取,并且需要指定 value 属性来指示请求体应该映射到哪个参数上。其使用方式和示例代码如下:

  1. 前端发送JSON数据(示例)如下:
    1
    2
    3
    4
    5
    {
    "name": "张三",
    "age": 18,
    "gender": "男"
    }
  2. 定义一个用于接收 JSON 数据的 Java 类
    1
    2
    3
    4
    5
    6
    public class Student {
    private String name;
    private int age;
    private String gender;
    // 补充 get set 方法
    }
  3. 在控制器中,使用@RequestBody注解来接收 JSON 数据,并将其转换为 Java 对象,例如:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    @Controller
    public class StudentController {

    @RequestMapping("/student/save")
    @ResponseBody
    public String save(@RequestBody Student student){
    return student.toString();
    }
    }
  4. 问题:此时进行测试,发现报错:HTTP状态 415 - 不支持的媒体类型
    原因是不支持json数据类型处理,没有json类型处理的工具,Java原生的api,只支持路径参数和param参数 request.getParameter(“key”); param 不支持 json,json本就是前端的数据格式
  5. 解决:
    a.导入json处理的依赖
    1
    2
    3
    4
    5
    <dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.15.2</version>
    </dependency>
    b.springmvc handlerAdpater配置json转化器,配置类需要明确:
    在你的webmvc配置类加上注解@EnableWebMvc,并不用再写,RequestMappingHandlerMappingRequestMappingHandlerAdapter的bean
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    //SpringMVC对应组件的配置类 [声明SpringMVC需要的组件信息]
    @Configuration
    @EnableWebMvc //json数据处理,必须使用此注解,因为他会加入json处理器
    @ComponentScan("cn.xnj.controller")//TODO: 进行controller扫描
    public class MvcConfig {
    /*@Bean
    public RequestMappingHandlerMapping handlerMapping(){
    return new RequestMappingHandlerMapping();
    }

    @Bean
    public RequestMappingHandlerAdapter handlerAdapter(){
    return new RequestMappingHandlerAdapter();
    }*/
    }
  6. @EnableWebMvc注解效果等同于在 XML 配置中,可以使用 元素!

接收Cookie数据

使用 @CookieValue 注释将 HTTP Cookie 的值绑定到控制器中的方法参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Controller
@RequestMapping("/cookie")
@ResponseBody
public class CookieController {

@RequestMapping("/data")//取得cookie的值
public String data(@CookieValue("cookieName") String value){
System.out.println("value:"+value);
return value;
}

@GetMapping("/save")//保存cookie的值
public String save(HttpServletResponse response){
Cookie cookie = new Cookie("cookieName","root");
response.addCookie(cookie);
return "ok";
}

}

接收请求头数据

使用 @RequestHeader(value="请求头的名字") 批注将请求标头绑定到控制器中的方法参数。
如以下带有标头的请求:

1
2
3
4
5
6
Host                    localhost:8080
Accept text/html,application/xhtml+xml,application/xml;q=0.9
Accept-Language fr,en-gb;q=0.7,en;q=0.3
Accept-Encoding gzip,deflate
Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive 300

获取Host示例:
1
2
3
4
5
6
7
8
9
10
11
@Controller
@RequestMapping("/header")
@ResponseBody
public class HeaderController {

@RequestMapping("/data")
public String data(@RequestHeader("Host") String host){
System.out.println("host:"+host);
return "host:"+host;
}
}

SpringMVC响应数据

页面跳转控制

返回模板视图

这里以jsp例子来示例:

  1. 准备jsp页面和依赖
    pom.xml
    1
    2
    3
    4
    5
    6
    <!-- jsp需要依赖! jstl-->
    <dependency>
    <groupId>jakarta.servlet.jsp.jstl</groupId>
    <artifactId>jakarta.servlet.jsp.jstl-api</artifactId>
    <version>3.0.0</version>
    </dependency>
    jsp页面:创建webapp/WEB-INF/下,避免被外界直接访问到
    这里示例创建在/WEB-INF/views/index.jsp
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
    <title>Title</title>
    </head>
    <body>
    <font color="red">hello jsp:${msg}</font><br>
    ${msg}-》相当于后台的vue格式:{{msg}}
    </body>
    </html>
  2. 创建配置类并配置jsp视图解析器
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    @Configuration
    @EnableWebMvc
    @ComponentScan("cn.xnj.controller")
    public class MvcConfig implements WebMvcConfigurer {

    //配置jsp对应的视图解析器
    @Override
    public void configureViewResolvers(ViewResolverRegistry registry) {
    //视图解析器,指定了视图的前缀和后缀
    registry.jsp("/WEB-INF/views/",".jsp");
    }
    }
    配置 Spring MVC 应用的初始化
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public class MyWebAppInit extends AbstractAnnotationConfigDispatcherServletInitializer {
    @Override
    protected Class<?>[] getRootConfigClasses() {
    return new Class[0];
    }
    @Override
    protected Class<?>[] getServletConfigClasses() {
    return new Class[]{MvcConfig.class};
    }
    @Override
    protected String[] getServletMappings() {
    return new String[]{"/"};
    }
    }
  3. handler返回视图
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @Controller
    @RequestMapping("/jsp")
    public class JspViewsController {

    @GetMapping("/index")
    public String indexJsp(HttpServletRequest request){
    request.setAttribute("msg","hello world!!");
    return "index";
    }
    }
  4. 测试
    部署本地tomcat访问http://localhost:8080/jsp/index

转发和重定向

在 Spring MVC 中,Handler 方法返回值来实现快速转发,可以使用 redirect 或者 forward关键字来实现重定向。
先说总结:

  • 转发:客户端上方的访问地址不会发生变化,使用forward关键字
  • 重定向:客户端上方的访问地址会变化,使用redirect关键字
  • 语法:将方法返回值设置为String,返回值:关键字:/路径
  • 注意:方法或类上都不能用@ResponseBody注解

接4.1.1案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 转发  :返回字符串前  forward:/转发地址
@GetMapping("/forward")
public String forward(){
System.out.println("forward...");
return "forward:/jsp/index";
}
// 访问:localhost:8080/jsp/forward,地址不变,返回index.jsp页面


// 重定向 : 返回字符串前 redirect:/重定向的地址
@GetMapping("/redirect")
public String redirect(){
System.out.println("redirect...");
return "redirect:/jsp/index";
}
// 访问:localhost:8080/jsp/redirect,地址变为:localhost:8080/jsp/index,返回index.jsp页面

返回JSON数据

  1. 导入jackson依赖

    1
    2
    3
    4
    5
    <dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.15.0</version>
    </dependency>
  2. 添加json数据转换器
    在mvc配置类上添加注解:@EnableWebMvc

  3. @ResponseBody

    1. 方法上使用@ResponseBody注解
      在方法上使用 @ResponseBody注解,用于将方法返回的对象序列化为 JSON 或 XML 格式的数据,并发送给客户端。
    2. 上使用 @ResponseBody注解
      在类上加上该注解相当于该类下所有方法都加上了该注解
  4. @RestController
    在类上使用:@RestController == @Controller + @ResponseBody

返回静态资源处理

  1. 静态资源包括:
    纯HTMML文件,图片,CSS文件,javaScript文件

  2. web应用上加入静态资源
    这里我们希望能直接被外部访问到,存放位置:webApp/images/a.png

  3. 编译项目,确认图片在编译后的目录中

  4. 访问静态资源:http://localhost:8080/images/a.png
    报404错误,原因如下:

    • DispatcherServlet 的 url-pattern 配置的是“/”
    • url-pattern 配置“/”表示整个 Web 应用范围内所有请求都由 SpringMVC 来处理
    • 对 SpringMVC 来说,必须有对应的 @RequestMapping 才能找到处理请求的方法
    • 现在 images/mi.jpg 请求没有对应的 @RequestMapping 所以返回 404
  5. 问题解决
    在MvcConfig配置类上开启静态资源处理:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    @Configuration
    @EnableWebMvc
    @ComponentScan("cn.xnj.controller")
    public class MvcConfig implements WebMvcConfigurer {

    //配置jsp对应的视图解析器
    @Override
    public void configureViewResolvers(ViewResolverRegistry registry) {
    //视图解析器,指定了视图的前缀和后缀
    registry.jsp("/WEB-INF/views/",".jsp");
    }

    //开启静态资源处理 <mvc:default-servlet-handler/>
    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
    configurer.enable();
    }
    }

    再次测试访问图片路径即可在浏览器看到图片

RESTFul风格

restful风格设计规范

操作 请求方式 传统风格 REST 风格
查询操作 GET /CRUD/editEmp?empId=2 URL 地址:/CRUD/emp/2
请求方式:GET
保存操作 POST /CRUD/saveEmp URL 地址:/CRUD/emp
请求方式:POST
删除操作 DELETE /CRUD/removeEmp?empId=2 URL 地址:/CRUD/emp/2
请求方式:DELETE
更新操作 PUT /CRUD/updateEmp URL 地址:/CRUD/emp
请求方式:PUT

restful风格案例

接口设计:

功能 接口和请求方式 请求参数 返回值
分页查询 GET /user page=1&size=10 { 响应数据 }
用户添加 POST /user { user 数据 } {响应数据}
用户详情 GET /user/1 路径参数 {响应数据}
用户更新 PUT /user { user 更新数据} {响应数据}
用户删除 DELETE /user/1 路径参数 {响应数据}
条件模糊 GET /user/search page=1&size=10&keywork=关键字 {响应数据}

controller层实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
@RestController
@RequestMapping("/user")
public class UserController {

// 分页查询
@GetMapping
public List<User> getPages(@RequestParam(required = false,defaultValue = "1") int page,
@RequestParam(required = false,defaultValue = "10") int size){
return null;
}

// 添加用户
@PostMapping
public String save(@RequestBody User user){
return "save";
}

//获取用户详情
@GetMapping("/{id}")
public User getdetail(@PathVariable Integer id){
return null;
}

//用户更新
@PutMapping
public String update(@RequestBody User user){
return "update";
}

//用户删除
@DeleteMapping("/{id}")
public String delete(@PathVariable int id){
return "delete";
}

//条件模糊查询
@GetMapping("/search")
public List<User> search(@RequestParam(required = false) String name,
@RequestParam(required = false) Integer age,
String keywork){
return null;
}

}

全局异常处理机制

开发过程中是不可避免地会出现各种异常情况的,例如网络连接异常、数据格式异常、空指针异常等等。
对于异常的处理,一般分为两种方式:
编程式异常处理:try/catch
声明式异常处理:@Throws 、@ExceptionHandler

声明异常处理控制器类
cn.xnj.error

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
//@ControllerAdvice // 可以返回逻辑视图 转发和重定向的
@RestControllerAdvice // @RessponseBody,并且返回json数据
public class GlobalExceptionHandler {

// 当发生算术异常会触发此方法!
@ExceptionHandler(ArithmeticException.class)
public Object handlerArithmeticException(ArithmeticException e){
String msg = e.getMessage();
System.out.println("msg:"+msg);
return msg;
}

//空指针异常
@ExceptionHandler(NullPointerException.class)
public Object handlerNullPointerException(NullPointerException e){

return null;
}

//HTTP 消息不可读异常
@ExceptionHandler(HttpMessageNotReadableException.class)
public Object handlerJsonDateException(HttpMessageNotReadableException e){

return null;
}


/**
* 所有异常都会触发此方法!但是如果有具体的异常处理Handler!
* 具体异常处理Handler优先级更高!
* 例如: 发生NullPointerException异常!
* 会触发handlerNullException方法,不会触发handlerException方法!
*/
@ExceptionHandler(Exception.class)
public Object handlerException(Exception e){

return null;
}
}

记得让注解被扫描到,在配置类中要导入
@ComponentScan({"cn.xnj.controller","cn.xnj.error"})

拦截器

实现步骤:

  • 1.创建一个类实现HandlerInterceptor接口
  • 2.重写该接口里的preHandle,postHandle,afterCompletion方法
  • 3.在配置类中重写addInterceptors方法,并注册我们自定义的拦截器
    • 指定地址拦截:.addPathPatterns("/地址");
    • 排除拦截:.excludePathPatterns("/地址");

a.实现接口,定义拦截器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public class MyInterceptor implements HandlerInterceptor {

/** 在执行控制器(controller)之前执行的方法!,
* 如编码格式设置,登录保护,权限处理
* @param request 请求对象
* @param response 响应对象
* @param handler handler就是我们要调用的方法对象
* @return 返回true表示放行,返回false表示拦截
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("request:"+request+" response:"+response+" handler:"+handler);
return true;
}

/** 在执行控制器(controller)之后执行的方法!没有拦截机制了! 只有preHandle方法返回true会执 行此方法!
* 如对结果处理!敏感词汇检查!
* @param request 请求对象
* @param response 响应对象
* @param handler handler就是我们要调用的方法对象
* @param modelAndView 返回的视图和共享域数据对象
* @throws Exception
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("MyInterceptor.postHandle");
}

/** 整体处理完毕之后执行的方法!在视图渲染完毕之后执行的方法!
* 如资源清理,记录日志, 异常处理!
* @param request
* @param response
* @param handler
* @param ex handler报错了,异常对象
* @throws Exception
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("MyInterceptor.afterCompletion");
}
}

b.注册拦截器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Configuration
@EnableWebMvc
@ComponentScan({"cn.xnj.controller","cn.xnj.error"})
public class MvcConfig implements WebMvcConfigurer {

// 添加拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
//注册拦截器
// 配置方案1:拦截所有请求,包括静态资源
//registry.addInterceptor(new MyInterceptor());

// 配置方案2:指定地址拦截:.addPathPatterns("/地址"),/*表示任意一层字符串,/**表示任意多层
//registry.addInterceptor(new MyInterceptor())
// .addPathPatterns("/user/**");

// 配置方案3:排除拦截:.excludePathPatterns("/地址");
registry.addInterceptor(new MyInterceptor())
.addPathPatterns("/user/**")
.excludePathPatterns("/user/{id}}");
}
}

更多有关拦截器参考文章:拦截器和过滤器

参数效验

JSR 303 是 Java 为 Bean 数据合法性校验提供的标准框架,它已经包含在 JavaEE 6.0 标准中。JSR 303 通过在 Bean 属性上标注类似于 @NotNull、@Max 等标准的注解指定校验规则,并通过标准的验证接口对Bean进行验证。

先总结:1.引入依赖、2.实体类属性添加效验注解、3.handler(@Validated 实体类对象)、4.注解对param和json参数都有效,json参数仍然需要加上@RequestBody注解、5:使用@BindingResult或其他捕获异常方式处理异常

注解 规则
@Null 标注值必须为 null
@NotNull 标注值不可为 null
@AssertTrue 标注值必须为 true
@AssertFalse 标注值必须为 false
@Min(value) 标注值必须大于或等于 value
@Max(value) 标注值必须小于或等于 value
@DecimalMin(value) 标注值必须大于或等于 value
@DecimalMax(value) 标注值必须小于或等于 value
@Size(max,min) 标注值大小必须在 max 和 min 限定的范围内
@Digits(integer,fratction) 标注值值必须是一个数字,且必须在可接受的范围内
@Past 标注值只能用于日期型,且必须是过去的日期
@Future 标注值只能用于日期型,且必须是将来的日期
@Pattern(value) 标注值必须符合指定的正则表达式

JSR 303 只是一套标准,需要提供其实现才可以使用。Hibernate Validator 是 JSR 303 的一个参考实现,除支持所有标准的校验注解外,它还支持以下的扩展注解:

注解 规则
@Email 标注值必须是格式正确的 Email 地址
@Length 标注值字符串大小必须在指定的范围内
@NotEmpty 标注值字符串不能是空字符串
@Range 标注值必须在指定的范围内

注意:包装类型不为空用:@NotNull。字符串不为空且不为””用:@NotBlank。集合类型长度大于0用:@NotEmpty

Spring 4.0 版本已经拥有自己独立的数据校验框架,同时支持 JSR 303 标准的校验框架。Spring 在进行数据绑定时,可同时调用校验框架完成数据校验工作。在SpringMVC 中,可直接通过注解驱动 @EnableWebMvc 的方式进行数据校验。Spring 的 LocalValidatorFactoryBean 既实现了 Spring 的 Validator 接口,也实现了 JSR 303 的 Validator 接口。只要在Spring容器中定义了一个LocalValidatorFactoryBean,即可将其注入到需要数据校验的 Bean中。Spring本身并没有提供JSR 303的实现,所以必须将JSR 303的实现者的jar包放到类路径下。

配置 @EnableWebMvc后,SpringMVC 会默认装配好一个 LocalValidatorFactoryBean,通过在处理方法的入参上标注 @Validated 注解即可让 SpringMVC 在完成数据绑定后执行数据校验的工作。

使用示例

引入依赖:

1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- 校验注解实现-->        
<!-- https://mvnrepository.com/artifact/org.hibernate.validator/hibernate-validator -->
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>8.0.0.Final</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.hibernate.validator/hibernate-validator-annotation-processor -->
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator-annotation-processor</artifactId>
<version>8.0.0.Final</version>
</dependency>

应用校验注解:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.Min;
import org.hibernate.validator.constraints.Length;

/**
* projectName: com.atguigu.pojo
*/
public class User {
//age 1 <= age < = 150
@Min(1)
private int age;

//name 3 <= name.length <= 6
@Length(min = 3,max = 10)
private String name;

//email 邮箱格式
@Email
private String email;

//日期格式 过去日期
@Past
private Data birthday;

//get set 方法...
}

handler标记和绑定错误收集
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
@RestController
@RequestMapping("user")
public class UserController {

/**
* @Validated 代表应用校验注解! 必须添加!
*
* 注意:如果不符合效验规则,会直接向前端抛异常!
* 接收错误绑定信息!自定义返回结果!约定:参数错误->{code:400}->前端
* 捕捉错误,绑定错误信息
* 1.handler(效验对象,BindingResult result)要求:BindingResult需要紧挨着校验对象
* 2.bindresult获取绑定错误
*
* 或者你也可以用全局异常捕获等其他机制处理异常
*/
@PostMapping("save")
public Object save(@Validated @RequestBody User user,
//在实体类参数和 BindingResult 之间不能有任何其他参数, BindingResult可以接受错误信息,避免信息抛出!
BindingResult result){
//判断是否有信息绑定错误! 有可以自行处理!
if (result.hasErrors()){
System.out.println("错误");
String errorMsg = result.getFieldError().toString();
return errorMsg;
}
//没有,正常处理业务即可
System.out.println("正常");
return user;
}
}

跨域问题

跨域:由于浏览器的同源策略限制,向不同源(不同协议,不同域名,不同端口)发送ajax请求会失败
解决办法:在controller上添加@CrossOrigin注解

  1. 在contrller类上面添加@CrossOrigin注解来解决跨域
    @CrossOrigin // 解决跨域问题,允许其他源访问我们的controller
    这个cotroller下的所有方法都可以被外部访问

  2. 在controller下某些方法添加@CrossOrigin注解来解决跨域
    该方法可以被外部访问

SSM整合案例

至少两个容器:
root容器(mapper,service,aop,tx,dataSource,mybatis等)和web容器(controller,拦截器,全局异常等springmvc核心组件)
调用关系:
web容器是root容器的子容器,父子容器关系。子容器可以单向注入父Ioc容器组件,父容器则不行
Ioc初始化方式和配置位置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

//指定root容器对应的配置类
//root容器的配置类
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[] { ServiceJavaConfig.class,MapperJavaConfig.class };
}

//指定web容器对应的配置类 webioc容器的配置类
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[] { WebJavaConfig.class };
}

//指定dispatcherServlet处理路径,通常为 /
@Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}

创建项目引入依赖

注意:创建项目记得改成web项目形式<packaging>war</packaging>
依赖导入:pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
<dependencies>
<!--spring 相关依赖-->
<!-- spring核心容器 ioc/di/(传递)aop -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.0.6</version>
</dependency>

<!-- 注解依赖 jsr250如@Resource -->
<dependency>
<groupId>jakarta.annotation</groupId>
<artifactId>jakarta.annotation-api</artifactId>
<version>2.1.1</version>
</dependency>

<!-- spring aop 已被spring-context传递-->
<!--<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>6.0.6</version>
</dependency>-->

<!-- spring aop 的注解依赖如:@Aspect定义切面类 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>6.0.6</version>
</dependency>

<!-- spring 事务管理:@EnableTransactionManagement,@Transactional -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>6.0.6</version>
</dependency>

<!-- spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>6.0.6</version>
</dependency>

<!-- springwebmvc相关依赖 -->
<!-- spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>6.0.6</version>
</dependency>

<!-- servlet依赖 -->
<dependency>
<groupId>jakarta.platform</groupId>
<artifactId>jakarta.jakartaee-web-api</artifactId>
<version>9.1.0</version>
<scope>provided</scope>
</dependency>

<!-- jsp需要依赖! jstl-->
<dependency>
<groupId>jakarta.servlet.jsp.jstl</groupId>
<artifactId>jakarta.servlet.jsp.jstl-api</artifactId>
<version>3.0.0</version>
</dependency>

<!-- json处理 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.2</version>
</dependency>

<!-- Hibernate校验注解 @Validated-->
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>8.0.0.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator-annotation-processor</artifactId>
<version>8.0.0.Final</version>
</dependency>

<!--mybatis相关依赖 -->
<!-- mybatis依赖 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.11</version>
</dependency>

<!-- mysql数据库驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.25</version>
</dependency>

<!--pagehelper-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.2.0</version>
</dependency>

<!--整合需要-->
<!-- spring-web 整合servlet-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>6.0.6</version>
</dependency>

<!-- mybatis-spring 整合mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>3.0.3</version>
</dependency>

<!-- 数据库连接池 druid-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.8</version>
</dependency>

<!--日志-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.5.6</version>
</dependency>

<!--lombok 自动生成get,set等方法-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.28</version>
</dependency>

<!--测试-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>6.0.6</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>

</dependencies>

数据库和实体类

数据库准备:mysql

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
create database studb ;

use studb;

create table stu_tb
(
id int auto_increment
primary key,
name varchar(32) not null,
gender varchar(1) not null,
age int,
class_name varchar(32)
);

insert into stu_tb(name, gender, age, class_name) VALUES('test','男',80,'高中一班')
,('李四','男',16,'高中二班'),('王五','女',16,'高中一班'),('赵六','女',18,'高中三班')
,('刘七','男',17,'高中二班'),('陈八','女',16,'高中一班'),('杨九','男',18,'高中三班')
,('吴十','男',17,'高中二班')

实体类添加:cn.xnj.pojo

1
2
3
4
5
6
7
8
9
10
11
12
13
@Data //lombok 自动生成get set toString 方法
public class Student {
private Integer id;
@NotEmpty //Hibernate校验注解 不能为空
private String name;
@NotEmpty
@Pattern(regexp = "男|女")
private String gender;
@Range(min = 1,max = 100)
private Integer age;
@NotEmpty
private String className;
}

各配置类和配置文件

数据库连接配置文件:resource/jdbc.properties

1
2
3
4
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/studb
jdbc.username=root
jdbc.password=123456

logback日志输出配置文件:resource/logback.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="true">
<!-- 指定日志输出的位置,ConsoleAppender表示输出到控制台 -->
<appender name="STDOUT"
class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<!-- 日志输出的格式 -->
<!-- 按照顺序分别是:时间、日志级别、线程名称、打印日志的类、日志主体内容、换行 -->
<pattern>[%d{HH:mm:ss.SSS}] [%-5level] [%thread] [%logger] [%msg]%n</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>

<!-- 设置全局日志级别。日志级别按顺序分别是:TRACE、DEBUG、INFO、WARN、ERROR -->
<!-- 指定任何一个日志级别都只打印当前级别和后面级别的日志。 -->
<root level="DEBUG">
<!-- 指定打印日志的appender,这里通过“STDOUT”引用了前面配置的appender -->
<appender-ref ref="STDOUT" />
</root>

<!-- 根据特殊需求指定局部日志级别,可也是包名或全类名。 -->
<logger name="cn.xnj.mybatis" level="DEBUG" />

</configuration>

SpringWebMvc配置类:cn.xnj.config/SpringMvcConfig

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/**
* 1.controller
* 2.全局异常处理器
* 3.handlerMapping,handlerAdapter:`WebMvcConfigurer`
* 4.静态资源处理
* 5.jsp 视图解析器前后缀
* 6.json解析器
* 7.拦截器...
*/
@Configuration
@ComponentScan({"cn.xnj.controller","cn.xnj.exception"})// 1,2
@EnableWebMvc // 3,6
public class SpringMvcConfig implements WebMvcConfigurer{


//4
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();//静态资源处理器,图片,js,css等
}

//5
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
// registry.jsp("/WEB-INF/jsp/",".jsp");// 视图解析器jsp前后缀
}

//7
@Override
public void addInterceptors(InterceptorRegistry registry) {
// registry.addInterceptor();// 拦截器
}
}

SpringConfig配置类:cn.xnj.config/SpringConfig

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
* 1. service
* 2. 开启aop注解的支持 aspect:@Before@After...
* 3. 声明式事务管理:
* a.对应的事务管理器实现[TransactionManager DataSource..Hibrnate..Jpa..]
* b.开启事务注解支持 @Transactional
* 4. 配置事务管理器
* 5. 配置事务注解驱动
*/
@Configuration
@EnableAspectJAutoProxy // 2
@EnableTransactionManagement // 3
@ComponentScan("cn.xnj.service")
@Import({MyBatisConfig.class, DataSourceConfig.class})//导入配置类
public class SpringConfig {

// 4
@Bean
public TransactionManager transactionManager(DataSource dataSource){
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
transactionManager.setDataSource(dataSource);
return transactionManager;
}

}

数据源配置DataSourceConfig:cn.xnj.config/DataSourceConfig

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 数据源配置
@Configuration
@PropertySource("classpath:jdbc.properties")
public class DataSourceConfig {

// 注入数据库属性值
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;


// Durid 连接池
@Bean
public DataSource dataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(driver);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
}

MyBatisConfig配置类:cn.xnj.config/MyBatisConfig

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// 配置MyBatis
@Configuration
public class MyBatisConfig {
// MyBatisConfig 配置SqlSessionFactoryBean
@Bean
public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) throws Exception {
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(dataSource);// 数据源,连接池

//配置mybatis功能
org.apache.ibatis.session.Configuration configuration = factoryBean.getObject().getConfiguration();
configuration.setMapUnderscoreToCamelCase(true);//开启驼峰命名
//configuration.setLogImpl(Slf4jImpl.class);//开启日志输出 已经使用了第三方logback 所以注释掉
//configuration.setAutoMappingBehavior(AutoMappingBehavior.FULL);//开启resultMap自动映射
factoryBean.setConfiguration(configuration);// 配置mybatis功能

// 别名扫描
factoryBean.setTypeAliasesPackage("cn.xnj.pojo");

//添加分页插件
PageInterceptor pageInterceptor = new PageInterceptor();
Properties properties = new Properties();//设置属性
properties.setProperty("helperDialect", "mysql");
pageInterceptor.setProperties(properties);

factoryBean.addPlugins(pageInterceptor);//添加插件

return factoryBean;
}

// MyBatisConfig 配置MapperScannerConfigurer
//mapper接口的扫描器
@Bean
public MapperScannerConfigurer mapperScannerConfigurer() {
MapperScannerConfigurer msc = new MapperScannerConfigurer();
msc.setBasePackage("cn.xnj.mapper");// 扫描mapper接口和mapper.xml文件的路径
return msc;
}
}

SpringMvC初始化配置类:cn.xnj.config/SpringIocInit

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class SpringIocInit extends AbstractAnnotationConfigDispatcherServletInitializer {
//root容器配置类
@Override
protected Class<?>[] getRootConfigClasses() {
//return new Class[]{SpringConfig.class, DataSourceConfig.class, MyBatisConfig.class};
return new Class[]{SpringConfig.class};//SpringConfig.class导入了DataSourceConfig.class, MyBatisConfig.class
}

//webioc容器配置类指定
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{SpringMvcConfig.class};
}

//dispatcherServlet的拦截路径
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
}

工具类

规范返回结果Result:cn.xnj.utils

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
//返回结果类
public class Result {

private int code = 200; //200成功状态码

private String msg ; //返回信息

private Object data; //返回具体数据


public static Result success(Object data){
Result result = new Result();
result.data = data;
result.msg="操作成功";
return result;
}


public static Result success(Object data,String msg){
Result result = new Result();
result.data = data;
result.msg=msg;
return result;
}

public static Result error(String msg){
Result result = new Result();
result.code = 500; //错误码
result.msg = msg; //错误状态
return result;
}

public int getCode() {
return code;
}

public void setCode(int code) {
this.code = code;
}

public void setMsg(String msg){
this.msg=msg;
}

public String getMsg(){
return msg;
}

public Object getData() {
return data;
}

public void setData(Object data) {
this.data = data;
}
}

分页工具类PageBean:cn.xnj.utils

1
2
3
4
5
6
7
8
9
10
// 分页工具类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class PageBean<T> {
private int currentPage; // 当前页码
private int pageSize; // 每页显示的数据量
private long total; // 总数据条数
private List<T> data; // 当前页的数据集合
}

全局异常管理类

全局异常GlobalExceptionHandler:cn.xnj.exeception
注意生效前提:@ComponentScan("cn.xnj.exception")

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
@RestControllerAdvice //开启
@Slf4j // Lombok 库提供的注解,用于自动生成日志记录器
public class GlobalExceptionHandler {
// 当发生算术异常会触发此方法!
@ExceptionHandler(ArithmeticException.class)
public Result handlerArithmeticException(ArithmeticException e){
String msg = e.getMessage();
log.info("ArithmeticException:{}",msg);
return Result.error(msg);
}

//空指针异常
@ExceptionHandler(NullPointerException.class)
public Result handlerNullPointerException(NullPointerException e){
String msg = e.getMessage();
log.info("NullPointerException:{}",msg);
return Result.error(msg);

}

//sql 异常
@ExceptionHandler(SQLException.class)
public Result handlerSQLException(Exception e){
String msg = e.getMessage();
log.info("SQLException:{}",msg);
return Result.error(msg);
}


/**
* 所有异常都会触发此方法!但是如果有具体的异常处理Handler!
* 具体异常处理Handler优先级更高!
* 例如: 发生NullPointerException异常!
* 会触发handlerNullException方法,不会触发handlerException方法!
*/
@ExceptionHandler(Exception.class)
public Result handlerException(Exception e){
String msg = e.getMessage();
log.info("Exception:{}",msg);
return Result.error(msg);
}
}

表现层controller和业务逻辑层service

controller层:cn.xnj.controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
@CrossOrigin // 解决跨域问题,允许其他源访问我们的controller
@RestController
@RequestMapping("/student")
@Slf4j //lombok 提供
public class StudentController {

@Autowired
private StudentService studentService;

// 分页条件(可选)模糊查询
//GetMapping http://localhost:8080/student/1/4?gender=男
@GetMapping("/{currentPage}/{pageSize}")
public Result getPage(@PathVariable(name ="currentPage") int currentPage,
@PathVariable(name ="pageSize") int pageSize,
Student student){
PageBean pageBean = studentService.getPage(currentPage, pageSize,student);
log.info("查询的数据为 :{}",pageBean);
return Result.success(pageBean);
}

//学生删除
//DeleteMapping http://localhost:8080/student/9
@DeleteMapping("/{id}")
public Result delete(@PathVariable Integer id){
int rows = studentService.deleteById(id);
if (rows>0){
return Result.success(rows);
}
return Result.error("删除失败");
}


/** 添加学生
* PostMapping http://localhost:8080/student
* {
* "name":"阿柒",
* "gender":"男",
* "age":20,
* "className":"高中三班"
* }
*/
@PostMapping
public Result save(@Validated @RequestBody Student student , BindingResult result){
if (result.hasErrors()){
//String msg = result.getFieldError().getDefaultMessage();
return Result.error("必要参数为空或格式错误");
}
studentService.save(student);
return Result.success(student);
}

/**更新学生信息
*@PutMapping http://localhost:8080/student
*{
* "id":1,
* "name":"刘坤",
* "age":16
* }
*/
@PutMapping
public Result update( @RequestBody Student student){
if(student.getId()==null){
return Result.error("操作失败,id不能为空");
}
int rows=studentService.update(student);
if(rows==0){
return Result.error("保存修改失败,未知错误");
}
return Result.success(rows);
}

}

service层:cn.xnj.service/impl

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
//接口类 cn.xnj.service
public interface StudentService {

//分页查询
public PageBean getPage(int currentPage, int pageSize, Student student);

// 根据id删除
int deleteById(Integer id);

//添加学生信息
public int save(Student student);

//更新学生信息
int update(Student student);
}



//实现类 cn.xnj.service.impl
@Service
public class StudentServiceImpl implements StudentService {

@Autowired
private StudentMapper studentMapper;

// 分页查询
@Override
public PageBean getPage(int currentPage, int pageSize, Student student) {
//开启分页
PageHelper.startPage(currentPage, pageSize);
//查询
List<Student> studentList = studentMapper.queryList(student);
//分页数据装配
PageInfo<Student> info = new PageInfo<>(studentList);
// 封装数据
PageBean<Student> pageBean = new PageBean<>(currentPage, pageSize, info.getTotal(), info.getList());
return pageBean;
}

// 根据id删除
@Override
public int deleteById(Integer id) {
return studentMapper.deleteById(id);
}

//添加学生
@Override
public int save(Student student) {
return studentMapper.insert(student);
}

//更新学生信息
@Override
public int update(Student student) {

return studentMapper.update(student);
}
}

数据访问层Mapper.xml,Mapper

XxxMapper:cn.xnj.mapper

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Mapper
public interface StudentMapper {

//添加
int insert(Student student);

//分页查询
List<Student> queryList(Student student);

//根据id删除
@Delete("delete from stu_tb where id = #{id}")
int deleteById(Integer id);

//更新学生信息
int update(Student student);
}

XxxMapper:resource/cn/xnj/mapper

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- namespace=接口类的全限定名,这样实现对应 -->
<mapper namespace="cn.xnj.mapper.StudentMapper">

<!--分页 条件(可选)模糊查询-->
<select id="queryList" resultType="pojo.Student">
select * from stu_tb
<where>
<if test="name!= null">
name like concat('%',#{name},'%')
</if>
<if test="gender!= null">
and gender = #{gender}
</if>
<if test="age!= null">
and age = #{age}
</if>
<if test="className!=null">
and class_name = #{className}
</if>
</where>
</select>

<insert id="insert" useGeneratedKeys="true" keyProperty="id">
insert into stu_tb(name,gender,age,class_name)
values (#{name},#{gender},#{age},#{className})
</insert>

<update id="update">
update stu_tb
<set>
<if test="name!=null">
name = #{name},
</if>
<if test="gender!=null">
gender = #{gender},
</if>
<if test="age!=null">
age = #{age},
</if>
<if test="className!=null">
class_name = #{className},
</if>
</set>
where id = #{id}
</update>
</mapper>