SpringMVC基础
1 基础知识
1.1 什么是 SpringMVC?
SpringMVC 是一门属于 Spring 的技术,主要用于一定程度的简化 Web 开发,其主要对 Servlet 做了封装,使得我们的开发更加快捷;同时利用 Spring 的特性,使得开发更加简便快捷;
当然,传统 Servlet 开发的时候很多项目都是前端和后端是同步响应的方式,而 SpringMVC 更倾向于异步的响应,在向前端传递响应数据的时候非常的方便。
异步响应?
1) 给前端返回信息的时候只返回数据,前端获取到数据后将数据渲染到页面;
2) 在使用 Servlet 开发 Web 项目的时候,Servlet 本身是一个动态的资源,同时还有 JSP 等等,这两者都属于动态资源,需要先将它们编译成.class 文件的形式,然后服务器再将其渲染到前端。显然这是十分麻烦的事情;
3)在 Web 开发的基础课程当中,也有使用到异步响应,做法是使用 vue.js,后端返回 JSON 数据给前端,前端将数据获取之后直接渲染即可;
1.2 bean 的加载控制
1)在 SpringMVC 中有一个专门针对 SpringMVC 的配置类,而 Spring 本身也有一个 Spring 的配置类。
2)应该将 controller 层的 bean 将给 SpringMVC 管理,其他的 bean 交给 Spring 管理;
主要的有几种方式:
1)修改Spring配置类,设定扫描范围为精准范围;
@Configuration
@ComponentScan({"cn.edu.njust.dao", "cn.edu.njust.service"})
public class SpringConfig {
}
2)修改Spring配置类,设定扫描范围为cn.edu.njust,排除掉controller包中的bean;
@Configuration
//@ComponentScan({"cn.edu.njust.dao", "cn.edu.njust.service"})
@ComponentScan(value = "cn.edu.njust",
excludeFilters = @ComponentScan.Filter(
type = FilterType.ANNOTATION,
classes = Controller.class
))
public class SpringConfig {
}
3)不区分 Spring 与 SpringMVC 的环境,加载到同一个环境中;
这种方式不常用
1.3 Web 项目中加载 Spring 和 SpringMVC 上下文
有两种方式,一种是比较中规中矩的方式,先声明变量,然后将配置类注册进去;
这个配置类在 SpringWeb 项目中十分重要的,是加载配置路由加载 bean 等等十分重要的配置;
主要是继承AbstractDispatcherServletInitializer
并且实现三个方法;
1.3.1 方式一:注册配置类的 class 对象
package cn.edu.njust.config;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.servlet.support.AbstractDispatcherServletInitializer;
import javax.servlet.Filter;
public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer {
/**
* 加载SpringMVC配置类,获取SpringMVC/SpringWeb上下文环境,我看作是获取的IoC容器
* 和之前使用注解加载IoC容器的时候类似,不过这里为了获取SpringMVC的bean
* 这里获取IoC对象ctx的形式和Spring中有些区别就是:这里没有直接将Class对象传入的方法,需要调用register注册
*
* @return SpringMVC的IoC对象
*/
protected WebApplicationContext createServletApplicationContext() {
//初始化WebApplicationContext对象
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
//加载指定配置类
ctx.register(SpringMvcConfig.class);
return ctx;
}
/**
* 设置由springmvc控制器处理的请求映射路径
* 即SpringMVC拦截哪些请求
* @return 拦截的路由数组
*/
protected String[] getServletMappings() {
return new String[]{"/"};
}
/**
* 加载spring环境,获取Spring的容器上下文环境
* createRootApplicationContext方法:
* 如果创建Servlet容器时需要加载非SpringMVC对应的bean,使用当前方法进行
* 使用方式和createServletApplicationContext相同
*
* @return Spring的上下文环境
*/
protected WebApplicationContext createRootApplicationContext() {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.register(SpringConfig.class);
return ctx;
}
/**
* 乱码处理
* @return 一个过滤器数组
*/
@Override
protected Filter[] getServletFilters() {
CharacterEncodingFilter filter = new CharacterEncodingFilter();
filter.setEncoding("UTF-8");
return new Filter[]{filter};
}
}
1.3.2 方式二:返回配置类数组
使用这两个方法代替上面的两个方法
package cn.edu.njust.config;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.servlet.support.AbstractDispatcherServletInitializer;
import javax.servlet.Filter;
public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer {
/**
* 设置由springmvc控制器处理的请求映射路径
* 即SpringMVC拦截哪些请求
* @return 拦截的路由数组
*/
protected String[] getServletMappings() {
return new String[]{"/"};
}
// Spring上下文环境
protected Class<?>[] getRootConfigClasses() {
return new Class[]{SpringConfig.class};
}
// SpringMvc上下文环境
protected Class<?>[] getServletConfigClasses() {
return new Class[]{SpringMvcConfig.class};
}
/**
* 乱码处理
* @return 一个过滤器数组
*/
@Override
protected Filter[] getServletFilters() {
CharacterEncodingFilter filter = new CharacterEncodingFilter();
filter.setEncoding("UTF-8");
return new Filter[]{filter};
}
}
2 SpringMVC 中的请求与响应
2.1 请求路径
请求路径是指使用 url 具体匹配到哪个方法,具体的做法是使用 @RequestMapping 注解设置访问路径
1)设置在类上:整个类中所有方法的访问都需要经过这个路径
2)设置在方法上:给每个方法指定具体的访问路径,方法的访问路径是 类+方法 上的注解路径
// 访问路径
@RequestMapping("/save")
@ResponseBody
public String save() {
System.out.println("save in running...");
return "{'info':'SpringMVC'}";
}
2.2 请求参数
主要是前端在发起请求的时候通常会带有一定的参数,后端需要接受参数做一定的处理。而请求参数一般有好几种形式,如路径参数,get 请求参数,请求体参数等等;
2.2.1 GET 请求
直接在方法后用参数进行接受,需要指定参数的类型
1)接受参数的形式
@RequestMapping("/commonParam")
@ResponseBody
public String commonParam(String name, int age){
System.out.println("普通参数传递 name = " + name);
System.out.println("普通参数传递 age = " + age);
return "{'module':'commonParam'}";
}
2)乱码问题:如果使用 tomcat7 插件运行,会遇到中文乱码问题,解决方法是在 pom.xml 文件中将编码格式指定为 UTF-8
<build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.1</version>
<configuration>
<port>8080</port>
<path>/</path>
<uriEncoding>UTF-8</uriEncoding>
</configuration>
</plugin>
</plugins>
</build>
3)使用本地 Tomcat 乱码
Tomcat8 以上的版本一般不会 Get 请求乱码,但是较低版本的还是可能会存在问题的,如果发生这样的错误,可以使用这样的方式来解决
原理:默认的 URL 使用 ISO-8859-1 字符集,使用 UTF-8 字符集重新编码;其中 str 是获取到的乱码的字符串
str = new String(str.getBytes(StandardCharsets.ISO_8859_1),StandardCharsets.UTF_8);
2.2.2 POST 请求
1) 接受参数和 GET 请求类似:https://www.yuque.com/gzwangsong/mystudy_java/gd0k2pl8gaggvnxg?inner=LqCF8
一般来说,POST 请求不推荐使用 GET 请求的参数形式,但是确实可以使用那样形式的参数
2)中文乱码:POST 请求的中文乱码和 GET 请求的中文乱码不一样,通常是使用过滤器对请求的数据进行处理
//乱码处理
@Override
protected Filter[] getServletFilters() {
CharacterEncodingFilter filter = new CharacterEncodingFilter();
filter.setEncoding("UTF-8");
return new Filter[]{filter};
}
2.3 五种类型参数的传递
2.3.1 简单数据参数
基本数据类型和 String 字符串类的,一把是直接在方法里面使用参数来接收即可
@RequestMapping("/commonParam")
@ResponseBody
public String commonParam(String name,int age){
System.out.println("普通参数传递 name ==> "+name);
System.out.println("普通参数传递 age ==> "+age);
return "{'module':'commonParam'}";
}
2.3.2 POJO 类型的参数
需要请求请求参数的名称和 POJO 对象的成员变量名称一致;
2.3.3 嵌套 POJO 类型的参数
类似 2.3.2,需要成员变量的名称和前端请求数据的名称一致;
2.3.4 数组类型参数
直接定义数据类型的参数接收即可
//数组参数:同名请求参数可以直接映射到对应名称的形参数组对象中
@RequestMapping("/arrayParam")
@ResponseBody
public String arrayParam(String[] likes){
System.out.println("数组参数传递 likes ==> "+ Arrays.toString(likes));return "{'module':'array param'}";
}
2.3.5 集合类型参数
//集合参数:同名请求参数可以使用@RequestParam注解映射到对应名称的集合对象中作为数据
@RequestMapping("/listParam")
@ResponseBody
public String listParam(List<String> likes){
System.out.println("集合参数传递 likes ==> "+ likes);
return "{'module':'list param'}";
}
2.4 响应
对于 SpringMVC 的响应,主要包含两个方面:响应页面和响应数据;
2.4.1 响应页面
直接返回页面的名称,会直接跳转到响应的页面;
2.4.2 响应文本数据
直接返回即可
2.4.3 响应 JSON 数据
1)将数据封装到 POJO 类型数据
将请求的数据封装到 POJO 模型当中,参数名称和 POJO 模型中的属性名称相对应,Spring 底层会自动将数据类型给封装到 POJO 模型的数据当中;
2)开启 SpringMVC 的增强功能:@EnableWebMvc
@Configuration
@ComponentScan("cn.edu.njust.controller")
@EnableWebMvc
public class SpringMvcConfig {
}
3)@ResponseBody:标识方法返回的是数据;
3 JSON 数据传输
在 SpringMVC 参与开发的项目中,大多数是使用 JSON 数据格式进行前后端的数据交互;
SpringMVC默认使用的是jackson来处理json的转换,所以需要在pom.xml添加jackson依赖
3.1 简单使用
1)导入依赖坐标,加载依赖
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.2</version>
</dependency>
2)开启 SpringMVC 对 JSON 的支持
主要是:@EnableWebMvc 注解
@Configuration
@ComponentScan("cn.edu.njust.controller")
@EnableWebMvc
public class SpringMvcConfig {
}
3)使用@RequestBody 注解著名参数
@RequestMapping("/jsonarray")
@ResponseBody
public String jsonArray(@RequestBody List<Integer> list) {
System.out.println(list);
return "{'status':'true'}";
}
@RequestMapping("/jsonobject")
@ResponseBody
public String jsonRequest(@RequestBody User user) {
System.out.println(user);
return "{'status':'true'}";
}
3.2 SpringMVC 的转换 JSON 的底层原理
SpringMVC 底层通过一些转换数据的接口,对我们传入的数据进行转换,其中一个接口是:Converter,所属的包为org.springframework.core.convert.converter
;
4 REST 风格
1)对 URL 的请求路径做隐藏,隐藏客户的行为动作;
2)同一个 URL 使用不同的方式发起请求,代表着不同的意义;
3)常见的风格(不是规范)
3.1) 发送GET请求是用来做查询
3.2) 发送POST请求是用来做新增
3.3) 发送PUT请求是用来做修改
3.4) 发送DELETE请求是用来做删除
指定请求参数:
@Controller
public class UserController {
//设置当前请求方法为POST,表示REST风格中的添加操作
@RequestMapping(value = "/users",method = RequestMethod.POST)
@ResponseBody
public String save() {
System.out.println("user save...");
return "{'module':'user save'}";
}
}
/**
* restful风格的请求,使用 method 指明请求方法
* @param userId -- 使用 PathVariable 绑定路径参数,如果参数名称与路径参数一样,可以省略
* @return 可以直接返回字符串
*/
@RequestMapping(value = "/user/test/{id}", method = RequestMethod.GET)
@ResponseBody
public String testMethod(@PathVariable("id") Integer userId) {
System.out.println(userId);
return "true";
}
5 Spring 中的拦截器
5.1 拦截器和过滤器
1)归属不同:过滤器(Filter)属于 Servlet 技术,而拦截器(interceptor)是属于 SpringMVC 的技术;
2)拦截的内容不同:过滤器拦截访问,对访问进行增强控制;而拦截器主要对 SpringMVC 的访问进行控制;
3)顺序:请求应该先经过过滤器,再到达拦截器;响应先通过拦截器,再通过过滤器;
5.2 实例
1)实现 HandlerInterceptor 接口
package cn.edu.njust.controller.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
//定义拦截器类,实现HandlerInterceptor接口
//注意当前类必须受Spring容器控制
public class ProjectInterceptor implements HandlerInterceptor {
@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...");
}
}
2)添加拦截器
package cn.edu.njust.config;
import cn.edu.njust.controller.interceptor.ProjectInterceptor;
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;
@Configuration
public class SpringMvcSupport extends WebMvcConfigurationSupport {
@Autowired
private ProjectInterceptor projectInterceptor;
@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/");
}
/**
* 配置拦截器
* @param registry
*/
@Override
protected void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(projectInterceptor).addPathPatterns("/books");
}
}
3)将拦截器添加到 SpringMVC 的配置类中