老冯笔记SpringMVC
1.SpringMVC简介
SpringMVC是一种基于Java实现MVC模型的轻量级Web框架,SpringMVC已经成为了目前最主流的MVC框架之一,它通过一套注解,让一个简单的Java类成为处理请求的控制器,而无需实现任何接口,同时它还支持RESTful编程风格的请求。
Spring框架是什么?
SpringMVC就是一个mvc的框架,基于一套注解可以让普通类变成控制器
作用是什么?
简化开发,提升开发效率
2.入门案例
1.创建maven工程,安装javatoweb插件,然后转换为web模块
2.导入maven坐标
<?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">
<modelVersion>4.0.0</modelVersion>
<groupId>com.darksnow</groupId>
<artifactId>SpringMVC-Demo</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<!-- servlet依赖,因为springmvc底层还是使用servlet -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!-- SpringMVC的依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
</dependencies>
</project>
3.定义处理请求的功能类
package com.darksnow.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
//SpringMVC特点:可以让普通类使用@Controller注解就能马上变成一个控制器
@Controller
public class UserController {
/**
@RequestMapping 该注解就是用于设置方法的访问路径
注意:如果SpringMVC的方法需要直接返回一个字符串或者一个json对象,那么都需要在方法上添加@ResponseBody注解
*/
@ResponseBody
@RequestMapping("/save")
public String save(){
System.out.println("save方法被调用了......");
return "Hi SpringMVC~";
}
}
注意事项:
对于SpringMVC而言,Controller方法返回值默认表示要跳转的页面,没有对应的页面就会报错,如果不想跳转页面而是响应数据,那么就需要在方法上使用@ResponseBody注解
4.编写SpringMVC配置类,加载处理请求的Bean
package com.darksnow.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
/**
* SpringMVC配置类,本质上还是一个Spring配置类
*/
@Configuration
@ComponentScan("com.darksnow.controller")
public class SpringMvcConfig {
}
5.加载SpringMVC配置,并设置SpringMVC请求拦截的路径
package com.darksnow.config;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.support.AbstractDispatcherServletInitializer;
/*
tomcat在启动的时候,其实默认是会加载web.xml文件,只不过我们目前都是使用了注解开发,
所以web.xml文件目前没有任何内容,那么我们就需要定义一个类去取代web.xml文件,
让tomcat在启动的时候就加载该配置类
注意:如果一个类需要去取代web.xml文件,那么该类必须要继承AbstractDispatcherServletInitializer
*/
public class WebConfig extends AbstractDispatcherServletInitializer {
/**
* 创建SpringMVC容器,加载SpringMVC的配置文件
*/
@Override
protected WebApplicationContext createServletApplicationContext() {
//创建容器
AnnotationConfigWebApplicationContext webApplicationContext = new AnnotationConfigWebApplicationContext();
webApplicationContext.register(SpringMvcConfig.class);
return webApplicationContext;
}
/**
* 配置SpringMVC能够处理的路径
*/
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
@Override
protected WebApplicationContext createRootApplicationContext() {
return null;
}
}
6.运行结果
3.入门案例工作流程分析
3.1 启动服务器初始化过程
1.tomcat服务器启动,执行自己编写的WebConfig类,初始化web容器
2.执行createServletApplicationContext方法,创建webApplicationContext对象
3.加载自己编写的SpringMvcConfig配置类
4.执行@ComponentSca加载对应的bean
5.加载UserController,每个@RequestMapping的值对应一个具体的方法
6.执行WebConfig类中的getServletMappings方法,定义所有请求都通过SpringMVC
3.2 单次请求过程
1.发送请求http://localhost:8080/save
2.web容器(tomcat)发现所有请求都经过SpringMVC,讲请求交给SpringMVC处理
3.解析请求路径/save
4.由/save匹配执行对应的save()
5.执行save()
6.检测到有@ResponseBody直接将save()方法的返回值作为响应体返回给请求方
4.Controller加载控制与业务bean加载控制
例子
package com.darksnow.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.stereotype.Controller;
/**
* SpringMVC配置类,本质上还是一个Spring配置类
*/
@Configuration
//@ComponentScan("com.darksnow.controller")
/*
excludeFilters:
排除扫描路径中加载的bean,需要指定类别(type)与具体项(classes)
type默认取值就是注解类型,@ComponentScan.Filter是注解内部的注解
includeFilters:
加载指定的bean,需要指定type,与classes
*/
@ComponentScan(value = "com.darksnow",
excludeFilters = @ComponentScan.Filter(
type = FilterType.ANNOTATION,
classes = Controller.class
)
)
public class SpringMvcConfig {
}
5.分开加载
SpringMVC配置类
package com.darksnow.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
/**
* SpringMVC配置类,本质上还是一个Spring配置类
*/
@Configuration
@ComponentScan("com.darksnow.controller")
public class SpringMvcConfig {
}
Spring配置类
package com.darksnow.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
/**
* Spring的配置类
*/
@Configuration
@ComponentScan("com.darksnow.service")
public class SpringConfig {
}
我们可以将SpringMVC与Spring分开写配置类,但却同时加载两个配置类
package com.darksnow.config;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.support.AbstractDispatcherServletInitializer;
/*
tomcat在启动的时候,其实默认是会加载web.xml文件,只不过我们目前都是使用了注解开发,
所以web.xml文件目前没有任何内容,那么我们就需要定义一个类去取代web.xml文件,
让tomcat在启动的时候就加载该配置类
注意:如果一个类需要去取代web.xml文件,那么该类必须要继承AbstractDispatcherServletInitializer
*/
public class WebConfig extends AbstractDispatcherServletInitializer {
/**
* 创建SpringMVC容器,加载SpringMVC的配置文件
*/
@Override
protected WebApplicationContext createServletApplicationContext() {
//创建容器
AnnotationConfigWebApplicationContext webApplicationContext = new AnnotationConfigWebApplicationContext();
webApplicationContext.register(SpringMvcConfig.class);
return webApplicationContext;
}
/**
* 配置SpringMVC能够处理的路径
*/
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
/**
* 加载Spring的配置
*/
@Override
protected WebApplicationContext createRootApplicationContext() {
AnnotationConfigWebApplicationContext webApplicationContext = new AnnotationConfigWebApplicationContext();
webApplicationContext.register(SpringConfig.class);
return webApplicationContext;
}
}
service层代码
package com.darksnow.service;
public interface UserService {
void save();
}
------------------------------------------------------
package com.darksnow.service.impl;
import com.darksnow.service.UserService;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService {
@Override
public void save() {
System.out.println("业务侧:保存用户");
}
}
UserController
package com.darksnow.controller;
import com.darksnow.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
//SpringMVC特点:可以让普通类使用@Controller注解就能马上变成一个控制器
@Controller
public class UserController {
@Autowired
private UserService userService;
@ResponseBody
@RequestMapping(value = "/save",produces = "application/json;charset=utf-8")
public String save(){
System.out.println("控制器:调用保存");
userService.save();
return "添加成功~";
}
}
6.Servlet容器初始化的简写格式
Spring3.2开始引入一个简易的WebApplicationInitializer实现类,AbstractAnnotationConfigDispatcherServletInitializer
package com.darksnow.config;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
/**
* Servlet容器初始化的配置类
* AbstractAnnotationConfigDispatcherServletInitializer 专门针对注解的配置编写的抽象类
*/
public class WebConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
/**
* 读取Spring的配置类
*/
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] {SpringConfig.class};
}
/**
* 读取SpringMVC的配置类
*/
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[] {SpringMvcConfig.class};
}
/**
* 指定拦截的访问地址
*/
@Override
protected String[] getServletMappings() {
return new String[] {"/"};
}
}
7.PostMan
7.1 PostMan介绍
PostMan是一款强大的网页调试与发送网页HTTP请求的工具,它的作用是常用于进行接口的测试。
7.2 PostMan使用
创建集合
发送请求获取json数据
保存当前请求
8.请求
8.1 请求映射路径
package com.darksnow.controller;
import com.darksnow.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
/*
@RequestMapping注解是用于设置请求路径的
@RequestMapping注解可以用于类上与方法上
一个Controller层的方法的路径 = http://localhost:8080/类上的路径/方法上的路径
类上一般是否会设置访问路径?
一般都会设置,因为代表了模块名字
*/
@Controller
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@ResponseBody
@RequestMapping(value = "/save",produces = "application/json;charset=utf-8")
public String save(){
System.out.println("控制器:调用保存");
userService.save();
return "添加成功~";
}
}
8.2 请求参数(GET/POST)
GET请求传递普通参数
普通参数:url地址传参,地址参数名与形参变量名相同,定义形参即可接收url地址的参数
package com.darksnow.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class ParamController {
/*
@GetMapping 这个注解专门用于get请求方式
使用SpringMVC获取参数的时候不需要再使用getParameter方法了,以后接收
请求参数,只需要在方法上声明形参即可。SpringMVC会自动帮你封装。
注意:响应数据如果出现中文,需要通知浏览器使用指定编码的码表去解码。
*/
@RequestMapping(value = "/commonParam",produces = "application/json;charset=utf-8",method = RequestMethod.GET)
@ResponseBody
public String commonParam(String name,int age){
System.out.println("姓名:" + name);
System.out.println("年龄:" + age);
return "数据接收完毕!";
}
}
POST请求传递普通参数
普通参数:form表单post请求传参,表单参数名与形参变量名相同,定义形参即可接收参数
package com.darksnow.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
@Controller
public class ParamController {
/*
@PostMapping 这个注解专门用于post请求方式
使用SpringMVC获取参数的时候不需要再使用getParameter方法了,以后接收
请求参数,只需要在方法上声明形参即可。SpringMVC会自动帮你封装。
注意:响应数据如果出现中文,需要通知浏览器使用指定编码的码表去解码。
*/
@RequestMapping(value = "/commonParam",produces = "application/json;charset=utf-8",method = RequestMethod.POST)
@ResponseBody
public String commonParam(String name,int age){
System.out.println("姓名:" + name);
System.out.println("年龄:" + age);
return "数据接收完毕!";
}
}
问题:我们发现,POST请求传递的参数如果包含中文那么就会出现中文乱码问题
POST请求中文乱码处理
在加载SpringMVC配置的配置类中可以指定字符过滤器
package com.darksnow.config;
import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
import javax.servlet.Filter;
/**
* Servlet容器初始化的配置类
* AbstractAnnotationConfigDispatcherServletInitializer 专门针对注解的配置编写的抽象类
*/
public class WebConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
/**
* 读取Spring的配置类
*/
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] {SpringConfig.class};
}
/**
* 读取SpringMVC的配置类
*/
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[] {SpringMvcConfig.class};
}
/**
* 指定拦截的访问地址
*/
@Override
protected String[] getServletMappings() {
return new String[] {"/"};
}
/**
* 解决全局乱码问题,需要重写getServletFilters方法,
* SpringMVC已经帮你写好了一个全局乱码过滤器的类CharacterEncodingFilter
*/
@Override
protected Filter[] getServletFilters() {
CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
characterEncodingFilter.setEncoding("utf-8");
return new Filter[]{characterEncodingFilter};
}
}
8.3 五种类型参数传递
普通参数
普通参数:当请求参数名与形参变量名不同,使用@RequestParam绑定参数关系
package com.darksnow.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
@Controller
public class ParamController {
/*
注意:SpringMVC默认情况只会把url请求参数名与方法形参名相同的数据进行封装,如果不同名是不能够封装的
如果方法形参名与url请求参数名不对应,那么解决方案为:使用@RequestParam注解来解决
注意:如果方法的形参个数与传递过来的参数个数不匹配,没有传递过来的参数会有一个默认值,null
@RequestParam注解常用的属性:
name:指定参数的名字
defaultValue:指定默认值,如果浏览器不传递该参数,则使用默认值
required:指定该参数是否必须要传递参数,如果当required=true时,且没有defaultValue时,不传递参数会报错!
*/
@RequestMapping(value = "/diffName",produces = "application/json;charset=utf-8",method = RequestMethod.GET)
@ResponseBody
public String diffName(@RequestParam(name = "username") String name,@RequestParam(defaultValue = "20",required = true) Integer age) {
System.out.println("姓名:" + name);
System.out.println("年龄:" + age);
return "数据接收完毕!";
}
}
POJO类型参数
POJO参数:url请求参数名与方法形参对象属性名相同,定义POJO类型形参即可接收参数
package com.darksnow.pojo;
public class User {
private String name;
private Integer age;
//省略getter,setter,toString
}
package com.darksnow.controller;
import com.darksnow.pojo.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
@Controller
public class ParamController {
/*
如果参数的名字与对象的属性名相同,那么就可以直接使用对象去接收,属性会一一对应。
*/
@RequestMapping(value = "/commonPojo",produces = "application/json;charset=utf-8",method = RequestMethod.GET)
@ResponseBody
public String commonPojo(User user){
System.out.println("对象的数据:" + user);
return "数据接收完毕!";
}
}
注意事项:请求参数key的名称要和POJO中的属性名称一致,否则无法封装
嵌套POJO类型参数
POJO对象中包含POJO对象
package com.darksnow.pojo;
public class User {
private String name;
private Integer age;
private Address address;
//省略getter,setter,toString
}
package com.darksnow.pojo;
public class Address {
private String province;
private String city;
//省略getter,setter,toString
}
嵌套POJO参数:url请求参数名与方法形参对象属性名相同,按照对象层次结构关系即可接收嵌套POJO属性参数
package com.darksnow.controller;
import com.darksnow.pojo.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
@Controller
public class ParamController {
/*
形参类型是POJO,SpringMVC如果形参是一个实体类对象会自动把属性给封装到对象
要求:形参名字必须与实体类的属性的名字一致,SpringMVC底层帮你们设置请求参数到对象的时候依赖的是setter方法
*/
@RequestMapping(value = "/pojoContainPojoParam",produces = "application/json;charset=utf-8",method = RequestMethod.GET)
@ResponseBody
public String pojoContainPojoParam(User user) {
System.out.println("接收的请求参数,姓名:" + user.getName() + " 年龄:" + user.getAge() + " 地址:" + user.getAddress());
return "success";
}
}
数组类型参数
数组参数:url请求参数名与形参对象属性名相同且请求参数为多个,定义数组类型即可接收参数。
package com.darksnow.controller;
import com.darksnow.pojo.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import java.util.Arrays;
@Controller
public class ParamController {
@RequestMapping(value = "/arrayParam",produces = "application/json;charset=utf-8",method = RequestMethod.GET)
@ResponseBody
public String arrayParam(String[] likes){
System.out.println("数组的数据:" + Arrays.toString(likes));
return "success";
}
}
集合类型参数
集合保存普通参数:请求参数名与形参集合对象名相同且请求参数为多个,@RequestParam绑定参数关系
package com.darksnow.controller;
import com.darksnow.pojo.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import java.util.Arrays;
import java.util.List;
@Controller
public class ParamController {
/*
传递的参数是复选框,一个名字有多个值的情况下就可以使用集合或者数组类型接收
注意:如果你使用List集合接收请求参数,必须使用@RequestParam注解
*/
@RequestMapping(value = "/listParam",produces = "application/json;charset=utf-8",method = RequestMethod.GET)
@ResponseBody
public String listParam(@RequestParam List<String> likes){
System.out.println("兴趣爱好为:" + likes);
return "success";
}
}
8.4 JSON数据参数传递
1.JSON数据参数介绍
- json普通数组:["","","",...]
- json对象:
- json对象数组:[{key:value,...},{key:value,...}]
2.传递json普通数组
添加json数据转换相关坐标
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.8</version>
</dependency>
设置发送json数据(请求body中添加json数据)
开启自动转换json数据的支持
package com.darksnow.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
/**
* SpringMVC配置类,本质上还是一个Spring配置类
*/
@Configuration
@ComponentScan("com.darksnow.controller")
@EnableWebMvc //开启json数据类型进行自动转换
public class SpringMvcConfig {
}
注意事项:
@EnableWebMvc注解功能强大,该注解整合了多个功能,此处仅使用其中一部分功能,即json数据类型进行自动转换
在Controller中编写方法接收json参数
package com.darksnow.controller;
import com.darksnow.pojo.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import java.util.Arrays;
import java.util.List;
@Controller
public class ParamController {
/*
请求参数是json数组
注意:如果请求参数是json格式,需要在请求参数的前面添加@RequestBody
*/
@RequestMapping(value = "/listParamForJson",produces = "application/json;charset=utf-8",method = RequestMethod.GET)
@ResponseBody
public String listParamForJson(@RequestBody String[] likes){
System.out.println("数组的数据:" + Arrays.toString(likes));
return "success";
}
}
3.@RequestBody注解介绍
- 名称:@RequestBody
- 类型:形参注解
- 位置:SpringMVC控制器方法形参定义的前面
- 作用:将请求中请求体所包含的数据传递给请求参数,此注解一个处理器方法只能使用一次
4.传递json对象
- pojo参数:json数据与形参对象属性名相同,定义pojo类型形参即可接收参数
package com.darksnow.controller;
import com.darksnow.pojo.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import java.util.Arrays;
import java.util.List;
@Controller
public class ParamController {
@RequestMapping(value = "/pojoParamForJson",produces = "application/json;charset=utf-8",method = RequestMethod.GET)
@ResponseBody
public String pojoParamForJson(@RequestBody User user){
System.out.println("json对象的数据:" + user);
return "success";
}
}
5.传递json对象数组
package com.darksnow.controller;
import com.darksnow.pojo.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import java.util.Arrays;
import java.util.List;
@Controller
public class ParamController {
@RequestMapping(value = "/pojoObjArrForJson",produces = "application/json;charset=utf-8",method = RequestMethod.GET)
@ResponseBody
public String pojoObjArrForJson(@RequestBody User[] users){
System.out.println("json对象数组的数据:" + Arrays.toString(users));
return "success";
}
}
8.5 日期类型参数传递
package com.darksnow.controller;
import com.darksnow.pojo.User;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
@Controller
public class ParamController {
/**
* SpringMVC默认请求是可以将字符串转换为Date类型数据,但是默认只会把
* yyyy/MM/dd 这种格式的字符串进行类型转换
*
* 因为前端传递字符串日期的时候,格式不是一定的,那么我们可以通过下面的注解来解决格式不一致问题
* @DateTimeFormat 该注解可以设置日期类型的格式,默认是yyyy/MM/dd
*
*/
@RequestMapping(value = "/dateParam",produces = "application/json;charset=utf-8",method = RequestMethod.GET)
@ResponseBody
public String dateParam(Date date,@DateTimeFormat(pattern = "yyyy-MM-dd") Date date1){
System.out.println("小雅的生日:" + date);
System.out.println("小兮的生日:" + date1);
return "success";
}
}
@DateTimeFormat注解介绍
- 类型:形参注解
- 位置:SpringMVC控制器方法形参前面
- 作用:设置日期时间型数据格式
- 属性:pattern,指定日期时间格式字符串
注意事项
传递日期类型参数必须在配置类上使用@EnableWebMvc注解。其功能之一:根据类型匹配对应的类型转换器
9.响应
9.1 响应页面
package com.darksnow.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class ResponseController {
/*
如果一个方法不添加@ResponseBody注解,那么该方法的返回值就是一个跳转页面名称。
*/
@RequestMapping("/jsp")
public String toJsp() {
return "Hello.jsp";
}
}
<%--
该页面在webapp这个目录下,名为Hello.jsp
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>Hi,JSP页面.......</h1>
</body>
</html>
9.2 文本数据
package com.darksnow.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class ResponseController {
@RequestMapping(value = "/text",produces = "application/json;charset=utf-8")
@ResponseBody
public String responseText() {
return "呵呵~";
}
}
9.3 json数据
package com.darksnow.controller;
import com.darksnow.pojo.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class ResponseController {
/*
如果你需要返回一个json数据,那么直接返回一个java对象即可
SpringMVC会自动帮你把java对象转为json
*/
@RequestMapping("/json")
@ResponseBody
public User toJson() {
User user = new User();
user.setName("小雅");
user.setAge(18);
return user;
}
}
注意:需要添加jackson-databind依赖以及在SpringMvcConfig配置类上添加@EnableWebMvc注解
10.RESTFul风格
10.1 RESTFul介绍
作用:简化url的书写格式,同一个url处理不同的请求
- REST(Representational State Transfer),表现形式状态转换
- 传统风格资源描述形式:http://localhost/user/getById?id=1
- REST风格描述形式:http://localhost/user/1
- 优点:
- 隐藏资源的访问行为,无法通过地址得知对资源是何种操作
- 简化书写
既然它描述的是个设计风格,那什么样的接口规范才算符合它的风格呢?就需要看Restful其中的两个特点:
1.每一个URI代表1种资源:http://localhost/user
2.客户端使用GET,POST,PUT,DELETE四个表示操作方式的动词对服务器资源进行操作
(1)GET用来获取资源
(2)POST用来新建资源
(3)PUT用来更新资源
(4)DELETE用来删除资源
10.2 快速入门
package com.darksnow.controller;
import com.darksnow.pojo.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
@Controller
@RequestMapping(produces = "application/json;charset=utf-8")
public class UserController {
/*
添加请求方式:post
用method = RequestMethod.POST来限制请求方式
你可以不加@RequestBody,但是在postman得选择Body里面的x-www-form-urlencoded
如果加了这个注解,你要选择raw,数据要为json的形式
*/
@RequestMapping(value = "/user",method = RequestMethod.POST)
@ResponseBody
public String add(@RequestBody User user){
System.out.println("添加操作..." + user);
return "添加";
}
/*
修改请求方式为:PUT
在postman测试的时候也得用PUT
*/
@RequestMapping(value = "/user",method = RequestMethod.PUT)
@ResponseBody
public String update(@RequestBody User user){
System.out.println("更新操作..." + user);
return "更新";
}
/*
删除请求方式为:DELETE
postman测试的时候,用DELETE方式,地址为:http://localhost:8080/user/1
{id} 参数占位符
@PathVariable:该注解的作用就是获取访问路径上的数据给到变量,占位符的名字与变量名一致,则注解可以省略名字不写
*/
@RequestMapping(value = "/user/{id}",method = RequestMethod.DELETE)
@ResponseBody
public String delete(@PathVariable("id") Integer id){
System.out.println("删除操作..." + id);
return "删除";
}
/*
查询的请求方式:get
postman测试的时候,用GET方式,地址为:http://localhost:8080/user/1
*/
@RequestMapping(value = "/user/{id}",method = RequestMethod.GET)
@ResponseBody
public String findById(@PathVariable("id") Integer id){
System.out.println("查询操作..." + id);
return "根据一个id查询";
}
/*
查询的请求方式:get
postman测试的时候,用GET方式,地址为:http://localhost:8080/user
*/
@RequestMapping(value = "/user",method = RequestMethod.GET)
@ResponseBody
public String findAll(){
System.out.println("查询全部...");
return "查询全部";
}
}
10.3 @PathVariable介绍
- 类型:形参注解
- 位置:SpringMVC控制器方法形参定义前面
- 作用:绑定路径参数与处理器方法形参间的关系,要求路径参数名与形参名一一对应
10.4 @RequestBody,@RequestParam,@PathVariable区别和应用
- 区别:@RequestParam用于接收url地址传参或表单传参,@RequestBody用于接收json数据,@PathVariable用于接收路径参数,使用{参数名称}描述路径参数
- 应用:后期开发中,发送请求参数超过1个,以json格式为主,@ReuqestBody应用较广。如果发送非json数据,选用@RequestParam接收请求参数。采用RESTFul进行开发,当参数数量较少时,例如1个,可以采用@PathVariable接收请求路径变量,通常用于传递id值。
10.5 快速入门中的问题
问题1:每个方法的@RequestMapping注解中都定义了访问路径/user,重复性太高。
解决:在Controller类上使用@RequestMapping定义共同的访问路径
问题2:每个方法的@RequestMapping注解中都要使用method属性定义请求方式,重复性太高。
解决:用具体的Mapping来替代RequestMapping
问题3:每个方法响应json都需要加上@ResponseBody注解,重复性太高。
解决:在类上使用@RestController,因为它是一个组合注解,@RestController = @ResponseBody+@Controller
package com.darksnow.controller;
import com.darksnow.pojo.User;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping(value = "/user",produces = "application/json;charset=utf-8")
public class UserController {
/*
添加请求方式:post
用method = RequestMethod.POST来限制请求方式
你可以不加@RequestBody,但是在postman得选择Body里面的x-www-form-urlencoded
如果加了这个注解,你要选择raw,数据要为json的形式
*/
@PostMapping
public String add(@RequestBody User user){
System.out.println("添加操作..." + user);
return "添加";
}
/*
修改请求方式为:PUT
在postman测试的时候也得用PUT
*/
@PutMapping
public String update(@RequestBody User user){
System.out.println("更新操作..." + user);
return "更新";
}
/*
删除请求方式为:DELETE
postman测试的时候,用DELETE方式,地址为:http://localhost:8080/user/1
{id} 参数占位符
@PathVariable:该注解的作用就是获取访问路径上的数据给到变量,占位符的名字与变量名一致,则注解可以省略名字不写
*/
@DeleteMapping("/{id}")
public String delete(@PathVariable("id") Integer id){
System.out.println("删除操作..." + id);
return "删除";
}
/*
查询的请求方式:get
postman测试的时候,用GET方式,地址为:http://localhost:8080/user/1
*/
@GetMapping("/{id}")
public String findById(@PathVariable("id") Integer id){
System.out.println("查询操作..." + id);
return "根据一个id查询";
}
/*
查询的请求方式:get
postman测试的时候,用GET方式,地址为:http://localhost:8080/user
*/
@GetMapping
public String findAll(){
System.out.println("查询全部...");
return "查询全部";
}
}
11.RESTFul案例
实体类
package com.darksnow.pojo;
public class Book {
private Integer id;
private String type;
private String name;
private String description;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
@Override
public String toString() {
return "Book{" +
"id=" + id +
", type='" + type + '\'' +
", name='" + name + '\'' +
", description='" + description + '\'' +
'}';
}
}
Controller层代码
package com.darksnow.controller;
import com.darksnow.pojo.Book;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
@RestController
@RequestMapping("/books")
public class BookController {
@PostMapping
public String save(@RequestBody Book book){
System.out.println("save book is ---> " + book);
return "success";
}
@GetMapping
public List<Book> getAll() {
List<Book> bookList = new ArrayList<>();
Book book1 = new Book();
book1.setId(1);
book1.setType("计算机");
book1.setName("疯狂Java讲义");
book1.setDescription("很好的一本Java入门书籍~");
bookList.add(book1);
Book book2 = new Book();
book2.setId(2);
book2.setType("计算机");
book2.setName("Java编程思想");
book2.setDescription("非常推荐的一本好书~");
bookList.add(book2);
Book book3 = new Book();
book3.setId(3);
book3.setType("生活");
book3.setName("小雅的故事");
book3.setDescription("小雅的爱情故事~");
bookList.add(book3);
return bookList;
}
}
复制页面到webapp目录下
对静态资源放行
编写类继承于WebMvcConfigurationSupport,重写addResourceHandlers方法,在类上加上@Configuration注解
package com.darksnow.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
@Configuration
public class SpringMvcSupport extends WebMvcConfigurationSupport {
//设置静态资源访问过滤的,当前类需要设置为配置类,并被扫描加载
//要注意这个类需要被@ComponentScan扫描到
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
//当访问/pages/xxx的时候,会从/pages目录下去查找资源
registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");
registry.addResourceHandler("/js/**") .addResourceLocations("/js/");
registry.addResourceHandler("/css/**").addResourceLocations("/css/");
registry.addResourceHandler("/plugins/**").addResourceLocations("/plugins/");
}
}
12.SSM框架整合(纯注解)
12.1 jdbc.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///darksnow?CharacterEncoding=utf-8
jdbc.username=root
jdbc.password=root
12.2 配置类
package com.darksnow.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.PlatformTransactionManager;
import javax.sql.DataSource;
public class JdbcConfig {
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Bean
public DataSource dataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(driver);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
/*
事务管理器的顶层接口:PlatformTransactionManager
*/
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
dataSourceTransactionManager.setDataSource(dataSource);
return dataSourceTransactionManager;
}
}
package com.darksnow.config;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
public class MyBatisConfig {
@Bean
public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
//扫描实体类,让实体类可以使用别名
sqlSessionFactoryBean.setTypeAliasesPackage("com.darksnow.entity");
sqlSessionFactoryBean.setDataSource(dataSource);
return sqlSessionFactoryBean;
}
/**
* 生成代理对象过程:
* mybatis会使用MapperScannerConfigurer去扫描dao包,每扫到一个接口,就会使用sqlSessionFactoryBean
* 去生成一个接口的代理对象,代理对象会存储到Spring容器中
*/
@Bean
public MapperScannerConfigurer mapperScannerConfigurer() {
MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
//扫描dao
mapperScannerConfigurer.setBasePackage("com.darksnow.dao");
return mapperScannerConfigurer;
}
}
package com.darksnow.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.PropertySource;
@Configuration
@ComponentScan(basePackages = "com.darksnow.service")
@PropertySource("classpath:jdbc.properties")
@Import(value = {MyBatisConfig.class,JdbcConfig.class})
public class SpringConfig {
}
package com.darksnow.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
@Configuration
@ComponentScan("com.darksnow.controller")
@EnableWebMvc
public class SpringMvcConfig {
}
package com.darksnow.config;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
public class ServletConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
protected Class<?>[] getRootConfigClasses() {
return new Class[]{SpringConfig.class};
}
protected Class<?>[] getServletConfigClasses() {
return new Class[]{SpringMvcConfig.class};
}
protected String[] getServletMappings() {
return new String[]{"/"};
}
}
12.3 实体类
package com.darksnow.entity;
public class Book {
private Integer id;
private String type;
private String name;
private String description;
//省略getter,setter,toString
}
package com.darksnow.entity;
/**
* 状态类,里面全是常量
* 当然你可以考虑用枚举的形式来完成
*/
public class Code {
public static final Integer SAVE_OK = 20011;
public static final Integer DELETE_OK = 20021;
public static final Integer UPDATE_OK = 20031;
public static final Integer GET_OK = 20041;
public static final Integer SAVE_ERR = 20010;
public static final Integer DELETE_ERR = 20020;
public static final Integer UPDATE_ERR = 20030;
public static final Integer GET_ERR = 20040;
}
package com.darksnow.entity;
public class Result {
private int status;
private Object obj;
private String msg;
public Result(int status, Object obj, String msg) {
this.status = status;
this.obj = obj;
this.msg = msg;
}
public Result(int status, String msg) {
this.status = status;
this.msg = msg;
}
//省略getter,setter,toString
}
12.4 dao层代码
package com.darksnow.dao;
import com.darksnow.entity.Book;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import java.util.List;
public interface BookDao {
@Insert("insert into tb_book values(null,#{type},#{name},#{description})")
int save(Book book);
@Delete("delete from tb_book where id = #{id}")
int delete(Integer id);
@Update("update tb_book set type=#{type},name=#{name},description=#{description} where id = #{id}")
int update(Book book);
@Select("select * from tb_book")
List<Book> findAll();
}
12.5 service层代码
package com.darksnow.service;
import com.darksnow.entity.Book;
import java.util.List;
public interface BookService {
boolean save(Book book);
boolean delete(Integer id);
boolean update(Book book);
List<Book> findAll();
}
package com.darksnow.service.impl;
import com.darksnow.dao.BookDao;
import com.darksnow.entity.Book;
import com.darksnow.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class BookServiceImpl implements BookService {
@Autowired
private BookDao bookDao;
@Override
public boolean save(Book book) {
return bookDao.save(book) > 0;
}
@Override
public boolean delete(Integer id) {
return bookDao.delete(id) > 0;
}
@Override
public boolean update(Book book) {
return bookDao.update(book) > 0;
}
@Override
public List<Book> findAll() {
return bookDao.findAll();
}
}
12.6 controller层代码
package com.darksnow.controller;
import com.darksnow.entity.Book;
import com.darksnow.entity.Code;
import com.darksnow.service.BookService;
import com.darksnow.entity.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 下面的方法统一返回值结果是为什么?
* 前后端分离开发的情况下,前端人员不清楚后端人员controller的返回值是什么,会给前端人员造成困扰
* 所以我们就统一返回结果。让前后端开发人员具有默契。
*/
@RestController
@RequestMapping("/books")
public class BookController {
@Autowired
private BookService bookService;
@PostMapping
public Result save(@RequestBody Book book) {
return bookService.save(book) ? new Result(Code.SAVE_OK,"添加成功") : new Result(Code.SAVE_ERR,"添加失败");
}
@DeleteMapping("/{id}")
public Result delete(@PathVariable("id") Integer id) {
return bookService.delete(id) ? new Result(Code.DELETE_OK,"删除成功") : new Result(Code.DELETE_ERR,"删除失败");
}
@PutMapping
public Result update(@RequestBody Book book) {
return bookService.update(book) ? new Result(Code.UPDATE_OK,"修改成功") : new Result(Code.UPDATE_ERR,"修改失败");
}
@GetMapping
public Result findAll() {
List<Book> bookList = bookService.findAll();
if (bookList != null && bookList.size() > 0) {
return new Result(Code.GET_OK,bookList,"查询成功");
}else {
return new Result(Code.GET_ERR,bookList,"查询失败");
}
}
}
12.7 pom.xml
<?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">
<modelVersion>4.0.0</modelVersion>
<groupId>com.darksnow</groupId>
<artifactId>SSM-Demo</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<!-- SpringMVC的依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.0</version>
</dependency>
<!-- spring整合mybatis依赖 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.18</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.8</version>
</dependency>
<!-- druid连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.16</version>
</dependency>
</dependencies>
</project>
13.异常处理
问题:项目各个层级均可能出现异常,异常处理代码写在哪一层?
程序开发过程中不可避免的会遇到异常现象,我们不能够让用户看到这样的页面数据
13.1 编写异常处理器
异常处理器的底层原理,就是AOP
@RestControllerAdvice是对Controller进行增强的,可以全局捕获SpringMVC抛出的异常,并匹配相应的@ExceptionHandler中指定的异常类型,重新封装异常信息,将统一格式返回给前端。
这个类所在的包要能够被SpringMVC扫描到
package com.darksnow.controller;
import com.darksnow.entity.Result;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
/**
* 统一异常处理器:
* 作用:一旦发现controller抛出异常,就会去处理
*/
//@ControllerAdvice //异常处理,该注解自带@Component注解
@RestControllerAdvice //异常处理,返回值默认是json,自带@ResponseBody和@Component注解
public class ProjectExceptionHandler {
@ExceptionHandler(Exception.class) //处理的是哪种类型的异常
public Result handlerException(Exception e) {
e.printStackTrace();
return new Result(4444,"您的网络异常,请稍后!");
}
}
13.2 项目异常处理方案
问题:请说出项目当前异常的分类以及对应类型异常该如何处理?
项目异常分类
-
业务异常(BusinessException)
- 规范的用户行为产生的异常
- 不规范的用户行为操作产生的异常
-
系统异常(SystemException)
- 项目运行过程中可预计且无法避免的异常
-
其他异常(Exception)
- 编程人员未预期到的异常
项目异常处理方案
- 业务异常(BusinessException)
- 发送对应消息传递给用户,提醒规范操作
- 系统异常(SystemException)
- 发送固定消息传递给用户,安抚用户
- 发送特定消息给运维人员,提醒维护
- 记录日志
- 其他异常(Exception)
- 发送固定消息传递给用户,安抚用户
- 发送特定消息给编程人员,提醒维护
- 记录日志
13.3 项目异常处理代码实现
自定义系统异常
package com.darksnow.exceptions;
/**
* 自定义系统异常
*/
public class SystemException extends RuntimeException{
private Integer code; //表示是什么类型的异常
public SystemException(Integer code) {
this.code = code;
}
public SystemException(Integer code,String message) {
super(message);
this.code = code;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
}
自定义业务异常
package com.darksnow.exceptions;
/**
* 自定义业务异常
*/
public class BusinessException extends RuntimeException{
private Integer code; //表示是什么类型的异常
public BusinessException(Integer code) {
this.code = code;
}
public BusinessException(Integer code, String message) {
super(message);
this.code = code;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
}
自定义异常编码
package com.darksnow.entity;
/**
* 状态类,里面全是常量
* 当然你可以考虑用枚举的形式来完成
*/
public class Code {
public static final Integer SAVE_OK = 20011;
public static final Integer DELETE_OK = 20021;
public static final Integer UPDATE_OK = 20031;
public static final Integer GET_OK = 20041;
public static final Integer SAVE_ERR = 20010;
public static final Integer DELETE_ERR = 20020;
public static final Integer UPDATE_ERR = 20030;
public static final Integer GET_ERR = 20040;
//异常的状态码(新增)
//接口中的变量默认被public static final修饰,所以它们都是全局常量
public static final Integer SYSTEM_ERR = 50001;
public static final Integer SYSTEM_TIMEOUT_ERR = 50002;
public static final Integer SYSTEM_UNKOWN_ERR = 59999;
public static final Integer BUSINESS_ERR = 60002;
}
出发自定义异常
package com.darksnow.controller;
import com.darksnow.entity.Book;
import com.darksnow.entity.Code;
import com.darksnow.exceptions.BusinessException;
import com.darksnow.service.BookService;
import com.darksnow.entity.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 下面的方法统一返回值结果是为什么?
* 前后端分离开发的情况下,前端人员不清楚后端人员controller的返回值是什么,会给前端人员造成困扰
* 所以我们就统一返回结果。让前后端开发人员具有默契。
*/
@RestController
@RequestMapping("/books")
public class BookController {
@Autowired
private BookService bookService;
@DeleteMapping("/{id}")
public Result delete(@PathVariable("id") Integer id) {
if (id < 0) {
throw new BusinessException(Code.BUSINESS_ERR,"数据有误!!!");
}
return bookService.delete(id) ? new Result(Code.DELETE_OK,"删除成功") : new Result(Code.DELETE_ERR,"删除失败");
}
}
在异常通知类中拦截并处理异常
package com.darksnow.controller;
import com.darksnow.entity.Result;
import com.darksnow.exceptions.BusinessException;
import com.darksnow.exceptions.SystemException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
/**
* 统一异常处理器:
* 作用:一旦发现controller抛出异常,就会去处理
*/
//@ControllerAdvice //异常处理,该注解自带@Component注解
@RestControllerAdvice //异常处理,返回值默认是json,自带@ResponseBody和@Component注解
public class ProjectExceptionHandler {
@ExceptionHandler(SystemException.class) //处理的是哪种类型的异常
public Result handlerSystemException(SystemException e) {
e.printStackTrace();
return new Result(e.getCode(), e.getMessage());
}
@ExceptionHandler(BusinessException.class) //处理的是哪种类型的异常
public Result handlerBusinessException(BusinessException e) {
e.printStackTrace();
return new Result(e.getCode(), e.getMessage());
}
@ExceptionHandler(Exception.class) //处理的是哪种类型的异常
public Result handlerException(Exception e) {
e.printStackTrace();
return new Result(4444,"您的网络异常,请稍后!");
}
}
14.案例开发
14.1 准备工作
将页面导入到webapp下
package com.darksnow.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
@Configuration
public class SpringMvcSupport extends WebMvcConfigurationSupport {
//设置静态资源访问过滤的,当前类需要设置为配置类,并被扫描加载
//要注意这个类需要被@ComponentScan扫描到
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
//当访问/pages/xxx的时候,会从/pages目录下去查找资源
registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");
registry.addResourceHandler("/js/**") .addResourceLocations("/js/");
registry.addResourceHandler("/css/**").addResourceLocations("/css/");
registry.addResourceHandler("/plugins/**").addResourceLocations("/plugins/");
}
}
package com.darksnow.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
@Configuration
@ComponentScan(basePackages = {"com.darksnow.controller","com.darksnow.config"})
@EnableWebMvc
public class SpringMvcConfig {
}
14.2 查询,删除,新建,编辑功能
<!DOCTYPE html>
<html>
<head>
<!-- 页面meta -->
<meta charset="utf-8">
<title>SpringMVC案例</title>
<!-- 引入样式 -->
<link rel="stylesheet" href="../plugins/elementui/index.css">
<link rel="stylesheet" href="../plugins/font-awesome/css/font-awesome.min.css">
<link rel="stylesheet" href="../css/style.css">
</head>
<body class="hold-transition">
<div id="app">
<div class="content-header">
<h1>图书管理</h1>
</div>
<div class="app-container">
<div class="box">
<div class="filter-container">
<el-input placeholder="图书名称" style="width: 200px;" class="filter-item"></el-input>
<el-button class="dalfBut">查询</el-button>
<el-button type="primary" class="butT" @click="openSave()">新建</el-button>
</div>
<el-table size="small" current-row-key="id" :data="dataList" stripe highlight-current-row>
<el-table-column type="index" align="center" label="序号"></el-table-column>
<el-table-column prop="type" label="图书类别" align="center"></el-table-column>
<el-table-column prop="name" label="图书名称" align="center"></el-table-column>
<el-table-column prop="description" label="描述" align="center"></el-table-column>
<el-table-column label="操作" align="center">
<template slot-scope="scope">
<el-button type="primary" size="mini" @click="handlerUpdate(scope.row)">编辑</el-button>
<el-button size="mini" type="danger" @click="handlerDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<div class="pagination-container">
<el-pagination
class="pagiantion"
@current-change="handleCurrentChange"
:current-page="pagination.currentPage"
:page-size="pagination.pageSize"
layout="total, prev, pager, next, jumper"
:total="pagination.total">
</el-pagination>
</div>
<!-- 编辑标签弹层 -->
<div class="add-form">
<el-dialog title="编辑图书" :visible.sync="dialogFormVisible4Edit">
<el-form ref="dataEditForm" :model="formData" :rules="rules" label-position="right" label-width="100px">
<el-row>
<el-col :span="12">
<el-form-item label="图书类别" prop="type">
<el-input v-model="formData.type"/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="图书名称" prop="name">
<el-input v-model="formData.name"/>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-form-item label="描述">
<el-input v-model="formData.description" type="textarea"></el-input>
</el-form-item>
</el-col>
</el-row>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogFormVisible4Edit = false">取消</el-button>
<el-button type="primary" @click="handlerEdit()">确定</el-button>
</div>
</el-dialog>
</div>
<!-- 新增标签弹层 -->
<div class="add-form">
<el-dialog title="新增图书" :visible.sync="dialogFormVisible">
<el-form ref="dataAddForm" :model="formData" :rules="rules" label-position="right" label-width="100px">
<el-row>
<el-col :span="12">
<el-form-item label="图书类别" prop="type">
<el-input v-model="formData.type"/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="图书名称" prop="name">
<el-input v-model="formData.name"/>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-form-item label="描述">
<el-input v-model="formData.description" type="textarea"></el-input>
</el-form-item>
</el-col>
</el-row>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogFormVisible = false">取消</el-button>
<el-button type="primary" @click="saveBook()">确定</el-button>
</div>
</el-dialog>
</div>
</div>
</div>
</div>
</body>
<!-- 引入组件库 -->
<script src="../js/vue.js"></script>
<script src="../plugins/elementui/index.js"></script>
<script type="text/javascript" src="../js/jquery.min.js"></script>
<script src="../js/axios-0.18.0.js"></script>
<script>
var vue = new Vue({
el: '#app',
data:{
dataList: [],//当前页要展示的分页列表数据
formData: {},//表单数据
dialogFormVisible: false,//增加表单是否可见
dialogFormVisible4Edit:false,//编辑表单是否可见
pagination: {},//分页模型数据,暂时弃用
},
//钩子函数,VUE对象初始化完成后自动执行
created() {
this.getAll();
},
methods: {
// 重置表单
resetForm() {
//清空输入框
this.formData = {};
},
// 弹出添加窗口
openSave() {
this.dialogFormVisible = true;
this.resetForm();
},
//添加
saveBook () {
axios.post("/books",this.formData).then((res)=>{
if (res.data.status == 20011) {
//成功
this.$message.success(res.data.msg);
//关闭窗口
this.dialogFormVisible = false;
//重新查询全部数据
this.getAll();
}else {
//失败
this.$message.error(res.data.msg);
}
});
},
//主页列表查询
getAll() {
axios.get("/books").then((res)=>{
this.dataList = res.data.obj;
});
},
handlerDelete(row) {
axios.delete("/books/"+row.id).then((res) => {
var result = res.data;
if(result.status == 20021) {
this.$message.success(result.msg);
//重新查询全部数据
this.getAll();
}else if(result.status == 20020) {
//失败
this.$message.error(result.msg);
}else {
//异常
this.$message.error(result.msg);
}
});
},
handlerUpdate(row) {
//显示编辑的弹出层
this.dialogFormVisible4Edit = true;
//数据回显
//this.formData = row;
axios.get("/books/" + row.id).then(res => {
this.formData = res.data.obj;
})
},
handlerEdit() {
axios.put("/books",this.formData).then(res => {
var result = res.data;
if (result.status == 20031) {
//关闭窗口
this.dialogFormVisible4Edit = false;
//成功提示信息
this.$message.success(result.msg);
//重新查询全部
this.getAll();
}else {
//失败提示
this.$message.error(result.msg);
}
})
}
}
})
</script>
</html>
15.拦截器
15.1 拦截器简介
拦截器概念与作用
-
拦截器(Interceptor)是一种动态拦截方法调用的机制,在SpringMVC中动态拦截控制器方法的执行。
-
作用:
- 在指定的方法调用前后执行预先设定的代码
- 阻止原方法的执行
- 总结一下:增强
-
核心原理:AOP思想
拦截器与过滤器的区别
- 归属不同:Filter属于Servlet技术,Interceptor属于SpringMVC技术
- 拦截内容不同:Filter对所有访问(静态资源与动态资源的访问)进行拦截,Interceptor仅针对SpringMVC(只会拦截Controller的请求,对静态资源是不拦截的)的访问进行增强。
15.2 入门案例
自定义拦截器
package com.darksnow.interceptor;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component //注意这个类所在的包要被扫描到,否则该类对象无法被容器管理
public class DemoInterceptor implements HandlerInterceptor {
/**
* 执行目标方法之前执行的方法,方法的返回值代表是否放行目标方法
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle执行了[前置通知]");
return true;
}
/**
* 目标方法执行完毕之后,就会执行该方法
* 该方法执行的前提
* 1.preHandle方法必须要放行
* 2.目标方法不要出现异常
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle......");
}
/**
最终执行方法,方法执行前提:
1.preHandle方法先放行
2.目标方法出现异常,这个方法是可以执行的
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("最终通知......afterCompletion");
}
}
配置拦截器的拦截路径
方法1:
package com.darksnow.config;
import com.darksnow.interceptor.DemoInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
/**
该类存在的作用:对静态资源进行放行,放行给tomcat去处理
*/
@Configuration
public class SpringMvcSupport extends WebMvcConfigurationSupport {
@Autowired
private DemoInterceptor demoInterceptor;
//设置静态资源访问过滤的,当前类需要设置为配置类,并被扫描加载
//要注意这个类需要被@ComponentScan扫描到
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
//当访问/pages/xxx的时候,会从/pages目录下去查找资源
registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");
registry.addResourceHandler("/js/**") .addResourceLocations("/js/");
registry.addResourceHandler("/css/**").addResourceLocations("/css/");
registry.addResourceHandler("/plugins/**").addResourceLocations("/plugins/");
}
/**
* 给拦截器定义拦截路径
*/
@Override
protected void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(demoInterceptor).addPathPatterns("/books","/books/","/books/*");
}
}
方式2:
注意:与方式1两者只能选择一种,不然会有冲突,如果方式1起作用会导致方式2的拦截器不起作用。
package com.darksnow.config;
import com.darksnow.interceptor.DemoInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
@ComponentScan(basePackages = {"com.darksnow.controller","com.darksnow.config","com.darksnow.interceptor"})
@EnableWebMvc
public class SpringMvcConfig implements WebMvcConfigurer {
@Autowired
private DemoInterceptor demoInterceptor;
/**
* 静态资源放行
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
//当访问/pages/xxx的时候,会从/pages目录下去查找资源
registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");
registry.addResourceHandler("/js/**") .addResourceLocations("/js/");
registry.addResourceHandler("/css/**").addResourceLocations("/css/");
registry.addResourceHandler("/plugins/**").addResourceLocations("/plugins/");
}
/**
* 给拦截器定义拦截路径
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
System.out.println("拦截器:" + demoInterceptor);
registry.addInterceptor(demoInterceptor).addPathPatterns("/books","/books/","/books/*");
}
}
15.3 拦截器流程分析
15.4 拦截器参数
问题:postHandle()和afterCompletion()方法都是处理器方法执行之后执行,有什么区别?
package com.darksnow.interceptor;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
public class DemoInterceptor implements HandlerInterceptor {
/**
* 执行目标方法之前执行的方法,方法的返回值代表是否放行目标方法,true为放行,false为被拦截的处理器将不执行
* @param request 请求对象
* @param response 响应对象
* @param handler 被调用的处理器对象,本质上是一个方法对象,对反射技术中的Method对象进行了包装
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle执行了[前置通知]");
return false;
}
/**
* 目标方法执行完毕之后,就会执行该方法
* 该方法执行的前提
* 1.preHandle方法必须要放行
* 2.目标方法不要出现异常
* @param request 请求对象
* @param response 响应对象
* @param handler 被调用的处理器对象,本质上是一个方法对象,对反射技术中的Method对象进行了包装
* @param modelAndView 如果处理器执行完成具有返回结果,可以读取到对应数据与页面信息,并进行跳转
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle......");
}
/**
* 最终执行方法,方法执行前提:
* 1.preHandle方法先放行
* 2.目标方法出现异常,这个方法是可以执行的
* Exception ex参数:如果处理器执行过程中出现异常对象,可以针对异常情况进行单独处理
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("最终通知......afterCompletion");
}
}
15.5 拦截器链配置
第一个拦截器
package com.darksnow.interceptor;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
@Component
public class Demo1Interceptor implements HandlerInterceptor {
/**
* 执行目标方法之前执行的方法,方法的返回值代表是否放行目标方法,true为放行,false为被拦截的处理器将不执行
* @param request 请求对象
* @param response 响应对象
* @param handler 被调用的处理器对象,本质上是一个方法对象,对反射技术中的Method对象进行了包装
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HandlerMethod handlerMethod = (HandlerMethod) handler;
//得到当前目标方法的方法名
Method method = handlerMethod.getMethod();
System.out.println("拦截器1 preHandle执行了[前置通知] ------ " + method.getName());
return true;
}
/**
* 目标方法执行完毕之后,就会执行该方法
* 该方法执行的前提
* 1.preHandle方法必须要放行
* 2.目标方法不要出现异常
* @param request 请求对象
* @param response 响应对象
* @param handler 被调用的处理器对象,本质上是一个方法对象,对反射技术中的Method对象进行了包装
* @param modelAndView 如果处理器执行完成具有返回结果,可以读取到对应数据与页面信息,并进行跳转
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("拦截器1 postHandle......");
}
/**
* 最终执行方法,方法执行前提:
* 1.preHandle方法先放行
* 2.目标方法出现异常,这个方法是可以执行的
* Exception ex参数:如果处理器执行过程中出现异常对象,可以针对异常情况进行单独处理
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("拦截器1 ...... afterCompletion");
}
}
第二个拦截器
package com.darksnow.interceptor;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
@Component
public class Demo2Interceptor implements HandlerInterceptor {
/**
* 执行目标方法之前执行的方法,方法的返回值代表是否放行目标方法,true为放行,false为被拦截的处理器将不执行
* @param request 请求对象
* @param response 响应对象
* @param handler 被调用的处理器对象,本质上是一个方法对象,对反射技术中的Method对象进行了包装
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HandlerMethod handlerMethod = (HandlerMethod) handler;
//得到当前目标方法的方法名
Method method = handlerMethod.getMethod();
System.out.println("拦截器2 preHandle执行了[前置通知] ------ " + method.getName());
return true;
}
/**
* 目标方法执行完毕之后,就会执行该方法
* 该方法执行的前提
* 1.preHandle方法必须要放行
* 2.目标方法不要出现异常
* @param request 请求对象
* @param response 响应对象
* @param handler 被调用的处理器对象,本质上是一个方法对象,对反射技术中的Method对象进行了包装
* @param modelAndView 如果处理器执行完成具有返回结果,可以读取到对应数据与页面信息,并进行跳转
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("拦截器2 postHandle......");
}
/**
* 最终执行方法,方法执行前提:
* 1.preHandle方法先放行
* 2.目标方法出现异常,这个方法是可以执行的
* Exception ex参数:如果处理器执行过程中出现异常对象,可以针对异常情况进行单独处理
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("拦截器2 ...... afterCompletion");
}
}
配置两个拦截器
package com.darksnow.config;
import com.darksnow.interceptor.Demo1Interceptor;
import com.darksnow.interceptor.Demo2Interceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
@ComponentScan(basePackages = {"com.darksnow.controller","com.darksnow.config","com.darksnow.interceptor"})
@EnableWebMvc
public class SpringMvcConfig implements WebMvcConfigurer {
@Autowired
private Demo1Interceptor demo1Interceptor;
@Autowired
private Demo2Interceptor demo2Interceptor;
/**
* 静态资源放行
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
//当访问/pages/xxx的时候,会从/pages目录下去查找资源
registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");
registry.addResourceHandler("/js/**") .addResourceLocations("/js/");
registry.addResourceHandler("/css/**").addResourceLocations("/css/");
registry.addResourceHandler("/plugins/**").addResourceLocations("/plugins/");
}
/**
* 给拦截器定义拦截路径
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(demo1Interceptor).addPathPatterns("/books","/books/","/books/*");
registry.addInterceptor(demo2Interceptor).addPathPatterns("/books","/books/","/books/*");
}
}
多个拦截器工作流程分析
- 当配置多个拦截器时,形成拦截器链
- 拦截器链的运行顺序参照拦截器添加顺序为准
- 当拦截器中出现对原始拦截器的拦截,后面的拦截器均终止运行
- 当拦截器运行中断,仅运行配置在前面的拦截器的afterCompletion操作
16.SSM框架整合(xml配置)
16.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">
<modelVersion>4.0.0</modelVersion>
<groupId>com.darksnow</groupId>
<artifactId>SSM-Demo</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<!-- SpringMVC的依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.0</version>
</dependency>
<!-- spring整合mybatis依赖 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.18</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.8</version>
</dependency>
<!-- druid连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.16</version>
</dependency>
</dependencies>
</project>
16.2 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">
<!-- 配置一个全局的参数:指定spring的配置文件 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!-- 编码过滤器 -->
<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>
<!-- 配置监听器:创建Spring容器对象 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- 前端控制器 -->
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 指定springmvc的配置文件 -->
<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>
16.3 jdbc.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///darksnow?CharacterEncoding=utf-8
jdbc.username=root
jdbc.password=root
16.4 applicationContext.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"
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">
<!-- 扫描service层包 -->
<context:component-scan base-package="com.darksnow.service"/>
<!-- 引入数据外部properties文件 -->
<context:property-placeholder location="classpath:jdbc.properties" />
<!-- 创建数据源对象 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!-- 创建SqlSessionFactory对象 这个bean可以代替MyBatis配置文件的一些配置 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 注入数据源对象 -->
<property name="dataSource" ref="dataSource"/>
<!-- 可以引入mybatis的配置文件 -->
<!-- <property name="configLocation" value="classpath:mybatis-config.xml"/>-->
<!-- 别名映射 -->
<property name="typeAliasesPackage" value="com.darksnow.entity"/>
</bean>
<!-- 扫描dao层接口的包,创建动态代理对象 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 需要指定dao层接口的包名 -->
<property name="basePackage" value="com.darksnow.dao"/>
</bean>
</beans>
16.5 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">
<!-- 扫描controller层包 -->
<context:component-scan base-package="com.darksnow.controller"/>
<!-- 视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/pages/" />
<property name="suffix" value=".jsp" />
</bean>
<!-- 注解驱动 -->
<mvc:annotation-driven/>
<!-- 静态资源放行 -->
<mvc:default-servlet-handler/>
</beans>
16.6 实体类
package com.darksnow.entity;
public class Book {
private Integer id;
private String type;
private String name;
private String description;
//省略setter,getter,toString方法
}
package com.darksnow.entity;
/**
* 状态类,里面全是常量
* 当然你可以考虑用枚举的形式来完成
*/
public class Code {
public static final Integer SAVE_OK = 20011;
public static final Integer DELETE_OK = 20021;
public static final Integer UPDATE_OK = 20031;
public static final Integer GET_OK = 20041;
public static final Integer SAVE_ERR = 20010;
public static final Integer DELETE_ERR = 20020;
public static final Integer UPDATE_ERR = 20030;
public static final Integer GET_ERR = 20040;
//异常的状态码(新增)
//接口中的变量默认被public static final修饰,所以它们都是全局常量
public static final Integer SYSTEM_ERR = 50001;
public static final Integer SYSTEM_TIMEOUT_ERR = 50002;
public static final Integer SYSTEM_UNKOWN_ERR = 59999;
public static final Integer BUSINESS_ERR = 60002;
}
package com.darksnow.entity;
public class Result {
private int status;
private Object obj;
private String msg;
public Result(int status, Object obj, String msg) {
this.status = status;
this.obj = obj;
this.msg = msg;
}
public Result(int status, String msg) {
this.status = status;
this.msg = msg;
}
//省略setter,getter,toString方法
}
16.7 controller层
package com.darksnow.controller;
import com.darksnow.entity.Book;
import com.darksnow.entity.Code;
import com.darksnow.exceptions.BusinessException;
import com.darksnow.service.BookService;
import com.darksnow.entity.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 下面的方法统一返回值结果是为什么?
* 前后端分离开发的情况下,前端人员不清楚后端人员controller的返回值是什么,会给前端人员造成困扰
* 所以我们就统一返回结果。让前后端开发人员具有默契。
*/
@RestController
@RequestMapping("/books")
public class BookController {
@Autowired
private BookService bookService;
@PostMapping
public Result save(@RequestBody Book book) {
return bookService.save(book) ? new Result(Code.SAVE_OK,"添加成功") : new Result(Code.SAVE_ERR,"添加失败");
}
@DeleteMapping("/{id}")
public Result delete(@PathVariable("id") Integer id) {
if (id < 0) {
throw new BusinessException(Code.BUSINESS_ERR,"数据有误!!!");
}
return bookService.delete(id) ? new Result(Code.DELETE_OK,"删除成功") : new Result(Code.DELETE_ERR,"删除失败");
}
@PutMapping
public Result update(@RequestBody Book book) {
return bookService.update(book) ? new Result(Code.UPDATE_OK,"修改成功") : new Result(Code.UPDATE_ERR,"修改失败");
}
@GetMapping("/{id}")
public Result findById(@PathVariable("id") Integer id) {
Book booke = bookService.findById(id);
if (booke != null ) {
return new Result(Code.GET_OK,booke,"查询成功");
}else {
return new Result(Code.GET_ERR,booke,"查询失败");
}
}
@GetMapping
public Result findAll() {
List<Book> bookList = bookService.findAll();
if (bookList != null && bookList.size() > 0) {
return new Result(Code.GET_OK,bookList,"查询成功");
}else {
return new Result(Code.GET_ERR,bookList,"查询失败");
}
}
}
package com.darksnow.controller;
import com.darksnow.entity.Book;
import com.darksnow.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import java.util.List;
@Controller
public class TestController {
@Autowired
private BookService bookService;
@RequestMapping("/findAllBooks")
public ModelAndView findAllBooks() {
//查询
List<Book> bookList = bookService.findAll();
//创建模型与视图对象
ModelAndView modelAndView = new ModelAndView();
//添加数据
modelAndView.addObject("bookList",bookList);
//指定页面
modelAndView.setViewName("show");
return modelAndView;
}
}
16.8 service层
package com.darksnow.service;
import com.darksnow.entity.Book;
import java.util.List;
public interface BookService {
boolean save(Book book);
boolean delete(Integer id);
boolean update(Book book);
List<Book> findAll();
Book findById(Integer id);
}
package com.darksnow.service.impl;
import com.darksnow.dao.BookDao;
import com.darksnow.entity.Book;
import com.darksnow.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class BookServiceImpl implements BookService {
@Autowired
private BookDao bookDao;
@Override
public boolean save(Book book) {
return bookDao.save(book) > 0;
}
@Override
public boolean delete(Integer id) {
return bookDao.delete(id) > 0;
}
@Override
public boolean update(Book book) {
return bookDao.update(book) > 0;
}
@Override
public List<Book> findAll() {
return bookDao.findAll();
}
@Override
public Book findById(Integer id) {
return bookDao.findById(id);
}
}
16.9 dao层
package com.darksnow.dao;
import com.darksnow.entity.Book;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import java.util.List;
public interface BookDao {
@Insert("insert into tb_book values(null,#{type},#{name},#{description})")
int save(Book book);
@Delete("delete from tb_book where id = #{id}")
int delete(Integer id);
@Update("update tb_book set type=#{type},name=#{name},description=#{description} where id = #{id}")
int update(Book book);
@Select("select * from tb_book")
List<Book> findAll();
@Select("select * from tb_book where id = #{id}")
Book findById(Integer id);
}
17.文件上传
17.1 引入依赖
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
17.2 upload.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%--
上传文件的表单前提:
1.提交方式必须post
2.表单的类型必须:multipart/form-data,多功能表单数据
3.必须有一个type=file的表单元素
--%>
<form action="${pageContext.request.contextPath}/upload" method="post" enctype="multipart/form-data">
<input type="text" name="username"><br/>
<input type="file" name="upload">
<input type="submit" value="上传">
</form>
</body>
</html>
17.3 UploadController
package com.darksnow.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.util.UUID;
@Controller
public class UploadController {
@RequestMapping("/upload")
public String uploadFile(String username, MultipartFile upload, HttpServletRequest request) {
System.out.println(username);
//1.目标路径
//获取项目运行的路径
String realPath = request.getSession().getServletContext().getRealPath("/upload");
System.out.println(realPath);
//判断该路径是否存在
File realFile = new File(realPath);
if (!realFile.exists()) {
realFile.mkdirs();
}
//2.获取唯一的文件名称(包含扩展名)
String uuidName = UUID.randomUUID().toString().replace("-", "");
System.out.println(uuidName);
//获取文件名
String originalFilename = upload.getOriginalFilename();
//截取字符串,获取文件的扩展名
String extendName = originalFilename.substring(originalFilename.lastIndexOf("."));
//得到最后的文件名
String filename = uuidName + extendName;
System.out.println(filename);
//3.文件上传
try {
upload.transferTo(new File(realFile,filename));
} catch (IOException e) {
e.printStackTrace();
}
return "show";
}
}
18.其他
18.1 重定向
package com.darksnow.controller;
import com.darksnow.entity.Book;
import com.darksnow.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.List;
@Controller
public class TestController {
@RequestMapping("/testRedirect")
public String testRedirect(HttpSession session, HttpServletRequest request, HttpServletResponse response) throws IOException {
//这几个对象都能打印出地址值,说明这些对象都是可以直接使用的,不需要你自己再去new了
System.out.println(session);
System.out.println(request);
System.out.println(response);
// response.sendRedirect("/pages/books.html");
return "redirect:/pages/books.html"; //重定向
}
}
18.2 转发
package com.darksnow.controller;
import com.darksnow.entity.Book;
import com.darksnow.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.List;
@Controller
public class TestController {
@RequestMapping("/testForward")
public String testForward(HttpServletRequest request, HttpServletResponse response) throws Exception {
// request.getRequestDispatcher("/pages/books.html").forward(request,response);
return "forward:/pages/books.html";
}
}
18.3 静态资源放行的方式
方式一(指定放行):
在SpringMVC的配置文件中写入:<mvc:resources mapping="/js/*" location="/js/"/>
方式二(全部放行):
在SpringMVC的配置文件中写入:<mvc:default-servlet-handler/>
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 字符编码:从基础到乱码解决
· 提示词工程——AI应用必不可少的技术