概述
SpringMVC是一种基于Java实现MVC模型的轻量级web框架
入门案例
创建一个Maven项目,在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.spark</groupId>
<artifactId>springmvc-start</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
编写表现层Controller
package com.spark.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* UserController class
* description: UserController
*
* @author Administrator
* @date 2023/7/26
*/
@Controller
public class UserController {
// 提供方法的请求路径
@RequestMapping("/save")
@ResponseBody
public String saveUser(){
System.out.println("user save ...");
return "{module:save}";
}
}
编写SpringMVC配置类加载Controller层注册的Bean
package com.spark.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
/**
* SpringmvcConfig class
* description: SpringMvcConfig
*
* @author Administrator
* @date 2023/7/26
*/
@Configuration
@ComponentScan("com.spark.controller")
public class SpringMvcConfig {
}
编写Servlet容器配置类加载SpringMVC配置类
package com.spark.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.support.AbstractDispatcherServletInitializer;
/**
* ServletInitConfig class
* description: ServletInitConfig
*
* @author Administrator
* @date 2023/7/26
*/
@Configuration
public class ServletInitConfig extends AbstractDispatcherServletInitializer {
@Override
protected WebApplicationContext createServletApplicationContext() {
AnnotationConfigWebApplicationContext atx = new AnnotationConfigWebApplicationContext();
atx.register(SpringMvcConfig.class);
return atx;
}
@Override
protected String[] getServletMappings() {
// 所有请求
return new String[] {"/"};
}
@Override
protected WebApplicationContext createRootApplicationContext() {
return null;
}
}
配置Tomcat
打开浏览器访问 URL + Controller定义的方法请求路径查看结果
http://localhost:8082/springmvc_start/save
入门案例的工作流程
① 服务器启动初始化流程
- 服务器启动,执行ServletInitConfig类初始化web容器
- 执行createServletApplicationContext方法,创建了WebApplicationContext对象
- 加载SpringMVC配置类
- 执行@ComponentScan加载对应bean
- 加载UserController,每个@RequestMapping的名称对应一个具体的方法
- 执行getServletMappings方法,定义所有请求都通过SpringMVC
② 发送save请求过程
- 发送请求
- Web容器发现所有请求都通过SpringMVC,将请求交由SpringMVC处理
- 解析请求路径/save
- 匹配到对应的方法save()
- 执行save()方法
- 检测到@ResponseBody直接将save()返回值作为响应体返回给请求方
Bean加载过程
一个完整的SpringMVC请求涉及到有表现层(Controller)、数据层(Dao)、业务层(Service)
Controller层由SpringMVC加载,业务层和数据层由Spring加载
Web容器启动后,Spring会加载到SpringMVC的bean,如何进行避免?
加载Spring控制的bean时排除掉SpringMVC控制的bean
方式一:Spring加载的bean设定扫描范围,并排除掉Controller包内的bean
package com.spark.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
/**
* SpringConfig class
* description: SpringConfig
*
* @author Administrator
* @date 2023/7/26
*/
@Configuration
// 方式一:排除掉Controller层,排除规则为注解方式
@ComponentScan(value = "com.spark",
excludeFilters = @ComponentScan.Filter(
type = FilterType.ANNOTATION
)
)
public class SpringConfig {
}
方式二:Spring加载的bean设定扫描范围为精确范围
package com.spark.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
/**
* SpringConfig class
* description: SpringConfig
*
* @author Administrator
* @date 2023/7/26
*/
@Configuration
// 方式二:设定精确的扫描范围
@ComponentScan({"com.spark.dao","com.spark.service"})
public class SpringConfig {
}
在Servlet容器配置类中注册Spring配置类
package com.spark.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.support.AbstractDispatcherServletInitializer;
/**
* ServletInitConfig class
* description: ServletInitConfig
*
* @author Administrator
* @date 2023/7/26
*/
@Configuration
public class ServletInitConfig extends AbstractDispatcherServletInitializer {
@Override
protected WebApplicationContext createServletApplicationContext() {
AnnotationConfigWebApplicationContext atx = new AnnotationConfigWebApplicationContext();
atx.register(SpringMvcConfig.class);
return atx;
}
@Override
protected String[] getServletMappings() {
// 所有请求
return new String[] {"/"};
}
@Override
protected WebApplicationContext createRootApplicationContext() {
// 注册Spring配置类
AnnotationConfigWebApplicationContext atx = new AnnotationConfigWebApplicationContext();
atx.register(SpringConfig.class);
return atx;
}
}
拓展:简化开发Servlet容器配置类
package com.spark.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
/**
* ServletInitConfig class
* description: ServletInitConfig
*
* @author Administrator
* @date 2023/7/26
*/
@Configuration
public class ServletInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer{
@Override
protected Class<?>[] getRootConfigClasses() {
// 注册Spring配置类
return new Class[]{SpringConfig.class};
}
@Override
protected Class<?>[] getServletConfigClasses() {
// 注册SpringMVC配置类
return new Class[]{SpringMvcConfig.class};
}
@Override
protected String[] getServletMappings() {
// 所有请求
return new String[]{"/"};
}
}
Postman
Postman是一款功能强大的网页调试与发送网页Http请求的Chrome插件,常用于进行接口测试
下载地址:https://www.postman.com/downloads/
安装完成后,打开让选择注册用户,可以不进行注册,关闭重新打开就可以进入
启动入门案例IDEA中配置的Tomcat,输入Controller层方法配置的接口地址,查看响应结果
请求与响应
设置请求路径
团队多人开发,每个人设置不同的请求路径,有可能会出现冲突,当多人的请求路径相同时,项目启动会出现错误。
通常会在请求路径前设置模块名作为请求路径前缀来解决问题
设置请求路径使用@RequestMapping注解,该注解可以用在类或方法上,前缀一般设置在Controller类上
package com.spark.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* UserController class
* description: UserController
*
* @author Administrator
* @date 2023/7/26
*/
@Controller
// 设置前缀,一般使用模块名
@RequestMapping("/user")
public class UserController {
// 提供方法的请求路径
@RequestMapping("/save")
@ResponseBody
public String saveUser(){
System.out.println("user save ...");
return "{module:save}";
}
}
请求方式GET和POST
创建一个新的Maven项目,在Controller层添加一个方法用于展示请求传递的参数
package com.spark.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* UserController class
* description: UserController
*
* @author Administrator
* @date 2023/7/26
*/
@Controller
// 设置前缀,一般使用模块名
@RequestMapping("/user")
public class UserController {
// 接收请求参数
@RequestMapping("/commonParams")
@ResponseBody
public String commonParams(String name,int age){
System.out.println("普通参数name===>"+name);
System.out.println("普通参数age===>"+age);
return "{module:commonParams}";
}
}
① GET请求传参
普通参数:URL地址传参,地址参数名与形参变量名相同,定义形参即可接收参数
② POST请求传参
普通参数:使用form表单进行传参,表单参数名与形参变量名相同,定义形参即可接收参数
传递中文会出现乱码问题,需要在Servlet容器配置类中添加过滤器配置,处理中文乱码
package com.spark.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
import javax.servlet.Filter;
/**
* ServletInitConfig class
* description: ServletInitConfig
*
* @author Administrator
* @date 2023/7/26
*/
@Configuration
public class ServletInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer{
@Override
protected Class<?>[] getRootConfigClasses() {
// 注册Spring配置类
return new Class[]{SpringConfig.class};
}
@Override
protected Class<?>[] getServletConfigClasses() {
// 注册SpringMVC配置类
return new Class[]{SpringMvcConfig.class};
}
@Override
protected String[] getServletMappings() {
// 所有请求
return new String[]{"/"};
}
// 添加过滤器配置
@Override
protected Filter[] getServletFilters() {
CharacterEncodingFilter filter = new CharacterEncodingFilter();
// 设置字符编码
filter.setEncoding("UTF-8");
return new Filter[]{filter};
}
}
五种请求参数
① 普通参数传递
普通参数传递要求形参与传参的参数名一致,如果不一致,需要使用@RequestParam注解指定传参的参数名
举例:形参列表的参数名为userName,传参的参数名为name
// 接收请求参数
@RequestMapping("/commonParams")
@ResponseBody
// 将传参的name属性值赋值给形参的userName
public String commonParams(@RequestParam("name") String userName){
System.out.println("普通参数userName===>"+userName);
return "{module:commonParams}";
}
② POJO类型参数传递
POJO参数传递要求请求参数名与形参对象属性名相同,定义POJO类型参数即可接收参数,将请求参数封装为实体类
package com.spark.entity;
/**
* UserEntity class
* description: UserEntity
*
* @author Administrator
* @date 2023/7/30
*/
public class UserEntity {
private String userName;
private int age;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "UserEntity{" +
"userName='" + userName + '\'' +
", age=" + age +
'}';
}
}
Controller层定义方法
// 接收POJO参数
@RequestMapping("/pojoParams")
@ResponseBody
public String pojoParams(UserEntity user){
System.out.println("user:::"+user);
return "{module:pojoParams}";
}
③ 嵌套POJO类型参数传递
当实体类中包含其他实体类情况下,请求参数名与形参对象属性名相同,按照对象层次结构关系即可接收嵌套POJO属性参数
添加Address实体类
package com.spark.entity;
/**
* AddressEntity class
* description: AddressEntity
*
* @author Administrator
* @date 2023/7/30
*/
public class AddressEntity {
private String city;
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
@Override
public String toString() {
return "AddressEntity{" +
"city='" + city + '\'' +
'}';
}
}
UserEntity实体类中添加AddressEntity实体类
package com.spark.entity;
/**
* UserEntity class
* description: UserEntity
*
* @author Administrator
* @date 2023/7/30
*/
public class UserEntity {
private String userName;
private int age;
private AddressEntity address;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public AddressEntity getAddress() {
return address;
}
public void setAddress(AddressEntity address) {
this.address = address;
}
@Override
public String toString() {
return "UserEntity{" +
"userName='" + userName + '\'' +
", age=" + age +
", address=" + address +
'}';
}
}
Controller层定义方法
// 接收嵌套POJO参数
@RequestMapping("/pojoNestParams")
@ResponseBody
public String pojoNestParams(UserEntity user){
System.out.println("user:::"+user);
return "{module:pojoParams}";
}
④ 数组类型参数传递
请求参数名与形参对象属性名相同且请求参数为多个,定义数组类型形参即可接收参数
Controller层定义方法
// 接收数组类型参数
@RequestMapping("/arrayParams")
@ResponseBody
public String arrayParams(String [] likes){
System.out.println(Arrays.toString(likes));
return "{module:arrayParams}";
}
⑤ 集合参数传递
请求参数名与形参集合对象名相同且请求参数为多个,@RequestParam绑定参数关系
Controller层定义方法
// 接收集合类型参数
@RequestMapping("/listParams")
@ResponseBody
public String listParams(@RequestParam List<String>likes){
System.out.println(likes);
return "{module:listParams}";
}
集合类型参数加@RequestParam原因是因为系统会默认将List看做为引用数据类型,会去调用其构造方法去初始化,List类型为接口没有构造方法
json数据传递
在项目中数据传递一般是使用的是json对象和json数组。
① 实现json对象传递
pom.xml中引入依赖
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.0</version>
</dependency>
在SpringMVC配置类中开启json转换为实体
package com.spark.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
/**
* SpringmvcConfig class
* description: SpringMvcConfig
*
* @author Administrator
* @date 2023/7/26
*/
@Configuration
@ComponentScan("com.spark.controller")
@EnableWebMvc
public class SpringMvcConfig {
}
Controller层定义方法
// 接收json类型参数
@RequestMapping("/jsonParams")
@ResponseBody
public String jsonParams(@RequestBody UserEntity user){
System.out.println(user);
return "{module:jsonParams}";
}
@RequestBody将请求中请求体包含的数据传递给请求参数,此注解一个处理器方法只能用一次
Postman发送请求
② json数组参数传递
Controller层定义方法
// 接收json类型参数
@RequestMapping("/jsonArrayParams")
@ResponseBody
public String jsonArrayParams(@RequestBody List<UserEntity> users){
System.out.println(users);
return "{module:jsonArrayParams}";
}
Postman发送请求
@RequestParam和@RequestBody
区别
- @RequestParam用于接收url地址传参,表单传参
- @RequestBody用于接收json数据
应用
- 后期开发中,发送json格式数据为主,@RequestBody应用较广
- 如果发送非json数据,选用@RequestParam接收请求参数
日期型参数传递
日期类型基于系统不同格式也不尽相同
接收形参时,根据不同的日期格式设置不同的接收方式
// 接收日期类型参数
@RequestMapping("/dateParams")
@ResponseBody
public String dateParams(Date date,
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date date2){
System.out.println("Date:"+date);
System.out.println("Date2:"+date2);
return "{module:dateParams}";
}
@DateTimeFormat设置日期类型数据格式
Rest风格
概述
REST表现形式状态转换
传统风格资源描述形式
- http://localhost/user/getById?id=1
- http://localhost/user/saveUser
REST风格描述形式
- http://localhost/user/1
- http://localhost/user
优点:隐藏资源的访问行为,无法通过地址得知对资源是何种操作,书写简化
按照REST风格访问资源时使用行为动作区分对资源进行了何种操作,常用的有四种
- GET(查询)
- POST(新增/保存)
- PUT(修改/更新)
- DELETE(删除)
根据REST风格对资源进行访问称为RestFul
入门案例
使用REST风格约定请求动作,编写表现层Controller
package com.spark.controller;
import com.spark.entity.UserEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* RestController class
* description: RestController
*
* @author Administrator
* @date 2023/7/31
*/
@Controller
@RequestMapping("/users")
public class RestController {
// 约定save方法的请求动作为POST
@RequestMapping(value = "/save",method = RequestMethod.POST)
@ResponseBody
public String save(@RequestBody UserEntity user){
System.out.println(user);
return "{module:save}";
}
// 约定delete方法的请求动作为DELETE
@RequestMapping(value = "/delete",method = RequestMethod.DELETE)
@ResponseBody
public String delete(Integer id){
System.out.println("user id===>"+id);
return "{module:delete}";
}
}
当规定了行为动作后,使用其他方式访问接口会报请求方式不支持异常
地址传参,使用DELETE方式访问却报404
使用路径传参时,方法的形参列表需要使用@PathVariable表明值从url获取
改写delete方法
// 约定delete方法的请求动作为DELETE
// url指定参数名
@RequestMapping(value = "/delete/{id}",method = RequestMethod.DELETE)
@ResponseBody
// 使用@PathVariable注解表明参数从url获取
public String delete(@PathVariable Integer id){
System.out.println("user id===>"+id);
return "{module:delete}";
}
简化开发
@RestController注解是@Controller和@ResponseBody的复合注解,以后在表现层只需要使用@RestController注解就可以管理该类并且方法结果都可以原样返回
@RequestMapping中的method属性指明行为动作,也可以使用具体的注解进行简化开发
- @GetMapping
- @PostMapping
- @PutMapping
- @DeleteMapping
package com.spark.controller;
import org.springframework.web.bind.annotation.*;
/**
* RestController class
* description: RestController
*
* @author Administrator
* @date 2023/7/31
*/
@RestController
@RequestMapping("/books")
public class RestfulController {
@GetMapping("/allBooks")
public String getAllBooks() {
return "{module:getAllBooks}";
}
@PostMapping("/saveBooks")
public String saveBooks(){
return "{module:saveBooks}";
}
@PutMapping("/updateBook")
public String updateBook(){
return "{module:updateBook}";
}
@DeleteMapping("/deleteBook")
public String deleteBook(){
return "{module:deleteBook}";
}
}
SSM整合
整合配置
使用web模版创建一个Maven项目,在pom.xml中引入依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.6</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.8</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.49</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.13.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.13.1</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
resources目录下新建jdbc.properties文件,写入MySQL连接信息
jdbc.driver = com.mysql.jdbc.Driver
jdbc.url = jdbc:mysql://localhost:3306/ssm_db
jdbc.username = root
jdbc.password = root
编写JDBC配置类
package com.spark.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import javax.sql.DataSource;
/**
* JdbcConfig class
* description: JdbcConfig
*
* @author Administrator
* @date 2023/8/3
*/
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 getDataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(driver);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
}
编写Mybatis配置类
package com.spark.config;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.context.annotation.Bean;
import javax.sql.DataSource;
/**
* MybatisConfig class
* description: MybatisConfig
*
* @author Administrator
* @date 2023/8/3
*/
public class MybatisConfig {
// 获取SqlSessionFactory
@Bean
public SqlSessionFactoryBean getSqlSessionFactoryBean(DataSource dataSource) {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
// 扫描entity包
sqlSessionFactoryBean.setTypeAliasesPackage("com.spark.entity");
return sqlSessionFactoryBean;
}
// 映射dao
@Bean
public MapperScannerConfigurer mapperScannerConfigurer(){
MapperScannerConfigurer configurer = new MapperScannerConfigurer();
configurer.setBasePackage("com.spark.dao");
return configurer;
}
}
编写Spring和SpringMVC配置类
package com.spark.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;
/**
* SpringConfig class
* description: SpringConfig
*
* @author Administrator
* @date 2023/8/3
*/
@Configuration
@PropertySource({"classpath:jdbc.properties"})
@Import({JdbcConfig.class,MybatisConfig.class})
@ComponentScan({"com.spark.service","com.spark.dao"})
public class SpringConfig {
}
package com.spark.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
/**
* SpringMvcConfig class
* description: SpringMvcConfig
*
* @author Administrator
* @date 2023/8/3
*/
@Configuration
@ComponentScan("com.spark.controller")
@EnableWebMvc
public class SpringMvcConfig {
}
编写Servlet容器配置
package com.spark.config;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
/**
* ServletConfig class
* description: ServletConfig
*
* @author Administrator
* @date 2023/8/3
*/
public class ServletConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[]{SpringConfig.class};
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{SpringMvcConfig.class};
}
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
}
功能模块开发
数据库创建数据表,并写入数据
DROP TABLE IF EXISTS `books`;
CREATE TABLE `books` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`type` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`name` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`description` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 13 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
INSERT INTO `books` VALUES (1, '计算机理论', 'Spring实战 第5版', 'Spring入门经典教程,深入理解Spring原理技术内幕');
INSERT INTO `books` VALUES (2, '计算机理论', 'Spring 5核心原理与30个类手写实战', '十年沉淀之作,手写Spring精华思想');
INSERT INTO `books` VALUES (3, '计算机理论', 'Spring 5 设计模式', '深入Spring源码剖析Spring源码中蕴含的10大设计模式');
INSERT INTO `books` VALUES (4, '计算机理论', 'Spring MVC+MyBatis开发从入门到项目实战', '全方位解析面向Web应用的轻量级框架,带你成为Spring MVC开发高手');
INSERT INTO `books` VALUES (5, '计算机理论', '轻量级Java Web企业应用实战', '源码级剖析Spring框架,适合已掌握Java基础的读者');
INSERT INTO `books` VALUES (6, '计算机理论', 'Java核心技术 卷I 基础知识(原书第11版)', 'Core Java 第11版,Jolt大奖获奖作品,针对Java SE9、10、11全面更新');
INSERT INTO `books` VALUES (7, '计算机理论', '深入理解Java虚拟机', '5个维度全面剖析JVM,大厂面试知识点全覆盖');
INSERT INTO `books` VALUES (8, '计算机理论', 'Java编程思想(第4版)', 'Java学习必读经典,殿堂级著作!赢得了全球程序员的广泛赞誉');
INSERT INTO `books` VALUES (9, '计算机理论', '零基础学Java(全彩版)', '零基础自学编程的入门图书,由浅入深,详解Java语言的编程思想和核心技术');
INSERT INTO `books` VALUES (10, '市场营销', '直播就该这么做:主播高效沟通实战指南', '李子柒、李佳琦、薇娅成长为网红的秘密都在书中');
INSERT INTO `books` VALUES (11, '市场营销', '直播销讲实战一本通', '和秋叶一起学系列网络营销书籍');
INSERT INTO `books` VALUES (12, '市场营销', '直播带货:淘宝、天猫直播从新手到高手', '一本教你如何玩转直播的书,10堂课轻松实现带货月入3W+');
创建实体类,并编写Dao
package com.spark.entity;
/**
* Book class
* description: Book
*
* @author Administrator
* @date 2023/8/3
*/
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 + '\'' +
'}';
}
}
package com.spark.dao;
import com.spark.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;
/**
* BookDao class
* description: BookDao
*
* @author Administrator
* @date 2023/8/3
*/
public interface BookDao {
@Insert("insert into books(`type`,`name`,`description`) values(#{type},#{name},#{description})")
int save(Book book);
@Update("update books set type=#{type}, name=#{name}, description=#{description} where id = #{id}")
int update(Book book);
@Delete("delete from books where id = #{id}")
int delete(Integer id);
@Select("select * from books where id = #{id}")
Book getById(Integer id);
@Select("select * from books")
List<Book> getAll();
}
定义业务层接口方法并编写实现类
package com.spark.service;
import com.spark.entity.Book;
import java.util.List;
/**
* BookService class
* description: BookService
*
* @author Administrator
* @date 2023/8/3
*/
public interface BookService {
boolean addBook(Book book);
boolean updateBook(Book book);
boolean deleteBook(Integer id);
Book selectById(Integer id);
List<Book> selectAll();
}
package com.spark.service.impl;
import com.spark.dao.BookDao;
import com.spark.entity.Book;
import com.spark.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* BookServiceImpl class
* description: BookServiceImpl
*
* @author Administrator
* @date 2023/8/3
*/
@Service
public class BookServiceImpl implements BookService {
@Autowired
BookDao bookDao;
@Override
public boolean addBook(Book book) {
return bookDao.save(book) > 0;
}
@Override
public boolean updateBook(Book book) {
return bookDao.update(book) > 0;
}
@Override
public boolean deleteBook(Integer id) {
return bookDao.delete(id) > 0;
}
@Override
public Book selectById(Integer id) {
return bookDao.getById(id);
}
@Override
public List<Book> selectAll() {
return bookDao.getAll();
}
}
编写表现层Controller
package com.spark.controller;
import com.spark.entity.Book;
import com.spark.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* BookController class
* description: BookController
*
* @author Administrator
* @date 2023/8/3
*/
@RestController
@RequestMapping("/books")
public class BookController {
@Autowired
private BookService bookService;
@PostMapping("/addBook")
public String addBook(@RequestBody Book book){
if(bookService.addBook(book)){
return "添加成功";
}else{
return "添加失败";
}
}
@PutMapping("/updateBook")
public String updateBook(@RequestBody Book book){
if(bookService.updateBook(book)){
return "更新成功";
}else{
return "更新失败";
}
}
@DeleteMapping("/deleteBook")
public String deleteBook(Integer id){
if(bookService.deleteBook(id)){
return "删除成功";
}else{
return "删除失败";
}
}
@GetMapping("/getById")
public Book selectById(Integer id){
return bookService.selectById(id);
}
@GetMapping("/getAll")
public List<Book> selectAll(){
return bookService.selectAll();
}
}
接口测试
编写测试类
package com.spark;
import com.spark.config.SpringConfig;
import com.spark.entity.Book;
import com.spark.service.BookService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* BookServiceTest class
* description: BookServiceTest
*
* @author Administrator
* @date 2023/8/5
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class BookServiceTest {
@Autowired
BookService bookService;
@Test
public void testBookInsert(){
Book book = new Book();
book.setName("Java从入门到放弃");
book.setType("计算机");
book.setDescription("Java从入门到放弃");
bookService.addBook(book);
}
@Test
public void testBookUpdate(){
Book book = new Book();
book.setName("Java从入门到放弃 第二版");
book.setType("计算机");
book.setDescription("Java从入门到放弃第二版");
book.setId(13);
bookService.updateBook(book);
}
@Test
public void testBookDelete(){
bookService.deleteBook(13);
}
@Test
public void testBookSelectById(){
System.out.println(bookService.selectById(1));
}
@Test
public void testBookSelectAll(){
System.out.println(bookService.selectAll());
}
}
拓展:开启事务
Spring配置类中使用注解开启事务管理
package com.spark.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;
import org.springframework.transaction.annotation.EnableTransactionManagement;
/**
* SpringConfig class
* description: SpringConfig
*
* @author Administrator
* @date 2023/8/3
*/
@Configuration
@PropertySource({"classpath:jdbc.properties"})
@Import({JdbcConfig.class,MybatisConfig.class})
@ComponentScan({"com.spark.service","com.spark.dao"})
// 开启事务
@EnableTransactionManagement
public class SpringConfig {
}
在Jdbc配置类中获取事务管理对象
package com.spark.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import javax.sql.DataSource;
/**
* JdbcConfig class
* description: JdbcConfig
*
* @author Administrator
* @date 2023/8/3
*/
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 getDataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(driver);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
// 数据源事务管理对象
@Bean
public DataSourceTransactionManager transactionManager(DataSource dataSource){
DataSourceTransactionManager ds = new DataSourceTransactionManager();
ds.setDataSource(dataSource);
return ds;
}
}
在业务层使用事务管理注解
package com.spark.service;
import com.spark.entity.Book;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
/**
* BookService class
* description: BookService
*
* @author Administrator
* @date 2023/8/3
*/
@Transactional
public interface BookService {
boolean addBook(Book book);
boolean updateBook(Book book);
boolean deleteBook(Integer id);
Book selectById(Integer id);
List<Book> selectAll();
}
表现层与前端数据传输
由于数据传输可能会有多种格式,前端需要分别去解析数据,因此与前端交互时一般需要统一格式。创建结果模型类,封装数据到data属性中,封装操作结果到code属性中,封装特殊消息到message(msg)属性中
public class Result{
private String code;
private String msg;
private Object data;
}
Result类字段并不固定,可以根据需要自行增减,提供若干个构造方法,方便操作
定义统一结果类
package com.spark.util;
/**
* Result class
* description: Result
*
* @author Administrator
* @date 2023/8/5
*/
public class Result {
private String code;
private String msg;
private Object data;
public Result(String code, String msg, Object data) {
this.code = code;
this.msg = msg;
this.data = data;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
}
定义结果类所需的常量
package com.spark.common;
/**
* RestCommonContent class
* description: RestCommonContent
*
* @author Administrator
* @date 2023/8/5
*/
public class RestCommonContent {
// 定义响应编码和响应信息
public static final String RESPONSE_SUCCESS = "操作成功!";
public static final String RESPONSE_ERROR = "操作失败!";
public static final String RESPONSE_SUCCESS_CODE = "200";
public static final String RESPONSE_ERROR_CODE = "9999";
public static final String SELECT_SUCCESS = "查询成功!";
public static final String SELECT_ERROR = "数据不存在,查询失败!";
// 定义数据常量
public static final String CONTENT_EMPTY = "";
public static final String CONTENT_NULL = null;
}
改写表现层Controller
package com.spark.controller;
import com.spark.common.RestCommonContent;
import com.spark.entity.Book;
import com.spark.service.BookService;
import com.spark.util.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.ObjectUtils;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* BookController class
* description: BookController
*
* @author Administrator
* @date 2023/8/3
*/
@RestController
@RequestMapping("/books")
public class BookController {
@Autowired
private BookService bookService;
@PostMapping("/addBook")
public Result addBook(@RequestBody Book book){
if(bookService.addBook(book)){
return new Result(RestCommonContent.RESPONSE_SUCCESS_CODE,
RestCommonContent.RESPONSE_SUCCESS,
true);
}else{
return new Result(RestCommonContent.RESPONSE_ERROR_CODE,
RestCommonContent.RESPONSE_ERROR,
false);
}
}
@PutMapping("/updateBook")
public Result updateBook(@RequestBody Book book){
if(bookService.updateBook(book)){
return new Result(RestCommonContent.RESPONSE_SUCCESS_CODE,
RestCommonContent.RESPONSE_SUCCESS,
true);
}else{
return new Result(RestCommonContent.RESPONSE_ERROR_CODE,
RestCommonContent.RESPONSE_ERROR,
false);
}
}
@DeleteMapping("/deleteBook")
public Result deleteBook(Integer id){
if(bookService.deleteBook(id)){
return new Result(RestCommonContent.RESPONSE_SUCCESS_CODE,
RestCommonContent.RESPONSE_SUCCESS,
true);
}else{
return new Result(RestCommonContent.RESPONSE_ERROR_CODE,
RestCommonContent.RESPONSE_ERROR,
false);
}
}
@GetMapping("/getById")
public Result selectById(Integer id){
Book book = bookService.selectById(id);
if(ObjectUtils.isEmpty(book)){
return new Result(RestCommonContent.RESPONSE_ERROR_CODE,
RestCommonContent.SELECT_ERROR,
RestCommonContent.CONTENT_EMPTY);
}
return new Result(RestCommonContent.RESPONSE_SUCCESS_CODE,
RestCommonContent.SELECT_SUCCESS,
book);
}
@GetMapping("/getAll")
public Result selectAll(){
List<Book> books = bookService.selectAll();
if(ObjectUtils.isEmpty(books)){
return new Result(RestCommonContent.RESPONSE_SUCCESS_CODE,
RestCommonContent.SELECT_SUCCESS,
RestCommonContent.CONTENT_EMPTY);
}
return new Result(RestCommonContent.RESPONSE_SUCCESS_CODE,
RestCommonContent.SELECT_SUCCESS,
books);
}
}
异常处理器
开发中避免不了会遇到异常现象,如果不做处理,前端页面获取错误信息时拿到的是错误页面,无法进行解析,此时需要对异常进行统一处理
出现异常现象的常见位置与常见诱因如下
- 框架内部抛出的异常,使用不合规导致
- 数据层抛出的异常,外部服务器故障导致(例如服务器访问超时)
- 业务层抛出的异常,业务逻辑书写错误导致(例如空指针异常等)
- 表现层抛出的异常,数据收集、校验等规则导致(例如不匹配的数据类型间导致异常)
- 工具类抛出的异常,因工具类书写不严谨不够健壮导致(例如必要释放的连接长期未释放等)
Spring提供了异常处理器,集中的、统一的处理项目中出现的异常
@RestControllerAdvice
public class ProjectExceptionAdvice{
@ExceptionHandler(Exception.class)
public Result doException(Exception ex){
// 处理异常返回结果
}
}
@ExceptionHandler专用于异常处理的控制器方法上方,设置指定异常的处理方案,功能等同于控制器方法,出现异常后终止原始控制器执行,并转入当前方法执行
项目异常分类
业务异常(BusinessException)
- 不规范的用户行为产生的异常
系统异常(SystemException)
- 项目运行过程中可预计且无法避免的异常
其他异常(Exception)
- 编程人员未预期的异常
项目异常处理
编写业务异常类和系统异常类
package com.spark.exception;
/**
* BusinessException class
* description: BusinessException
*
* @author Administrator
* @date 2023/8/5
*/
public class BusinessException extends RuntimeException{
private String code; // 错误编码
private String tranCode; // 交易码(接口号)
public BusinessException(String code, String message, String tranCode) {
super(message);
this.code = code;
this.tranCode = tranCode;
}
public BusinessException(String code,String message, String tranCode, Throwable cause) {
super(message, cause);
this.code = code;
this.tranCode = tranCode;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getTranCode() {
return tranCode;
}
public void setTranCode(String tranCode) {
this.tranCode = tranCode;
}
}
package com.spark.exception;
/**
* SystemException class
* description: SystemException
*
* @author Administrator
* @date 2023/8/5
*/
public class SystemException extends RuntimeException {
private String code; // 错误编码
private String tranCode; // 交易码(接口号)
public SystemException(String code, String message, String tranCode) {
super(message);
this.code = code;
this.tranCode = tranCode;
}
public SystemException(String code,String message, String tranCode, Throwable cause) {
super(message, cause);
this.code = code;
this.tranCode = tranCode;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getTranCode() {
return tranCode;
}
public void setTranCode(String tranCode) {
this.tranCode = tranCode;
}
}
自定义异常错误编码和错误信息
package com.spark.common;
/**
* ExceptionContent class
* description: ExceptionContent
*
* @author Administrator
* @date 2023/8/5
*/
public class ExceptionContent {
public static final String BUSINESS_ERROR_CODE = "50001";
public static final String BUSINESS_DATA_ERROR = "数据有误,请检查您的数据格式是否正确!";
public static final String BUSINESS_ERROR_MSG = "操作有误,请联系管理员!";
public static final String SYSTEM_ERROR_CODE = "50002";
public static final String SYSTEM_ERROR_MSG = "服务器异常,请您稍后重试!";
public static final String SYSTEM_UNKNOWN_ERROR_CODE = "50003";
public static final String SYSTEM_UNKNOWN_MSG = "系统繁忙,请联系管理员!";
}
改写业务层根据id查询Book方法,处理异常
@Override
public Book selectById(Integer id) {
if(id<=0){
throw new BusinessException(ExceptionContent.BUSINESS_ERROR_CODE,
ExceptionContent.BUSINESS_DATA_ERROR,
"BK1004",
new Throwable(ExceptionContent.BUSINESS_DATA_ERROR));
}
return bookDao.getById(id);
}
编写异常处理器统一处理异常
package com.spark.exception;
import com.spark.common.ExceptionContent;
import com.spark.util.Result;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
/**
* ProjectExceptionAdvice class
* description: ProjectExceptionAdvice
*
* @author Administrator
* @date 2023/8/5
*/
@RestControllerAdvice
public class ProjectExceptionAdvice {
// 处理业务异常
@ExceptionHandler
public Result doBusinessException(BusinessException ex){
// 记录日志
System.out.println(ex.getTranCode() + "出现异常");
return new Result(ex.getCode(),ex.getMessage(),null);
}
// 处理系统异常
@ExceptionHandler
public Result doSystemException(SystemException sy){
// 记录日志
System.out.println(sy.getTranCode() + "出现异常");
return new Result(sy.getCode(),sy.getMessage(),null);
}
// 处理其他未知异常
@ExceptionHandler
public Result doException(Exception ex){
// 记录日志
System.out.println("出现未知异常");
System.out.println("异常信息如下:");
System.out.println(ex.getMessage());
return new Result(ExceptionContent.SYSTEM_UNKNOWN_ERROR_CODE,
ExceptionContent.SYSTEM_UNKNOWN_MSG,
null);
}
}
前后台联调
由于后台Servlet容器配置里面是拦截一切请求,前端代码在项目的webapp目录下,Tomcat启动后,去访问前端页面会被拦截,因此需要对前端资源进行放行
编写SpringMVC资源放行配置类
package com.spark.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
/**
* SpringMvcSupportConfig class
* description: SpringMvcSupportConfig
*
* @author Administrator
* @date 2023/8/5
*/
@Configuration
public class SpringMvcSupportConfig extends WebMvcConfigurationSupport {
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");
registry.addResourceHandler("/css/**").addResourceLocations("/css/");
registry.addResourceHandler("/js/**").addResourceLocations("/js/");
registry.addResourceHandler("/plugins/**").addResourceLocations("/plugins/");
}
}
在SpringMVC配置类中扫描config包
package com.spark.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
/**
* SpringMvcConfig class
* description: SpringMvcConfig
*
* @author Administrator
* @date 2023/8/3
*/
@Configuration
@ComponentScan({"com.spark.controller","com.spark.config"})
@EnableWebMvc
public class SpringMvcConfig {
}
拦截器
概述
拦截器是一种动态拦截方法调用的机制,在SpringMVC中动态拦截控制器方法的执行
作用:在指定的方法调用前后执行预先设定的代码;阻止原始方法的执行
拦截器和过滤器的区别
- 归属不同。Filter属于Servlet技术,Inteceptor属于SpringMVC技术
- 拦截内容不同。Filter对所有访问进行增强,Inteceptor仅对SpringMVC的访问进行增强
入门案例
编写拦截器
package com.spark.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;
/**
* ProjectInteceptor class
* description: ProjectInteceptor
*
* @author Administrator
* @date 2023/8/7
*/
@Component
public class ProjectInteceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("原始方法执行前执行...");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("原始方法执行后执行...");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("postHandle执行后执行...");
}
}
SpringMVC配置类中添加拦截器并扫描
package com.spark.config;
import com.spark.interceptor.ProjectInteceptor;
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;
/**
* SpringMvcSupportConfig class
* description: SpringMvcSupportConfig
*
* @author Administrator
* @date 2023/8/5
*/
@Configuration
public class SpringMvcSupportConfig extends WebMvcConfigurationSupport {
@Autowired
ProjectInteceptor projectInteceptor;
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");
registry.addResourceHandler("/css/**").addResourceLocations("/css/");
registry.addResourceHandler("/js/**").addResourceLocations("/js/");
registry.addResourceHandler("/plugins/**").addResourceLocations("/plugins/");
}
// 添加拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(projectInteceptor).addPathPatterns("/books/*","/books");
}
}
package com.spark.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
/**
* SpringMvcConfig class
* description: SpringMvcConfig
*
* @author Administrator
* @date 2023/8/3
*/
@Configuration
@ComponentScan({"com.spark.controller","com.spark.config","com.spark.interceptor"})
@EnableWebMvc
public class SpringMvcConfig{
}
拦截器链
当配置多个拦截器时,形成拦截器链
多拦截器执行顺序
- 当配置多个拦截器时,形成拦截器链
- 拦截器链的运行顺序参照拦截器添加顺序为准
- 当拦截器中出现对原始处理器的拦截,后面的拦截器均终止执行
- 当拦截器运行中断,仅运行配置在前面的拦截器的afterCompletion操作
preHandle方法按照拦截器配置顺序执行,postHandle和afterCompletion方法按照配置顺序倒序执行
工作流程
- 用户发送请求至前端控制器DispatcherServlet
- DispatcherServlet收到请求后,调用HandlerMapping处理器映射器,请求获取Handle
- 处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet
- DispatcherServlet调用HandlerAdapter处理器适配器
- HandlerAdapter经过适配调用具体处理器(Handler,也叫后端控制器)
- Handler执行完毕后返回ModelAndView
- HandlerAdapter将Handler执行结果ModelAndView返回给DispatcherServlet
- DispatcherServlet将ModelAndView传给ViewResolver视图解析器进行解析
- ViewResolver解析后返回具体View
- DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中)
- DispatcherServlet响应用户