SpringMVC

杭州下起了小雪,但是也阻止不了我写博客总结技术的步伐。

什么是SpringMVC

SpringMVC是一个强大的Web框架,它支持注解配置,易用性高。Spring MVC 是当前应用最多的 MVC 框架,而且在很多公司,通常会把 Spring MVC 和 Mybatis 整合起来使用。

框架原理和执行流程

在Spring MVC框架中,从“Request(请求)”开始,依次进入“DispatcherServlet(核心分发器)” —> “HandlerMapping(处理器映射)” —> “Controller(控制器)” —> “ModelAndView(模型和视图)” —> “ViewResolver(视图解析器)” —> “View(视图)” —> “Response(响应)”结束,其中DispatcherServlet、HandlerMapping和ViewResolver 只需要在XML文件中配置即可,从而大大提高了开发的效率,特别是对于 HandlerMapping 框架为其提供了默认的配置。Spring MVC 框架的结构图如下所示:
SpringMVC执行过程
相关接口解释:
(1) DispatcherServlet
前端控制器,所有的请求都有经过它来统一分发,请求会被分发给对应的Handler。
(2) HandlerMapping(处理器映射器)
解析请求连接,然后根据请求连接找到执行这个请求的类(Controller)。可以通过注解或在xml文件中配置。
(3) Controller
Controller 将处理用户请求,Controller 处理完用户请求,则返回 ModelAndView 对象给DispatcherServlet 前端控制器。
(4) ViewResolver(视图解析器)
解析 MdoelAndView,将 MdoelAndView 中的逻辑视图名变为一个真正的 View 对象,并将 MdoelAndView 中的 Model 取出。

从宏观角度考虑,DispatcherServlet 是整个 Web 应用的控制器;从微观考虑,Controller 是单个 Http 请求处理过程中的控制器。

搭建SpringMVC框架

pom.xml配置依赖的jar包

配置jar包依赖:

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
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.ali</groupId>
<artifactId>SpringMVCBlog</artifactId>
<packaging>war</packaging>
<version>0.0.1-SNAPSHOT</version>
<name>SpringMVCBlog Maven Webapp</name>
<url>http://maven.apache.org</url>
<properties>
<spring.version>5.0.7.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>

<!-- spring base jar -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>

<!-- spring mvc jar -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>

<!-- jackson jar -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.7</version>
</dependency>
</dependencies>
<build>
<finalName>SpringMVCBlog</finalName>
</build>
</project>

配置web.xml

在web.xml中主要需要配置一下DispatcherServlet类

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
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
<display-name>Archetype Created Web Application</display-name>

<!-- 配置 Spring MVC DispatchcerServlet 前端控制器 -->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<!-- contextConfigLocation 是参数名称,该参数的值包含 Spring MVC 的配置文件路径 -->
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/springmvc-config.xml</param-value>
</init-param>
<!-- 在 Web 应用启动时立即加载 Servlet -->
<load-on-startup>1</load-on-startup>
</servlet>
<!-- Servlet 映射声明 -->
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<!-- 监听当前域的所有请求 -->
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- 添加 register.jsp 为首页 -->
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>

</web-app>

添加springmvc-config.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
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd">
<!--
这个标签会自动配置两个bean,分别是DefaultAnnotatationHandlerMapping和
AnnotataionMethodHanderAdapter. 根据请求查找映射,AnnotationMethodHandlerAdapter
完成对控制器类的 @RequestMapping 标注方法(下面会介绍)的调用。
-->
<mvc:annotation-driven/>

<!-- 开启spring注解 -->
<context:annotation-config />

<!-- 配置自动扫描的包,完成 Bean 的创建和自动依赖注入的功能 -->
<context:component-scan base-package="com.haozai.controller" />

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

</beans>

配置的视图解析器 InternalResourceViewResolver 用来解析视图,将 View 呈现给用户。视图解析器中配置的 prefix表示视图的前缀, suffix表示视图的后缀。

实体类

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
package com.haozai.entity;

import java.io.Serializable;

/**
* 用户名和密码
* @author haozai
* @date 2018-12-30 21:50:43 新建
*/
public class User implements Serializable{

/**
* 用户名
*/
private String username;

/**
* 密码
*/
private String password;

public User() {
super();
// TODO Auto-generated constructor stub
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User [username=" + username + ", password=" + password + "]";
}

}

Controller控制器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.haozai.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import com.haozai.entity.User;

@Controller
public class TestController {

@RequestMapping("/test")
public ModelAndView test1(User user) {
//index会通过视图解析器,解析成url:/WEB-INF/views/test.jsp
ModelAndView mav = new ModelAndView("test");
//返回数据给前端
mav.addObject("username", user.getUsername());
mav.addObject("password", user.getPassword());
return mav;
}
}

@Controller注解表名这个类是一个Controller。
@RequestMapping注解,配置外部的访问路径。

jsp中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%>
<%@page isELIgnored="false" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link rel="stylesheet" type="text/css"
href="https://cdn.jsdelivr.net/semantic-ui/2.2.4/semantic.min.css">

<title>权限管理</title>
</head>
<body>
账号:${username}<br/>
密码:${password }
</body>
</html>

因为配置的Servlet是2.3版本的,需要设置<%@page isELIgnored=”false” %>标签,,2.4版本或者更新的版本,isELIgnored默认值为false,而2.3或者 更早的版本isELIgnored默认值为true。jsp中EL表达式不起作用的问题

测试

在浏览器中直接输入url

结果

可看到页面中成功拿到了值:

异步请求

在使用AJAX异步请求的时候,Controller中并不需要返回ModelAndView,一般只需要返回Json格式的数据即可。
异步请求和同步请求的流程有如下的区别:

异步请求的时候,发送请求Request到DispatcherServlet然后DispatcherServlet通过HandlerMapping找到Controller控制器中的方法,然后再通过Response返回Json格式的数据即可。所以在被@RequestMapping注解过的方法要被异步访问的时候,需要做一些修改,不再是返回ModelAndView而是可以直接返回字符串或者集合等数据类型。例如:

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
package com.haozai.controller;

import java.util.logging.Logger;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import com.haozai.entity.User;

@Controller
public class TestController2 {
private Logger log = Logger.getLogger("com.haozai.controller");

@RequestMapping(value="/ajax")
@ResponseBody
public String test1(User user) {
log.info("访问controller. url=/test,user="+user);
//index会通过视图解析器,解析成url:/WEB-INF/views/test.jsp
//返回数据给前端
return "username:"+user.getUsername()+" password:"+user.getPassword();
}
}

大家可以自行用ajax测试一下,或者直接在浏览器输入地址和参数。
需要注意的是,如果打算使返回的值是Json格式的话,需要在方法上加ResponseBody注解。
并且不止可以返回String类型的数据,还可以返回List、Map等集合,框架底层会自动封装成Json格式的数据。

-------------本文结束感谢您的阅读-------------