SpringMVC
项目配置
注意事项
注意 JDK 版本和 spring-webmvc 版本配置。不匹配将会出现很多的问题异常,目前配置:
- JDK 1.8
- spring-webmvc 4.1.3.RELEASE
- javax.servlet-api 4.0.1
SpringMVC 技术与 Servlet 技术功能相等,均属于 web 层开发技术 是一种基于 java 实现的 MVC 模型的轻量级 WEb 框架 SpringMVC 是一种表现层框架技术 SpringMVC 用于进行表现层功能开发
- 基于 SpringMVC 获取请求参数与响应 json 数据
- 熟练应用基于 REST 风格的请求路径设置与参数传递
- 能够根据实际业务简历前后端开发通信协议并进行实现
- 基于 SSM 整合技术开打任意模块的功能
SpringMVC 的开发步骤
- 引入坐标
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.22</version>
</dependency>
<!-- spring-webmvc版本使用 5.3 以上会出现 NoSuchMethodException 异常 -->
- 创建 SpringMVC 控制器类
package com.lmb.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class UserController() {
@RequestMapping("/save")
@ResponseBody
public String save() {
System.out.println("user save ...");
return "{ 'info': 'springmvc' }";
}
}
- 初始化 SpringMVC 配置文件,同 Spring 配置文件,设定 SpringMVC 加载对应 Bean
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan("com.lmb.controller")
public class SpringMvcConfig() {}
- 初始化 Servlet 容器,加载 SpringMVC 环境, 并设置 SpringMVC 技术处理得请求
// SpringMVC 已经定义好了一个类 AbstractDispatcherServletInitializer,
// 我们只需要继承自这个类
import com.lmb.config.SpringMvcConfig;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.support.AbstractDispatcherServletInitializer;
public class WebApplicationContext extends AbstractDispatcherServletInitializer {
protected WebApplicationContext createServletApplicationContext() {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.register(SpringMvcConfig.class);
return ctx;
}
/*
* 配置那些请求路径交给 SpringMVC 处理,
* */
protected String[] getServletMapping() {
return new String[]{"/"};
}
protected WebApplicationContext createRootApplicationContext() {
return null;
}
}
- 配置 Maven Tomcat 插件,启动开发 tomcat 服务器环境
<!--pom.xml-->
<build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<port>9000</port>
<path>/</path>
</configuration>
</plugin>
</plugins>
</build>
SpringMVC 的工作流程
服务器初始化的过程
- 启动服务器,执行 ServletContainersInitConfig 类,初始化 web 容器
- 执行 createServletApplicationContext 方法,创建了 WebApplicationContext 对象
- 加载 SpringMvcConfig
- 执行 @ComponentScan 加载对应的 bean
- 加载 UserController,每个 @RequestMapping 标注的名称对应一个 具体的方法
- 执行 getServletMappings 方法,获取哪些请求由 SpringMVC 来处理
单次请求的过程
- 发送请求 localhost/save
- web 容器检测该请求路径是否应该交给 SpringMVC,是的话将交给 SpringMVC 来处理
- 解析请求路径 /save
- 由 /save 匹配到对应的执行方法 save()
- 执行 save()
- 检测到有 #ResponseBody 直接将 save() 方法的返回值作为响应体返回给请求方
注解
-
@Controller
- 设定在 SpringMVC 控制器类的定义上方
- 用于设定 SpringMVC 核心的控制器 Bean
-
@RequestMapping("/访问路径")
- 写在方法的上面
- 用来设置访问路径
- 也可以写在 controller 类的上面表示统一前缀
- attr
- method:指定请求方式,没有指定默认GET,POST请求都可以访问
-
@ResponseBody
- 写在 SpringMVC 方法的上面
- 设置当前控制器方法响应内容为当前的返回值,无需解析。
-
@RequestParam("name")
- 写在方法参数前面
- 用来绑定请求参数和方法中的形参
-
@EnableWebMvc
- 写在 SpringMVC 配置类上
- 开启自动转换 json 数据的支持
- 使用 @RequestBody 标记一个 json 入参
-
@RequestBody
- 写在方法形参之前
- 标注形参为 json 格式的参数,此注解一个处理器方法只能写一个
- 写在方法之上
- 设置当前的控制器返回值为响应体
- 写在 Controller 类之上代表该控制器中的方法都将返回值作为响应体
- 写在方法形参之前
-
@DateTimeFormat(pattern = "yyyy-MM-dd")
- 写在方法参数之前
- 用来定义时间格式参数的解析模式
-
@PathVariable
- 从请求的路径中注入参数,需要配合 @RequestMapping 只当参数匹配模式
- 例如 @RequestMapping("/{id}")
-
@RestController
- 标识使用 REST 开发的控制器类,等于在类上加入注解 @Controller + @ResponseBody
-
@PostMapping/@GetMapping/@PutMapping/@DeleteMapping
@RequestParam 和 @RequestBody 之间有什么区别
- 区别
- @RequestParam 用于接收 url 地址传参,表单传参[content-type: application/x-www-form-urlencoded]
- @RequestBody 用于接收 json 格式的数据[content-type: application/json]
- 应用
- 后期开发中,发送 json 格式的数据为主,@RequestBody 使用的范围较广
- 如果发送非 json 格式的数据,选用 @RequestParam 接受请求参数(文件)
Spring 中整合 SpringMVC 注意事项
项目一般来说 com.lmb 下分
- service
- dao
- domain
- controller
- config
- ...
其中 SpringMVC 控制的 Bean 位于 controller 中,Spring 控制的 Bean 位于除了 controller 中的其他包 在进行 Bean 扫描控制(@ComponentScan)的时候注意不要重复扫描
- Spring 加载的 Bean 设定扫描范围为 com.lmb,但是排除掉 controller
// SpringConfig.java
@Configuration
//@ComponentScan({ "com.lmb.service", "com.lmb.dao" })
@ComponentScan(
value="com.lmb",
excludeFilters = @ComponentScan.Filter(
type = FilterType.ANNOTATION,
classes = Controller.class
)
)
public class SpringConfig {}
- Spring 加载的 Bean 设定扫描范围精准设定为,dao,domain,service....
@Configuration
//@ComponentScan({ "com.lmb.service", "com.lmb.dao", "com.lmb.domain" })
public class SpringConfig {}
- 不区分环境
乱码解决问题
- Post 为 Web 容器添加过滤器并只当字符集为 UTF-8, Spring-web包中提供了字符过滤器
// 在 SpringMVC Web 容器配置类中重写以下的方法,并添加字符过滤器
public class ServletContainerInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Filter[] getServletFilters() {
CharacterEncodingFilter filter = new CharacterEncodingFilter();
filter.setEncoding("UTF-8");
return new Filter[]{filter};
}
}
- Get
请求与响应
参数传递
- 直接在方法上加入参,一个入参就代表一个请求参数,请求时会将对应的参数直接作为形参传递进方中
@RequestMapping("/save")
@ResponseBody
public String save(String name, int id) {
System.out.println("user save ....");
System.out.println("name: " + name);
System.out.println("id: " + id);
return "{'module': 'springmvc'}";
}
- 请求参数和方法参数不一致
- 使用 @RequestParam("name") 绑定映射关系
- 使用实体类作为形参接受参数
-
实体类中要切记要提供无参的构造方法和有参的构造方法
-
实体类中要提供成员变量的 Getter 和 Setter 方法
-
实体类中最好重写 toString 方法
-
如果参数的属性名和实体类中的成员变量一致,可以自动转换获取实体类对象
-
如果实体类中有引用数据类型,可以 address.city 这样的形式来传参(不建议使用)
- 使用数组形式接收
- 请求中多个一样的参数名会自动转化成一个数组
- localhost:9000/user/add?likes=swimming&likes=run&likes=eat
- 方法定义:public String add(String[] likes) {}
- 使用集合传参
- 集合参数前面要加上 @RequestParam 注解
- JSON 参数
- 先引入 jackson-databind 依赖
- 在 SpringMVC 配置类中加入注解 @EnableWebMvc
- Date 时间类型
- SpringMVC 可以自动的解析格式为 "yyyy/MM/dd" 的字符串为 Date 类型参数
- 如果需要自定以解析格式,可以在参数加上 @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") 注解
响应请求
- 响应页面
- public String jumpToPage() { return "页面路径" }
- 只需要返回一个页面路径的字符串就行
- 不要在方法上加入 @ResponseBody 注解,这个注解会让返回值我作为响应发送给请求方,这样返回给请求方的就是一个字符串而不是页面了
REST 风格
REST (Representational State Transfer),表现形式状态转换
REST
- 请求路径风格
-
传统 url 风格
-
REST 风格
-
REST 风格中的通过 HTTP 请求方式区分操作类型
- GET:获取/查询
- POST:新增/保存
- PUT:修改/更新
- DELETE:删除
-
优点
- 书写简化
- 隐藏了资源访问的行为,无法通过地址得知是对什么资源的访问
- 使用 REST 风格定义用户的增删改查
- /users 查询全部用户 GET
- /users/1 查询指定 id 的用户 GET
- /users 添加用户 POST
- /users 修改用户信息 PUT
- /users/1 删除指定 id 的用户 DELETE
-
为什么叫风格 REST 只是一种资源定义的思想,并不是强制性要求,开发中开发者可以根据自身需求选择遵循 也可以选择不遵循。所以称之为一种风格,而不是一种规范
-
模块的名称描述通常使用复数形式,也就是加 s 的风格描述,表示此类资源,而非单个资源,如:users,books,accounts ...
-
根据 REST 风格对资源进行访问叫做 RESTful
SpringMVC 中对 REST 的支持
- 修改请求方式,@RequestMapping(method = RequestMethod.GET/POST/PUT/DELETE)
- 指定请求的参数
- @RequestMapping(value = "/{id}", method = RequestMethod.GET)
- public String getById(@PathVariable Integer id)
@RequestBody,@RequestParam,@PathVariable
- 区别
- @RequestParam 用于接收 url 传参或者表单传参
- @RequestBody 用来接收 json 数据传参
- @PathVariable 用于接收 url 传参,使用 {参数名称} 描述路径参数
- 应用
- 后期开发中,发送请求的参数超过 1 个时,以 json 格式为主,@RequestBody 使用广泛
- 如果发送非 json 的数据,选用 @RequestParam 接收请求参数
- 采用 RESTful 风格进行开发时,当参数数量较少,例如 1 个,采用 @PathVariable 接收请求路径的变量,通常用于传递 id
开发简化
- 使用 @RestController 代替控制器类中的 @Controller 和 @ResponseBody
- 控制器类上添加 @RequestMapping 定义总体的路径
- 控制器类方法上的 @RequestMapping 更换成 @PostMapping/@GetMapping/@PutMapping/@DeleteMapping 定义请求方式和路径
SSM 整合
拦截器
统一返回的风格
package com.lmb.pojo;
public class CommonRespond {
private Object data;
private Integer code;
private String msg;
/* 提供构造方法(无参/有参),提供 Getter/Setter 方法 */
}
/*
* 1. data:数据
* 2. code:响应码
* 3. msg:消息
* */
异常处理器
- 异常的分类
- 框架内容抛出的异常:使用不合理导致
- 数据层抛出的异常(dao):因外部服务器故障导致
- 业务层抛出的异常(service):因为业务逻辑书写错误导致
- 表现层抛出的异常(controller):因为数据收集,校验等规则导致
- 工具类抛出的异常(utils):因为工具类书写不严谨导致不够健壮导致
- 各个层都有可能出现异常,如何处理
- 所有的异常都上抛出到表现层(controller),由变现层来处理。(为什么:再往上抛出就到前端了,所以我们要在表现层处理)
- 出现异常就要处理,可以使用 AOP 来处理,
- springMVC 根据 AOP 思想对异常封装了异常处理器
- spring 中的异常处理
- @RestControllerAdvice:写在类上,定义一个异常处理类
- @ExceptionHandler(List):写在异常处理类中方法上,定义某些异常的处理方法
- 项目异常处理方案
-
对异常分类
- 业务异常(BusinessException)
- 规范额用户行为产生的异常
- 不规范的用户行为操作产生的异常
- 系统异常(SystemException)
- 项目运行过程中可预计但无法避免的异常
- 其他异常(Exception)
- 开发人员未预期的异常
-
处理
- 业务异常
-
发送对应的信息给用户,提醒规范操作
- 系统异常
-
发送固定的消息给用户,安抚用户
-
发送特定消息给运维人员,提醒维护
-
记录日志
- 其他异常
- 发送固定消息给用户,安抚用户
- 发消息给编程人员,提醒维护
- 记录日志
拦截器
拦截器(Interceptor)是一种动态拦截方法调用的机制
-
作用
- 在指定的方法调用前后执行预先预定后的代码
- 组织原始方法的执行
-
拦截器和过滤器(Filter)的区别
- Filter 属于 Servlet 技术,Interceptor 属于 SpringMVC 技术
- 拦截的内容不同,Filter 对访问进行增强,Interception仅是针对 SpringMVC 的访问进行增强
-
拦截器的执行时机
- 可以在 controller 前和后执行
4, SpringMVC 中定义拦截器
定义一个拦截器类,该拦截器类要定义成一个 bean,且保证该 bean 可以被 SpringMVC 配置类加载到
import org.springframework.web.servlet.HandlerInterceptor;
// 定义一个拦截器类实现 HandlerInterception 接口
@Component
public class ProjectInterception implements HandlerInterceptor {
/*
* 执行 controller 之前预处理,
* return false:不会进入具体的 controller 方法,不会执行原始方法
* return true:在拦截器之后继续执行原始操作
* */
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle...");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle...");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion");
}
}
注册拦截器 在 registry.addInterceptor(interception).addPathPatterns("/folder") 中 拦截的路径可以写多个 addPathPatterns("/folder", "/folder/1", "...") 也可以使用通配符的形式
@Configuration
public class SpringMvcSupport extends WebMvcConfigurationSupport {
@Autowired
private ProjectInterception interception;
@Override
protected void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(interception).addPathPatterns("/folder");
}
}