SpringMvc 纯注解搭建和 Request 小结
SpringMvc 是一种底层基于 Servlet 实现 MVC 模型的轻量级 Web 开发框架,是一种侧重于表现层的开发技术。
SpringMvc 使用起来比 Servlet 要方便很多,性能也很不错,常用于小型项目的快速搭建和开发。本篇博客不会介绍有关 MVC 的理论知识,主要侧重于代码实践,采用纯注解的方式快速搭建 SpringMvc 工程,并展示常用的接收请求参数的方式。在本篇博客的最后会提供 Demo 的源代码。
有关 SpringMvc 的详细使用可参考官网:
https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc
一、搭建工程
新建一个 maven 项目,导入相关 jar 包,我所导入的 jar 包都是最新的,内容如下:
有关具体的 jar 包地址,可以在 https://mvnrepository.com 上进行查询。
<dependencies>
<!--导入 servlet 相关的 jar 包-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
<scope>provided</scope>
</dependency>
<!--导入 Spring 核心 jar 包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.18</version>
</dependency>
<!--导入 SpringMvc 的 jar 包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.18</version>
</dependency>
</dependencies>
配置好引用的 jar 包后,打开右侧的 Maven 窗口,刷新一下,这样 Maven 会自动下载所需的 jar 包文件。
搭建好的项目工程整体目录比较简单,具体如下图所示:
项目工程结构简单介绍:
com.jobs.config 包下存储的是 SpringMvc 的配置文件和 Servlet 的初始化文件
com.jobs.controller 包下存储的是用于提供 http 请求的类
com.jobs.domain 包下存储的是 JavaBean 实体类
web 目录下放置的是网站文件,其中 statics 目录下存放是静态资源文件
二、SpringMvc 配置相关
com.jobs.config 下的 SpringMvcConfig 类是 SpringMvc 的配置类,具体内容如下:
package com.jobs.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.stereotype.Controller;
import org.springframework.web.servlet.config.annotation.*;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;
@Configuration
@ComponentScan("com.jobs")
//启用 mvc 功能,配置了该注解之后,SpringMvc 拦截器放行相关资源的设置,才会生效
@EnableWebMvc
public class SpringMvcConfig implements WebMvcConfigurer {
//配置 SpringMvc 拦截器放行的资源类型
/*@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/statics/**").addResourceLocations("/statics/");
//registry.addResourceHandler("/statics/css/**").addResourceLocations("/statics/css/");
//registry.addResourceHandler("/statics/img/**").addResourceLocations("/statics/img/");
//registry.addResourceHandler("/statics/js/**").addResourceLocations("/statics/js/");
}*/
//配置 SpringMvc 连接器放行常用资源的格式(图片,js,css)
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
//注解配置 SpringMvc 返回配置的字符串所表示的页面,从哪些去找
//可以注释掉下面的方法,这样需要在 SpringMvc 方法返回时,指定全局路径的页面地址
//这里配置的是:根据 SpringMvc 方法返回的字符串,到 /WEB-INF/pages/ 下找对应名称的 jsp 页面
@Bean
public InternalResourceViewResolver getViewResolver(){
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix("/WEB-INF/pages/");
viewResolver.setSuffix(".jsp");
//如果页面需要使用JSTL标签库的话
//viewResolver.setViewClass(JstlView.class);
return viewResolver;
}
}
这里需要注意以下几项:
- 使用注解 @ComponentScan 中的 includeFilters 指定 SpringMvc 只扫描装载带有 @Controller 注解的类,其它的注解不需要给 SpringMvc 扫描,其它的注解全部交给 Spring 来扫描装载。
- 必须在 SpringMvc 的配置类上添加 @EnableWebMvc 注解,这样才能够有机会访问到静态资源(图片、css、js)。
- 可以重写 configureDefaultServletHandling 方法,只需要一句代码 configurer.enable() 即可让 SpringMvc 拦截器放行静态资源的访问限制。
SpringMvc 技术底层是基于 Servlet 实现的,因此必须让 Servlet 装载才能实现,我们编写 ServletInitConfig 类初始化 Servlet 容器,装载 SpringMvc 的配置,具体内容如下:
package com.jobs.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.DispatcherType;
import javax.servlet.FilterRegistration;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import java.util.EnumSet;
public class ServletInitConfig extends AbstractDispatcherServletInitializer {
//初始化 Servlet 容器,加载 SpringMvc 配置类
//创建 web 专用的 Spring 容器对象:WebApplicationContext
@Override
protected WebApplicationContext createServletApplicationContext() {
AnnotationConfigWebApplicationContext cwa = new AnnotationConfigWebApplicationContext();
//装载 SpringMvc 的配置
cwa.register(SpringMvcConfig.class);
return cwa;
}
//配置 SpringMvc 的 DispatcherServlet 拦截地址,拦截所有请求
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
@Override
protected WebApplicationContext createRootApplicationContext() {
return null;
}
//在servlet容器启动时进行配置统一字符编码,防止接收到的中文参数值是乱码
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
//这行代码不能省略
super.onStartup(servletContext);
//设置【获取到的请求】的统一字符编码过滤器,
//无论是【请求、转发、包含】统一使用 UTF-8 编码
CharacterEncodingFilter cef = new CharacterEncodingFilter();
cef.setEncoding("UTF-8");
//注意:这里的过滤器名称必须是 characterEncodingFilter
FilterRegistration.Dynamic registration =
servletContext.addFilter("characterEncodingFilter", cef);
registration.addMappingForUrlPatterns(
EnumSet.of(DispatcherType.REQUEST,
DispatcherType.FORWARD, DispatcherType.INCLUDE), false, "/*");
}
}
经过上面两个类的处理,SpringMvc 纯注解方式已经配置完毕,下面就可以使用 SpringMvc 技术处理请求了。
三、验证搭建成果
我们编写 ReqController1 类来验证 SpringMvc 搭建成果,具体内容如下:
package com.jobs.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
//@RequestMapping 注解可以在类上使用,也可以在类中具体的方法上使用
@RequestMapping("/req1")
@Controller
public class ReqController1 {
//使用配置的前缀和后缀
@RequestMapping("/test1")
public String SpringMvcTest1() {
System.out.println("SpringMvc 搭建成功...默认使用所配置的前缀和后缀");
//由于在 SpringMvcConfig 类中的 getViewResolver 方法,配置了前缀和后缀
//因此这里如果返回字符串的话,默认会到 /WEB-INF/pages/ 下找对应名称的 jsp 页面
//由于这里返回字符串 success,因此直接找 /WEB-INF/pages/success.jsp 页面
return "success";
}
//临时不想使用所配置的前缀和后缀
@RequestMapping("test2")
public String SpringMvcTest2() {
System.out.println("SpringMvc 如果不想使用所配置的前缀和后缀,可以使用 forward 或 redirect");
//当使用了 forward 或 redirect 时,将不使用所配置的前缀和后缀
return "forward:/ok.html";
//return "redirect:/ok.html";
}
//访问静态资源
@RequestMapping(value = "test3")
public String SpringMvcTest3() {
System.out.println("SpringMvc 查看静态资源");
//由于在 SpringMvcConfig 类中重写了 configureDefaultServletHandling 方法,
//启用了常用的静态资源访问,所以 SpringMvc 不会拦截静态资源的访问
//在 picture.jsp 页面,可以看到 css 样式效果,图片展示,以及外部 js 引用后可以运行
return "picture";
}
}
上面用到的静态资源内容如下:
test.css 的内容为:
h1{
color: coral;
}
test.js 的内容为:
function testalert() {
alert('努力学习,天天向上!!!');
}
所用到的页面内容如下:
success.jsp 页面代码为:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>请求成功</title>
<link rel="stylesheet" href="${pageContext.request.contextPath}/statics/css/test.css">
</head>
<body>
<h1>这里是 jsp 动态页面:success.jsp</h1>
<h1>请求成功,请查看控制台上打印的结果...</h1>
</body>
</html>
picture.jsp 页面代码为:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<link rel="stylesheet" href="${pageContext.request.contextPath}/statics/css/test.css">
</head>
<body>
<h1>这里是 picture.jsp 展示静态资源(图片,css,js)的访问:</h1>
<img src="${pageContext.request.contextPath}/statics/img/study.jpg"/>
<input type="button" onclick="testalert()" value="点击我,运行js弹出框"/>
<!--建议把 js 文件放在网页的最下面,提高网页的打开速度-->
<script src="${pageContext.request.contextPath}/statics/js/test.js"></script>
</body>
</html>
ok.html 静态页面的代码为:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>请求成功</title>
<link rel="stylesheet" href="/statics/css/test.css">
</head>
<body>
<h1>这里是静态页面:ok.html</h1>
<h1>请求成功,请查看控制台上打印的结果...</h1>
</body>
</html>
四、请求参数的接收
首先列出 Employee 实体类,因为下面的代码会演示使用实体类作为接收参数,来接收请求参数中的值,然后编写 ReqController2 类,来演示在实际开发中,常用的一些请求参数的接收方式,具体细节如下:
package com.jobs.domain;
import java.util.List;
public class Employee {
//姓名
private String name;
//年龄
private Integer age;
//爱好
private List<String> hobby;
//这里省略了各个字段的 get 和 set 方法...
@Override
public String toString() {
return "Employee{" +
"name='" + name + '\'' +
", age='" + age + '\'' +
", hobby=" + hobby +
'}';
}
}
package com.jobs.controller;
import com.jobs.domain.Employee;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
//@RequestMapping 注解可以在类上使用,也可以在类中具体的方法上使用
//@RequestMapping("req2")
@Controller
public class ReqController2 {
//请求参数与接收参数,名称必须一致,如果参数缺少,则参数取类型默认值
//http://localhost:8080/request1?name=jobs
//http://localhost:8080/request1?name=jobs&age=33
@RequestMapping("/request1")
public String request1(String name, Integer age) {
String result = "name=" + name + ",age=" + age;
System.out.println(result);
return "success";
}
//设置请求参数的名称,以及是否为必须提供的参数,以及设置参数的默认值
//http://localhost:8080/request2 报错,没有提供必填参数 userName
//http://localhost:8080/request2?name=jobs
//http://localhost:8080/request2?name=jobs&age=33
@RequestMapping("/request2")
public String request2(
@RequestParam(value = "userName", required = true) String name,
@RequestParam(value = "userAge", defaultValue = "30") Integer age) {
String result = "name=" + name + ",age=" + age;
System.out.println(result);
return "success";
}
//请求参数名称,与接收参数的 JavaBean 类的字段名称相同的话,
//则 SpringMvc 会根据请求参数自动给 JavaBean 实例化的对象属性赋值
//http://localhost:8080/request3?name=jobs
//http://localhost:8080/request3?name=jobs&age=33
@RequestMapping("/request3")
public String request3(Employee emp) {
String result = "name=" + emp.getName() + ",age=" + emp.getAge();
System.out.println(result);
return "success";
}
//如果接收参数中,普通参数名称 与 JavaBean 类的字段名称相同的话,则都会进行赋值
//http://localhost:8080/request4?name=jobs&age=33
@RequestMapping("/request4")
public String request4(Employee emp, String name, Integer age) {
StringBuilder sb = new StringBuilder();
sb.append("使用 JavaBean 类作为参数,获取的结果:");
sb.append("name=" + emp.getName() + ",age=" + emp.getAge());
sb.append("\r\n");
sb.append("使用普通参数获取的结果:");
sb.append("name=" + name + ",age=" + age);
String result = sb.toString();
System.out.println(result);
return "success";
}
//使用数组作为参数,接收传入的数组数据
//http://localhost:8080/request5?hobby=唱歌&hobby=跳舞&hobby=打游戏
@RequestMapping("/request5")
public String request5(String[] hobby) {
if (hobby != null && hobby.length > 0) {
for (String h : hobby) {
System.out.println(h);
}
} else {
System.out.println("未获取到所传入的 hobby 数据");
}
return "success";
}
//使用集合作为参数,接收传入的数组数据,无法直接获取数组数据
//必须要使用 @RequestParam 注解将请求参数与接收参数进行名称映射
//http://localhost:8080/request6?hobby=唱歌&hobby=跳舞&hobby=打游戏
@RequestMapping("/request6")
public String request6(
@RequestParam(value = "hobby") List<String> hobbylist) {
if (hobbylist != null && hobbylist.size() > 0) {
for (String hobby : hobbylist) {
System.out.println(hobby);
}
} else {
System.out.println("未获取到所传入的 hobby 数据");
}
return "success";
}
//使用 JavaBean 类作为接收参数,接收数组数据
//http://localhost:8080/request7?hobby=唱歌&hobby=跳舞&hobby=打游戏
@RequestMapping("/request7")
public String request7(Employee emp) {
List<String> hobbylist = emp.getHobby();
if (hobbylist != null && hobbylist.size() > 0) {
for (String hobby : hobbylist) {
System.out.println(hobby);
}
} else {
System.out.println("未获取到所传入的 hobby 数据");
}
return "success";
}
//发来的请求数据,如果是日期类型,如果想直接使用日期类型的参数进行接收的话,
//可以根据传入数据的日期格式,明确指定好接收数据的对应格式,就可以接收到具体的日期数据
//http://localhost:8080/request8?date=2022-01-21 15:20:30
@RequestMapping("/request8")
public String request8(@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date date) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日HH时mm分ss秒");
String dateString = sdf.format(date);
System.out.println(dateString);
return "success";
}
//可以接收提交过来的 body 数据,必须使用 Post 请求方式
//默认情况下,各种请求方式都支持,可以通过 method 指定允许的请求方式
//这里可以使用 PostMan 来请求该接口,必须使用 Post 请求方式从 body 中获取数据
//http://localhost:8080/request9
@RequestMapping(value = "/request9", method = RequestMethod.POST)
public String request9(@RequestBody String json) {
System.out.println(json);
return "success";
}
}
上面的请求方式代码中,最后一个请求方法,从 body 中获取数据,需要采用 Post 请求才可以。这里可以使用 PostMan 工具进行测试,在 PostMan 工具中可以 Post 发送各种类型的数据,这里仅仅以 Post 发送 Json 数据为例,因为在实际开发中,向服务器发送 Json 格式的数据比较常用,使用 PostMan 工具请求最后一个接口的细节如下所示:
在 IDEA 工具的控制台上打印出 Post 请求发送过来的数据:
到此为止,SpringMvc 采用纯注解方式快速搭建已经介绍完毕。在 SpringMvc 的纯注解配置中实现了监控所有请求,并忽略和放行常见静态资源的请求,通过配置前缀和后缀,实现通过返回的字符串自动查找对应路径下的 jsp 页面。最后列出了常用的接收传入的参数值的几种方式。
本篇博客的 demo 源代码下载地址为:https://files.cnblogs.com/files/blogs/699532/SpringMVC_Request.zip