SpringMVC

参考

官方文档:https://docs.spring.io/spring-framework/docs/5.3.5-SNAPSHOT/reference/html/web.html#spring-web

其他:https://blog.csdn.net/litianxiang_kaola/article/details/79169148

【狂神说Java】SpringMVC最新教程IDEA版通俗易懂

配置

继承 Controller方式

注解方式

web.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?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">

<!-- 配置 DispatcherServlet:这是 SpringMVC 的核心: 请求分发器,前端控制器 -->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-servlet.xml</param-value>
</init-param>
<!-- 启动级别 -->
<load-on-startup>1</load-on-startup>
</servlet>

<!-- /:匹配所有请求 -->
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>

spring-servlet.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
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
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">

<!--Spring扫描机制,此处扫描SpringMVC控制器-->
<context:component-scan base-package="com.wnh.controller"/>

<!-- 配置静态资源映射,防止静态资源被拦截 -->
<mvc:default-servlet-handler/>

<!--&lt;!&ndash; 处理器映射器 &ndash;&gt;-->
<!--<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>-->
<!--&lt;!&ndash; 处理器适配器 &ndash;&gt;-->
<!--<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>-->
<!-- 替代了上面注释的配置 -->
<mvc:annotation-driven/>

<!-- 视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
<property name="prefix" value="/WEB-INF/module/"/>
<property name="suffix" value=".jsp"/>
</bean>

</beans>

测试

1
2
3
4
5
6
7
8
9
@Controller
public class HelloController{

@RequestMapping("/hello")
public String hello(Model model){
model.addAttribute("msg","helloSpringMVC");
return "test";
}
}

乱码问题

web.xml

1
2
3
4
5
6
7
8
9
10
11
12
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<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>

@RequestMapping

RequestMapping是一个用来处理请求地址映射的注解,可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。

RequestMapping注解有六个属性,下面我们把这些属性分成三类进行说明。

1、value属性、method属性

value:指定请求的实际地址,指定的地址可以是URI Template模式

method:指定请求的method类型,例如GET、POST、PUT、DELETE等;

2、consumes属性、produces属性

consumes:指定处理请求的提交内容类型(Content-Type),例如application/json、text/html等

produces:指定返回的内容类型,仅当Request请求头中的(Accept)类型中包含该指定类型才返回

3、params属性、headers属性

params:指定request中必须包含某些参数值是,才让该方法处理

headers:指定request中必须包含某些指定的header值,才能让该方法处理请求

RestFul风格

1
2
3
4
5
6
7
8
9
10
@RequestMapping("/add")
public String add(int a,int b,Model model){
model.addAttribute("msg",a+b+"的结果为"+(a+b));
return "test";
}
@RequestMapping("/add/{a}/{b}")
public String add0(@PathVariable int a,@PathVariable int b, Model model){
model.addAttribute("msg","结果为"+(a+b));
return "test";
}

原本url:http://localhost:8080/springmvc/add?a=1&b=2

RestFul:http://localhost:8080/springmvc/add/3/4

@RequestParam

接受前端传递的参数,只有符合@RequestParam()配置才接收,若是对象可直接传入

1
2
3
4
5
@RequestMapping("/t1")
public String test1(@RequestParam("username") String name) {
System.out.println(name);
return "test";
}

转发与重定向

配置视图解析器默认转发

  • forward: //请求转发
  • redirect: //重定向

重定向

1
2
3
4
@RequestMapping("redirect")
public String redirect(){
return "redirect:must.jsp";
}

AJAX 与 JSON

jackson

导包

1
2
3
4
5
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.10.0</version>
</dependency>

实体类对象

Controller

1
2
3
4
5
6
7
8
9
10
11
12

```java
@RequestMapping(value = "j1",produces = "text/json;charset=UTF-8")
@ResponseBody
public String json1() throws JsonProcessingException {
// jackson ObjectMapper
ObjectMapper mapper=new ObjectMapper();

User user=new User(1,"小王","dbfd");

return mapper.writeValueAsString(user);
}

SpringMVC 配置解决

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<constructor-arg value="UTF-8"/>
</bean>
<bean id="jacksonMessageConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper">
<bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
<property name="failOnEmptyBeans" value="false"/>
</bean>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>

结果

{"id":1,"name":"小王","pwd":"dbfd"}

简单集合

Controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@RequestMapping(value = "j1")
@ResponseBody
public String json1() throws JsonProcessingException {
// jackson ObjectMapper
ObjectMapper mapper=new ObjectMapper();
List<User> userList=new ArrayList<>();

User user1=new User(1,"小王","dbfd");
User user2=new User(2,"大王","55vgh");
User user3=new User(3,"老王","d785d");
User user4=new User(4,"少王","86fd");

userList.add(user1);
userList.add(user2);
userList.add(user3);
userList.add(user4);

return mapper.writeValueAsString(userList);
}

结果

[{"id":1,"name":"小王","pwd":"dbfd"},{"id":2,"name":"大王","pwd":"55vgh"},{"id":3,"name":"老王","pwd":"d785d"},{"id":4,"name":"少王","pwd":"86fd"}]

时间对象

Controller

1
2
3
4
5
6
7
@RequestMapping(value = "j2")
@ResponseBody
public String json2() throws JsonProcessingException {
Date data=new Date();

return new ObjectMapper().writeValueAsString(data);
}

结果

1613656289365

ObjectMapper 时间解析默认格式为:Timetamp:时间戳

格式化

1
2
3
4
5
6
7
8
9
@RequestMapping(value = "j2")
@ResponseBody
public String json2() throws JsonProcessingException {
Date data=new Date();

SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

return new ObjectMapper().writeValueAsString(sdf.format(data));
}

结果

"2021-02-18 21:55:51"

ObjectMapper 解决

.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS,false) 关闭默认时间戳方式

.setDateFormat(sdf) 自定义时间格式

1
2
3
4
5
6
7
8
9
10
11
@RequestMapping(value = "j2")
@ResponseBody
public String json2() throws JsonProcessingException {
Date data=new Date();

// SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//
// return new ObjectMapper().writeValueAsString(sdf.format(data));

return new ObjectMapper().configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS,false).writeValueAsString(data);
}

结果

"2021-02-18T14:00:26.890+0000"

JsonUtil

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class JsonUtil {
public static String getJson(Object object) {
return getJson(object, "yyyy-MM-dd HH:mm:ss");
}

public static String getJson(Object object, String dataFormat) {
ObjectMapper mapper = new ObjectMapper();

// 关闭默认时间戳方式
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
// 自定义时间格式
SimpleDateFormat sdf = new SimpleDateFormat(dataFormat);
mapper.setDateFormat(sdf);

try {
return mapper.writeValueAsString(object);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return null;
}
}

fastjson

导包

1
2
3
4
5
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.1.41</version>
</dependency>

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
@RequestMapping(value = "j4")
@ResponseBody
public String json4() {
List<User> userList = new ArrayList<>();

User user1 = new User(1, "小王", "dbfd");
User user2 = new User(2, "大王", "55vgh");
User user3 = new User(3, "老王", "d785d");
User user4 = new User(4, "少王", "86fd");

userList.add(user1);
userList.add(user2);
userList.add(user3);
userList.add(user4);

System.out.println("*******Java对象 转 JSON字符*******");
String str1 = JSON.toJSONString(userList);
System.out.println("JSON.toJS0NString(list)==>" + str1);
String str2 = JSON.toJSONString(user1);
System.out.println("JSON.toJS0NString(user1)==>" + str2);

System.out.println("\n****** JSON字符串 转 Java对象*******");
User jp_user1 = JSON.parseObject(str2, User.class);
System.out.println("JSON. parseObject(str2,User.class)==>" + jp_user1);

System.out.println("\n****** Java对象 转 JSON对象*****");
JSONObject jsonObject1 = (JSONObject) JSON.toJSON(user2);
System.out.println("(JSONObject) JSON. toJSON(user2)==> " + jsonObject1.getString("name"));

System.out.println("\n****** JSON对象 转 Java对象*****");
User to_java_user = JSON.toJavaObject(jsonObject1, User.class);
System.out.println("JSON. toJavaObject(jsonObject1, User.class)==>" + to_java_user);
return "Hello";
}

结果

1
2
3
4
5
6
7
8
9
10
11
12
*******Java对象 转 JSON字符*******
JSON.toJS0NString(list)==>[{"id":1,"name":"小王","pwd":"dbfd"},{"id":2,"name":"大王","pwd":"55vgh"},{"id":3,"name":"老王","pwd":"d785d"},{"id":4,"name":"少王","pwd":"86fd"}]
JSON.toJS0NString(user1)==>{"id":1,"name":"小王","pwd":"dbfd"}

****** JSON字符串 转 Java对象*******
JSON. parseObject(str2,User.class)==>User(id=1, name=小王, pwd=dbfd)

****** Java对象 转 JSON对象*****
(JSONObject) JSON. toJSON(user2)==> 大王

****** JSON对象 转 Java对象*****
JSON. toJavaObject(jsonObject1, User.class)==>User(id=2, name=大王, pwd=55vgh)

实例

后端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@RequestMapping(value = "addComment", produces = "text/json;charset=UTF-8")
@ResponseBody
public String addComment(Comment comment) {
int result = frontService.AddComment(comment);
Map<String, Object> map = new HashMap<String, Object>();
if (result > 0) {
long LastId = comment.getComment_id();
map.put("result", "success");
map.put("cid", LastId);
System.out.println("评论成功");
} else {
map.put("result", "fail");
System.out.println("评论失败");
}
return JSON.toJSONString(map);
}

前端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
$(function () {
var css = {
marginLeft: 'auto',
marginRight: 'auto',
width: '70%',
height: 'auto',
};
$("#show").css(css);
$("#sendTo").click(function () {
//获取值
var uid = '${sessionScope.userS.userId}';
var uname = '${sessionScope.userS.userName}';
var nid = '${news.newsId}';
var content = $("#content").val();
var uIcon = '${sessionScope.userS.userIcon}';
if (content !== "") {
$.ajax({
url: "${pageContext.request.contextPath}/addComment",
type: "post",
//注意序列化的值一定要放在最前面,并且不需要头部变量,不然获取的值得格式会有问题
data: {user_id: uid, news_id: nid, content: content},
dataType: "json",
success: function (data) {
if (data.result === 'success') {
alert("评论成功");
var iconSrc="${pageContext.request.contextPath}/Static/img/userIcon.png"
if("" !== uIcon){
iconSrc=uIcon;
}
$("#content").val('');
var s = '<div class="layui-card" id="div_' + data.cid + '">' +
'<div class="layui-card-body">' +
'<div align="left">' +
'<a href="${pageContext.request.contextPath}/queryUserById?uid='+uid+'">' +
'<img class="layui-nav-img" src="' + iconSrc + '"/>' +
uname + '</a>' +
' </div>' +
content +
'<div align="right">' +
'<a href="javascript:void(0);" onclick="delComment(' + data.cid + ')">删除</a>' +
'</div>' +
'<div align="right">' +
getCurrentDate(new Date()) +
'</div>' +
'</div>' +
'</div>';
$("#newComment").prepend(s);
} else {
alert("评论失败");
}
}
})
} else {
alert("评论不能为空!");
}
});
});

拦截器

Spring MVC中的拦截器(Interceptor)类似于Servlet中的过滤器(Filter),它主要用于拦截用户请求并作相应的处理。例如通过拦截器可以进行权限验证、记录请求信息的日志、判断用户是否登录等。 要使用Spring MVC中的拦截器,就需要对拦截器类进行定义和配置。

通过实现HandlerInterceptor接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class MyInterceptor implements HandlerInterceptor {

// return true 放行
// return false 拦截

@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");
}
}

自定义拦截器实现了HandlerInterceptor接口,并实现了接口中的三个方法:

  • preHandle() 方法:该方法会在控制器方法前执行,其返回值表示是否中断后续操作。当其返回值为true时,表示继续向下执行; 当其返回值为false时,会中断后续的所有操作(包括调用下一个拦截器和控制器类中的方法执行等)。
  • postHandle()方法:该方法会在控制器方法调用之后,且解析视图之前执行。可以通过此方法对请求域中的模型和视图做出进一步的修改。
  • afterCompletion()方法:该方法会在整个请求完成,即视图渲染结束之后执行。可以通过此方法实现一些资源清理、记录日志信息等工作。

配置

1
2
3
4
5
6
7
<mvc:interceptors>
<mvc:interceptor>
<!-- 配置拦截器作用的路径 -->
<mvc:mapping path="/**"/>
<bean class="com.wnh.interceptor.MyInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>

path 的属性值“/**” 表示拦截所有路径,“/hello” 表示拦截所有以 “/hello” 结尾的路径。

注意:中的子元素必须按照上述代码中的配置顺序进行编写,即 ,否则文件会报错。

文件上传下载

上传

导包

1
2
3
4
5
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>

配置

1
2
3
4
5
6
7
8
<!-- 文件上传 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 编码格式与 JSP 的 PageEncoding 属性一致 默认为 ISO-8859-1 -->
<property name="defaultEncoding" value="utf-8"/>
<!-- 上传文件的最大上限,单位为字节 -->
<property name="maxUploadSizePerFile" value="5242880"/><!--5M-->
<property name="maxInMemorySize" value="40960"/>
</bean>

前端

1
2
3
4
5
<h3>文件上传</h3>
<form action="${pageContext.request.contextPath}/upload" enctype="multipart/form-data" method="post" >
<input type="file" name="file" />
<input type="submit" value="上传">
</form>

后端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
@RequestMapping("upload")
public String upload(@RequestParam("file") MultipartFile file, HttpServletRequest request) throws IOException {
if (file.isEmpty()) {
return "failed";
}
// 获取原文件名
String fileName = file.getOriginalFilename();
System.out.println("上传文件名:"+fileName);

// 获取上传的目录路径
String path = request.getSession().getServletContext().getRealPath("/upload");

File filePath = new File(path);
if (!filePath.exists()){
// 创建目录或子目录
filePath.mkdirs();
}
System.out.println("上传保存地址:"+filePath);

// InputStream in = file.getInputStream() ;
//
// OutputStream out = new FileOutputStream(new File(filePath,fileName)) ;
//
// byte[] bs = new byte[1024];
// int len = -1;
// while(( len = in.read(bs)) !=-1 ) {
// out.write(bs, 0, len);
// }
// out.close();
// in.close();

file.transferTo(new File(filePath+"/"+fileName));

return "success";
}

下载

前端

1
2
<h3>文件下载</h3>
<a href="${pageContext.request.contextPath}/download">下载</a>

后端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@RequestMapping("download")
public String download(HttpServletRequest request, HttpServletResponse response) throws IOException {
String path = request.getServletContext().getRealPath("/upload");
String fileName="wnhyang.jpg";
//1、设置response 响应头
response.reset();
response.setCharacterEncoding("UTF-8");
response.setContentType("multipart/form-data");
response.setHeader("Content-Disposition",
"attachment;fileName="+ URLEncoder.encode(fileName, "UTF-8"));

File file = new File(path,fileName);
//2、 读取文件--输入流
InputStream in=new FileInputStream(file);
//3、 写出文件--输出流
OutputStream out = response.getOutputStream();

byte[] bs = new byte[1024];
int len = -1;
while(( len = in.read(bs)) !=-1 ) {
out.write(bs, 0, len);
}
out.close();
in.close();

return null;
}

注意

将之前依赖改了,因为只有 servlet-api 3以上才有 getServletContext()方法

1
2
3
4
5
6
7
8
9
10
11
12
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.2.1</version>
<scope>provided</scope>
</dependency>