【SpringMVC】(一)
SpringMVC简介
SpringMVC是Spring的一个后续产品,是Spring的一个子项目
基于原生的Servlet,通过了功能强大的DispatcherServlet,对请求和响应进行统一处理
什么是MVC
MVC是一种架构思想,将软件按照模型、视图、控制器来划分
M:Modedl,模型层,指工程中的JavaBean,作用是处理数据
JavaBean分为两类:
· 一类是实体类Bean,专门存储业务数据
· 一类是业务处理Bean,指Service和Dao对象,专门处理业务逻辑和数据库访问
V:View,视图层,指工程中的html和jsp页面,作用是与用户交互,展示数据
C:Controller,控制层,指工程中的servlet,作用是接收请求和响应浏览器
MVC的工作流程
用户通过视图层发送数据请求到服务器,在服务器中请求被Controller接收,Controller调用Model层处理请求,处理完毕将结果返回给Controller,Controller再根据请求处理的结果找到相应的View视图层,对数据进行渲染相应给浏览器。
开发环境
IDE:idea 2019.2
构建工具:maven3.5.4
服务器:tomcat7
Spring版本:5.3.1
创建Maven工程
①添加web模块
②设置打包方式为war
<packaging>war</packaging>
③引入依赖
<dependencies>
<!-- SpringMVC -->
<dependency>
<roupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.1</version>
</dependency>
<!-- 日志 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<!-- ServletAPI -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!-- Spring5和Thymeleaf整合包 -->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
<version>3.0.12.RELEASE</version>
</dependency>
</dependencies>
导入依赖时会同时导入依赖所需要的依赖,如导入spring-webmvc时就导入了Spring所需的aop、beans、context等
scope provided /scope 标签表示服务器已经提供了该依赖,打包时不必再注入
配置web.xml
默认请求方式
<!--配置SpringMVC前端控制器,对浏览器发送的请求进行统一处理 -->
<servlet>
<servlet-name>SpringMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>SpringMVC</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
对名为SpringMVC的servlet进行注册,servlet和servlet-mapping的servlet-name的名称必须相同
匹配路径设置为SpringMVC的核心控制器能够处理的请求的请求路径
/表示除了.jsp之外的请求,可以匹配如/login、.html、css方式的请求路径,/*则表示包括.jsp的所有请求
为什么不能匹配jsp?
jsp被认为是一种特殊的servlet,需要服务器指定的servlet进行处理,因此.jsp结尾的请求路径不需要DispatcherServlet处理,否则SpringMVC将.jsp请求当做普通请求处理而不会找到相应的jsp页面
扩展配置方式
Maven要求配置文件都要在resource下,因此通过init-param(初始化前端控制器属性)设置SpringMVC配置文件(contextConfigLocation,上下文配置路径)的位置(value,对应resource的类路径)和名称(name),通过load-on-startup标签设置前端控制器DispatcherServlet的启动时间为服务器启动时(Servlet生命周期默认为第一次访问时才启动)
<!--配置SpringMVC前端控制器,对浏览器发送的请求进行统一处理 -->
<servlet>
<servlet-name>SpringMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--配置SpringMVC配置文件的位置和名称 -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:SpringMVC.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
创建SpringMVC.xml配置文件
开启注解扫描
<context:component-scan base-package="com.hikaru.mvc.controller"></context:component-scan>
@Controller
public class HelloController {
}
这样控制器就能被IOC识别并注入
配置Thymeleaf视图解析器
<!-- 配置Thymeleaf视图解析器 -->
<bean id="viewResolver" class="org.thymeleaf.spring5.view.ThymeleafViewResolver">
<property name="order" value="1"/>
<property name="characterEncoding" value="UTF-8"/>
<property name="templateEngine">
<bean class="org.thymeleaf.spring5.SpringTemplateEngine">
<property name="templateResolver">
<bean class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver">
<!-- 视图前缀 -->
<property name="prefix" value="/WEB-INF/templates/"/>
<!-- 视图后缀 -->
<property name="suffix" value=".html"/>
<property name="templateMode" value="HTML5"/>
<property name="characterEncoding" value="UTF-8" />
</bean>
</property>
</bean>
</property>
</bean>
Order设置视图解析器的优先级
templateEngine为当前模板,其内部Bean表示的是当前解析视图的一种策略。视图名称加上视图前缀和视图后缀就可以得到要跳转的页面。
每当页面发生跳转的时候,视图名称符合当前条件,就会被视图解析器解析,得到相应的页面
对控制器设置请求映射注解 @RequestMapping
将当前的请求和控制器方法创建映射关系,不仅可以通过请求路径,而且可以通过请求方式、请求参数、请求报文等进行匹配
@Controller
public class HelloController {
@RequestMapping("/")
public String getIndex() {
return "index";
}
}
这里省略了value="/"
WEB-INFO下的页面浏览器是不能够访问的,也不能够通过重定向只能通过转发的方式
不允许多个控制器对同一个上下文地址进行映射
由于引入的日志依赖,控制台输出的日志信息:
13:53:35.733 [http-nio-8080-exec-1] DEBUG org.thymeleaf.TemplateEngine - [THYMELEAF] TEMPLATE ENGINE INITIALIZED
13:53:35.858 [http-nio-8080-exec-1] DEBUG org.springframework.web.servlet.DispatcherServlet - Completed 200 OK
13:53:36.105 [http-nio-8080-exec-4] DEBUG org.springframework.web.servlet.DispatcherServlet - GET "/springMVC/", parameters={}
13:53:36.106 [http-nio-8080-exec-4] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping - Mapped to com.hikaru.mvc.controller.HelloController#getIndex()
13:53:36.111 [http-nio-8080-exec-4] DEBUG org.springframework.web.servlet.DispatcherServlet - Completed 200 OK
可以看到
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping - Mapped to com.hikaru.mvc.controller.HelloController#getIndex()
这里发生的映射关系:访问请求Mapped to控制器方法,且与注解表示的方法有关而与方法名无关
@RequestMapping注解位置
为了解决多个控制器对应同一个请求的情况,需要在控制器类上和方法上各添加@RequestMapping注解
@Controller
public class TestController {
@RequestMapping("/")
public String getIndex() {
return "index";
}
}
@Controller
public class TestController1 {
@RequestMapping("/")
public String getIndex1() {
return "index";
}
}
如上两个控制器对应同一个访问请求/,服务器日志信息:
Caused by: java.lang.IllegalStateException: Ambiguous mapping. Cannot map 'testController1' method
com.hikaru.controller.TestController1#getIndex1()
to { [/]}: There is already 'testController' bean method
@RequestMapping标识一个类:设置映射请求的请求路径的初始信息
@RequestMapping标识一个方法:设置映射请求的请求路径的具体信息
在控制器TestController类上添加注解,则请求路径/会匹配控制器TestController1的控制器方法。
@Controller
@RequestMapping("/test")
public class TestController {
@RequestMapping("/")
public String getIndex() {
return "index";
}
}
或者在浏览器输入上下文路径(http://localhost:8080/springMVC/test/) 也能够通过控制器一匹配控制器方法/
@RequestMapping的属性
1 value
value值可以为数组,接收多个请求路径,value属性必不可少
2 method
method属性为请求的方式(get或post)
get方式的请求参数以 ?请求参数名=请求参数值&... 的方式拼接在地址后面,文件上传不能使用get方式
post方式请求参数放在请求体中,相较于post安全、数据量比较大但速度较慢
不写method属性则表明没有请求方式限制
@RequestMapping(
value = {"/success"},
method = {RequestMethod.GET}
)
public String success() {
return "success";
}
其中RequestMethod为枚举类,包含所有的请求方式
public enum RequestMethod {
GET,
HEAD,
POST,
PUT,
PATCH,
DELETE,
OPTIONS,
TRACE;
private RequestMethod() {
}
}
<a th:href="@{/test/success}">SUCCESS</a>
<form th:action="@{/test/success}" method="post">
<button type="submit">SUCCESS</button>
</form>
这种情况下,只有超链接get方式能够匹配请求
表单提交的post方式满足映射value属性,但是不满足method属性
3 params(了解)
@ RequestMapping注解的params属性通过请求的请求参数匹配请求映射,params属性是一个字符串数组,要求请求参数必须满足数组中的全部要求才能匹配成功,可以通过四种表达式设置请求参数和请求映射的匹配关系。
①"param":要求匹配映射所匹配的请求必须携带param请求参数
②"!param":要求匹配映射所匹配的请求不能携带param请求参数
③"param=value":要求匹配映射所匹配的请求必须携带值为value的param请求参数
④"param!=value":要求匹配映射所匹配的请求必须携带param请求参数但是不能为value
@RequestMapping(
value = {"/paramsAndHeaderTest"},
params = {"username=admin"}
)
public String paramsAndHeaderTest() {
return "success";
}
<a th:href="@{/test/paramsAndHeaderTest(username='admin')}">paramsAndHeaderTest</a>
thymeleaf会将上述解析为:/test/paramsAndHeaderTest?username=admin,也可以直接这样写,但是会报错
HTTP 400:请求参数不匹配
4 headers
@RequestMapping(
value = {"/paramsAndHeaderTest"},
params = {"username=admin"},
headers = {"Host=localhost:8080"}
)
public String paramsAndHeaderTest() {
return "success";
}
请求头参数为一个字符串键值对数组,请求头不匹配的话服务器会报HTTP404
@RequestMapping的常用的派生注解
使用派生注解可以不用声明method请求方式
@GetMapping | |
@PostMapping | |
@PutMapping | |
@DeleteMapping |
但是目前浏览器只支持post和get方式,表单提交时,若使用put、delete则会按照默认请求方式get处理
@PostMapping(
value = {"/success"}
)
public String success() {
return "success";
}
get方式超链接会出现http405错误
SpringMVC支持ant风格的路径(模糊匹配)
?: 表示任意的单个字符
*: 表示任意的0个或多个字符
**: 表示任意的一层或多层目录
在使用**时,只能使用/ **/xxx的形式,否则会被当做多个字符
@RequestMapping(
value = {"/**/*"},
params = {"username=admin"},
headers = {"Host=localhost:8080"}
)
public String paramsAndHeaderTest() {
return "success";
}
<a th:href= "@{/test/qweqweqwewq/eqweqweqwe(username='admin')}">paramsAndHeaderTest</a>
SpringMVC支持路径中的占位符
原始方式:/deleteUser?id=1
rest方式:/deleteUser/1
SpringMVC路径占位符常用于restful风格中,当请求路径中的某些参数通过路径的方式传输到服务器中,就可以在相应的@ResquestMapping属性中通过占位符{xxx}表示传输过来的数据,再通过@PathVariable注解,将占位符表示的数据赋值给控制器方法的形参。
@RequestMapping(
value = {"/*/{username}"},
headers = {"Host=localhost:8080"}
)
public String paramsAndHeaderTest(@PathVariable("username")String username) {
System.out.println(username);
return "success";
}
<a th:href= "@{/test/1/admin}">paramsAndHeaderTest</a>
通过thymeleaf视图解析器访问指定的页面th:href="@{/target}"th:href="@{/target}"th:href="@{/target}"
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>首页</h1>
<a th:href="@{/target}">target.html</a>
</body>
</html>
thymeleaf会解析a标签的上下文路径(或者使用 /springMVC/target作为上下文路径 ,但是上下文可以修改耦合性不好,否则普通超链接地址只有浏览器或服务器解析的绝对路径),将请求发送给控制器,经请求映射的控制器方法得到返回值后再加上视图前缀和视图后缀,返回完整的页面地址
@Controller
public class HelloController {
@RequestMapping("/")
public String getIndex() {
return "index";
}
@RequestMapping("/target")
public String toTarget() {
return "target";
}
}
总结
浏览器发送请求,若请求符合前段控制器的url-pattern,该请求就会被前端控制器DispatcherServlet处理,前端控制器会读取SpringMVC核心配置文件(resource中的SpringMVC.xml)通过扫描组件找到控制器,将请求地址和控制器方法的@RequestMapping的value值进行匹配,如果匹配成功则说明该控制器方法是处理请求的方法。处理请求的方法需要返回一个字符串类型的视图名称,该视图名称会被视图解析器解析,加上视图前缀、后缀组成视图的路径,通过ThymeLeaf对视图进行渲染,最终转发到视图对应的页面。
SpringMVC获取请求参数
1 通过原生ServletAPI获取
<a th:href="@{/testServletAPI(username='admin', password=123456)}">get_param</a>
<form th:action="@{/testServletAPI}" method="post">
<input type="text" name="username">
<input type="password" name="password">
<input type="submit">
</form>
@RequestMapping("/testServletAPI")
public String testServletAPI(HttpServletRequest request) {
String username = request.getParameter("username");
String password = request.getParameter("password");
System.out.println("username:" + username + ",password:" + password);
return "success";
}
浏览器发送的请求会被RequestMapping注解所匹配,匹配成功后会由在web.xml注册的前端控制器DispatcherServlet调用匹配控制器的控制器方法,并且将前端控制器得到的数据注入控制器方法的参数中
2 通过控制器方法形参获取
<form th:action="@{/testServletAPI}" method="post">
<input type="text" name="username">
<input type="password" name="password">
<input type="checkbox" name="hobby" value="a">a<br/>
<input type="checkbox" name="hobby" value="b">b<br/>
<input type="checkbox" name="hobby" value="c">c<br/>
<input type="submit">
</form>
@RequestMapping("/testServletAPI")
public String testServletAPI(String username, String password, String[] hobby) {
System.out.println("username:" + username + ",password:" + password);
System.out.println(Arrays.toString(hobby));
return "success";
}
多个同名请求参数的情况可以使用一个String数组接收
或者使用一个String类型形参接收,这样里面的请求参数会用,分割组成一个String
@RequestParam注解设置控制器方法的形参
@RequestMapping("/testServletAPI")
public String testServletAPI(
@RequestParam(value = "user_name", required = false, defaultValue = "tod4") String username,
String password,
String[] hobby) {
System.out.println("username:" + username + ",password:" + password);
System.out.println(Arrays.toString(hobby));
return "success";
}
<form th:action="@{/testServletAPI}" method="post">
<input type="text" name="user_name">
<input type="password" name="password">
<input type="checkbox" name="hobby" value="a">a<br/>
<input type="checkbox" name="hobby" value="b">b<br/>
<input type="checkbox" name="hobby" value="c">c<br/>
<input type="submit">
</form>
value属性:value值对应请求的请求参数名,防止请求参数名和控制器方法名不一致导致服务器报错400参数不一致
required属性:默认为true,表示请求必须含有此请求参数,否则服务器报错400
defaultValue属性:无论required值true或false,也无论请求有没有此请求参数或者请求参数为空都为其赋初始值
@RequestHeader
@ RequestHeader 将请求头信息和构造器方法形参之间创建映射关系
@RequestMapping("/param")
public String param() {
return "test_param";
}
@RequestMapping("/testServletAPI")
public String testServletAPI(
@RequestParam(value = "user_name", required = false, defaultValue = "tod4") String username,
String password,
String[] hobby,
@RequestHeader(value="host123", required = true, defaultValue = "11111111") String host) {
System.out.println("username:" + username + ",password:" + password);
System.out.println(Arrays.toString(hobby));
System.out.println("host:" + host);
return "success";
}
@ RequestHeader 也有三种属性value、defaultValue、required,并且与RequestParam注解相同
@CookieValue创建Cookie和控制器方法参数之间的映射关系
控制器方法的getSession执行时会先查看请求报文中上是否含有JSESSIONID的Cookie,如果没有的话说明会话使第一次,会创建Session对象并将其放在服务器所维护的Map集合中,并创建一个键值对为JSESSIONID和一个随机序列的的Cookie作为响应报文返回给客户端。
Cookie存储于客户端(浏览器端),Session存储于服务器端所维护的Map集合中
@RequestMapping("/testServletAPI")
public String testServletAPI(HttpServletRequest request) {
HttpSession session = request.getSession();
return "success";
}

第一次会话服务器端没有检测到含有JSESSIONID的Cookie,便会创建Session并返回一个含有JSESSIONID和随机序列的Cookie,以后每次客户端再发送请求都会再发送这个JSESSIONID的Cookie

后面的请求的响应报文就不再有JSESSIONID的Cookie了
@RequestMapping("/testCookie")
public String testCookie(@RequestParam("user_name")String username,
@RequestParam("password")String password,
@RequestParam("hobby")String[] hobby,
@CookieValue("JSESSIONID")String cookie) {
System.out.println("username:" + username + ",password" + password);
System.out.println("hobby:" + Arrays.toString(hobby));
System.out.println("JSESSION:" + cookie);
return "success";
}
@ CookieValue也有三种属性value、defaultValue、required,并且与RequestParam注解相同
通过POJO获取参数
可以把控制器方法的参数设置为一个实体类型的形参,此时若浏览器传输的请求参数与实体类中的属性名一致,那么请求参数就会为此属性赋值
@RequestMapping("/testCookie")
public String testCookie(User user,
@CookieValue("JSESSIONID")String cookie) {
System.out.println(user.toString());
System.out.println("JSESSION:" + cookie);
return "success";
}
<form th:action="@{/testCookie}" method="post" style="border: black 1px">
<input type="text" name="username">
<input type="password" name="password">
<input type="checkbox" name="hobby" value="a">a<br/>
<input type="checkbox" name="hobby" value="b">b<br/>
<input type="checkbox" name="hobby" value="c">c<br/>
<input type="submit">
</form>
框架一般使用反射来创建实体类对象,反射使用的是实体类的无参构造器,这里要么不写构造器默认无参构造器,要么有参无参都要声明,不能只写有参构造器
CharacterEncodingFilter编码过滤器处理获取请求参数乱码的问题
1 get请求方式乱码:
get请求由于是把参数加到URL,所以乱码是由Tomcat对URL解析编码不支持中文造成的,可以在conf下的service.xml中修改URLEncoding,但是Tomcat8之后默认为UTF-8也就不需要修改了。
2 post请求乱码
原生Servlet是通过设置HttpServletRequest编码的方式,但是SpringMVC是先在控制器方法的形参中获取请求参数和HttpServletRequest,再通过HttpServletRequest修改编码方式的话对于以已经获取到的请求参数是没有作用的,因此需要在前端控制器DIspatcherServlet调用控制器方法前修改编码方式。
服务器三大组件的执行的时间顺序是:ServletContextListener、Filter最后才是Servlet,监听器Listener只是监听作用,因此可以使用过滤器Filter,当请求地址满足过滤器地址都会被Filter过滤,经FIlter处理之后再交给DIspatcherServlet。
过滤器Filter作为服务器的组件,和Servlet一样需要在web.xml中注册:
<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>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
匹配路径/*设置为包括jsp的所有请求
CharacterEncodingFilter部分源码:
@Nullable
private String encoding;
private boolean forceRequestEncoding;
private boolean forceResponseEncoding;
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String encoding = this.getEncoding();
if (encoding != null) {
if (this.isForceRequestEncoding() || request.getCharacterEncoding() == null) {
request.setCharacterEncoding(encoding);
}
if (this.isForceResponseEncoding()) {
response.setCharacterEncoding(encoding);
}
}
filterChain.doFilter(request, response);
}
其中属性encoding为编码方式,forceRequestEncoding为是否强制设置请求编码方式,forceResponseEncoding为是否强制设置响应编码方式,这些属性均可以在web.xml中进行初始化设置
下面的doFilterInternal方法可以看到,forceRequestEncoding为true或者请求编码为null时进行编码设置为encoding,forceResponseEncoding为true时会进行请求编码设置
域对象共享数据
①servlet域对象范围太大不常使用
②session域对象的钝化和活化:
钝化:在会话过程中服务器关闭但是浏览器没有关闭,session过程是浏览器开启到浏览器关闭的过程与服务器关闭与否没有关系,因此会话仍然在继续,session中的数据就会经过序列化存储到磁盘上
活化:会话仍没有结束(浏览器没有关闭),此时服务器重启了,并且将钝化的文件内容重新读取到了session中的过程
Session域
1 使用ServletAPI向Session域添加共享数据
@RequestMapping("/testSessionScope")
public ModelAndView testSessionScope(HttpSession session,
@RequestParam("name") String name) {
ModelAndView mv = new ModelAndView();
mv.addObject("info", name);
session.setAttribute("info", "Hello,Session");
mv.setViewName("success");
return mv;
}
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>Success page</h1>
<hr>
<p th:text="${info}"></p>
<p th:text="${session.info}"></p>
</body>
</html>
JSESSIONID情况:
第一次点击超链接:
ResponseHeaders: JSESSIONID=B726AFACD887E8081984D1CA9D559FAB;
RequestHeaders: JSESSIONID=221AE584225F856A8F84E60ED96381BE;
第二次点击超链接
ResponseHeaders: 无
RequestHeaders: JSESSIONID=221AE584225F856A8F84E60ED96381BE;
③同理request域对象只在服务器启动时创建,在服务器关闭时销毁,因此使用的域对象都是同一个,因此能够共享数据
在选择域对象进行共享数据时,应选择能实现功能且范围最小的域对象
Request域
1 使用ServletAPI向request域中共享数据
request.setAttibute(String , Object)
@Controller
public class testScopeController {
@RequestMapping("/testScope")
public String testScope(HttpServletRequest request,
@RequestParam("name") String name) {
request.setAttribute("info", "Hello," + name);
return "success";
}
}
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>Index page</h1>
<hr>
<a th:href="@{/testScope(name='Hikaru')}">testScope</a>
</body>
</html>
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>Success page</h1>
<hr>
<p th:text="${info}"></p>
</body>
</html>
2 使用ModelAndView向request域对象共享数据
@RequestMapping("/testModelAndView")
public ModelAndView testModelAndView(@RequestParam("name") String name) {
ModelAndView mav = new ModelAndView();
//向请求域中添加共享数据
mav.addObject("info", "Hello," + name);
//设置试图,实现页面跳转
mav.setViewName("success");
return mav;
}
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>Index page</h1>
<hr>
<a th:href="@{/testScope(name='Hikaru')}">testScope</a><br>
<a th:href="@{/testModelAndView(name='Hikaru1')}">testModelAndView</a>
</body>
</html>
控制器方法执行完之后,DIspatcherServlet会将model(Request域添加信息)和view(视图层)信息封装成ModelAndView对象
3 使用Model向request域对象共享数据
4 使用Map向request域对象共享数据
@RequestMapping("/testModel")
public String testModel(@RequestParam("name") String name,
Model model) {
model.addAttribute("info", "Hello," + name);
return "success";
}
@RequestMapping("/testMap")
public String testMap(@RequestParam("name") String name,
Map<String, Object> map) {
map.put("info", "Hello" + name);
return "success";
}
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>Index page</h1>
<hr>
<a th:href="@{/testScope(name='Hikaru')}">testScope</a><br>
<a th:href="@{/testModelAndView(name='Hikaru1')}">testModelAndView</a><br>
<a th:href="@{/testModel(name='Hikaru2')}">testModel</a><br>
<a th:href="@{/testMap(name='Hikaru3')}">testMap</a><br>
</body>
</html>
需要注意的是,这些被添加到request对象域的共享数据的名称是相同的(info),但是值是不同的,这是因为每次点开的超链接哪怕是同一种都对应一个request,不同request域之间的共享数据自然不会相互影响
5 使用ModelMap向request域对象共享数据
※Model、Map以及MoelMap之间的关系
①其中Model、Map是接口,ModelMap是具体的类。
②三个类的实例化对象的class都是org.springframework.validation.support.BindingAwareModelMap
说明为三者形参赋值的对象是同一个,三者都可以通过一个类进行实例化
继承实现关系:
public interface Model
public interface Map
public class ModelMap extends LinkedHashMap<String, Object>
public class ExtendedModelMap extends ModelMap implements Model
public class BindingAwareModelMap extends ExtendedModelMap
所以BindingAwareModelMap可以为Model、Map、ModelMap赋值实例化。
Application(Servlet域)
对应整个工程范围
@RequestMapping("/testApplicationScope")
public String testApplicationScope(HttpSession session) {
ServletContext application = session.getServletContext();
application.setAttribute("info", "ApplicationScope");
return "success";
}
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>Success page</h1>
<hr>
<p th:text="${info}"></p>
<p th:text="${session.info}"></p>
<p th:text="${application.info}"></p>
</body>
</html>
SpringMVC的视图
SpringMVC的视图是View接口,视图的作用是渲染数据,将模型Model中的数据展示给用户
SpringMVC视图的种类有很多,默认有转发和重定向视图
此外,当工程引入JSTL依赖,转发视图自动转换为JSTL视图
若使用的视图技术是ThymeLeaf,在SpringMVC的配置文件中配置了Thymeleaf视图解析器,由视图解析器解析后得到的是ThymeLeaf视图
转发视图
1 ThymeLeaf视图
当控制器方法中的视图名称没有任何的前缀时,此时视图名称会被视图解析器解析,将视图名称拼接视图前缀和试图后缀,然后通过转发的方式进行跳转。
2 foward: InternalResourceView
在视图名称前面添加forward前缀,可以实现转发请求到其他控制器方法
@RequestMapping("/testApplicationScope")
public String testApplicationScope(HttpSession session) {
ServletContext application = session.getServletContext();
application.setAttribute("info", "ApplicationScope");
return "success";
}
@RequestMapping("/testInternalResourceView")
public String testInternalResourceView() {
return "forward:/testApplicationScope";
}
日志:
DispatcherServlet - GET "/springMVC_3_war_exploded/testInternalResourceView", parameters={}
mvc.method.annotation.RequestMappingHandlerMapping - Mapped to com.hikaru.controller.testScopeController#testInternalResourceView()
view.InternalResourceView - View [InternalResourceView], model {}
view.InternalResourceView - Forwarding to [/testApplicationScope]
DispatcherServlet - "FORWARD" dispatch for GET "/springMVC_3_war_exploded/testApplicationScope", parameters={}
mvc.method.annotation.RequestMappingHandlerMapping - Mapped to com.hikaru.controller.testScopeController#testApplicationScope(HttpSession)
由日志文件中可以看出这个过程中创建了两个视图:一个是testInternalResourceView方法中的转发视图,另一个是testApplicationScope的ThymeLeaf视图
重定向视图 RedirectView
※转发和重定向的区别
转发和重定向都是servlet中最常用的浏览器响应、页面跳转方式,都会将转发或者重定向的地址响应给浏览器。
①转发有一次浏览器请求,第二次是发生在服务器内部的请求,所以地址栏还是第一次发生请求的地址
②重定向有两次请求,第一次是访问Servlet的请求,第二次是访问重定向页面的请求,最终地址栏地址是重定向的地址
③转发可以获取请求域的共享数据,重定向是两次请求,不能获取请求域中的共享数据
④转发可以访问WEB-INF下的资源,重定向不可以,但是重定向可以访问工程外部资源
@RequestMapping("/testInternalResourceView")
public String testInternalResourceView() {
return "forward:/testApplicationScope";
}
@RequestMapping("/testRedirectView")
public String testRedirectView() {
return "redirect:/testInternalResourceView";
}
<a th:href="@{/testInternalResourceView}">testInternalResourceView</a><br>
<a th:href="@{/testRedirectView}">testRedirectView</a><br>
两个地址栏的最终地址均是testInternalResourceView
SpringMVC视图控制器 mvc:view-controller标签
当控制器方法只用来实现页面跳转,即只需要设置视图名称时,可以使用MVC视图控制器来代替该控制器方法
<mvc:view-controller path="/" view-name="index"></mvc:view-controller>
<mvc:annotation-driven />
当MVC中设置了任何一个视图控制器时,所有的控制器方法都会失效,因此需要手动开启MVC注解驱动
SpringMVC的视图解析器
【SpringMVC】(一)的更多相关文章
- 【分享】标准springMVC+mybatis项目maven搭建最精简教程
文章由来:公司有个实习同学需要做毕业设计,不会搭建环境,我就代劳了,顺便分享给刚入门的小伙伴,我是自学的JAVA,所以我懂的.... (大图直接观看显示很模糊,请在图片上点击右键然后在新窗口打开看) ...
- Springmvc数据校验
步骤一:导入四个jar包 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns=" ...
- 为什么做java的web开发我们会使用struts2,springMVC和spring这样的框架?
今年我一直在思考web开发里的前后端分离的问题,到了现在也颇有点心得了,随着这个问题的深入,再加以现在公司很多web项目的控制层的技术框架由struts2迁移到springMVC,我突然有了一个新的疑 ...
- 【SSM框架】Spring + Springmvc + Mybatis 基本框架搭建集成教程
本文将讲解SSM框架的基本搭建集成,并有一个简单demo案例 说明:1.本文暂未使用maven集成,jar包需要手动导入. 2.本文为基础教程,大神切勿见笑. 3.如果对您学习有帮助,欢迎各种转载,注 ...
- 快速搭建springmvc+spring data jpa工程
一.前言 这里简单讲述一下如何快速使用springmvc和spring data jpa搭建后台开发工程,并提供了一个简单的demo作为参考. 二.创建maven工程 http://www.cnblo ...
- redis集成到Springmvc中及使用实例
redis是现在主流的缓存工具了,因为使用简单.高效且对服务器要求较小,用于大数据量下的缓存 spring也提供了对redis的支持: org.springframework.data.redis.c ...
- 流程开发Activiti 与SpringMVC整合实例
流程(Activiti) 流程是完成一系列有序动作的概述.每一个节点动作的结果将对后面的具体操作步骤产生影响.信息化系统中流程的功能完全等同于纸上办公的层级审批,尤其在oa系统中各类电子流提现较为明显 ...
- springMVC学习笔记--知识点总结1
以下是学习springmvc框架时的笔记整理: 结果跳转方式 1.设置ModelAndView,根据view的名称,和视图渲染器跳转到指定的页面. 比如jsp的视图渲染器是如下配置的: <!-- ...
- springMVC初探--环境搭建和第一个HelloWorld简单项目
注:此篇为学习springMVC时,做的笔记整理. MVC框架要做哪些事情? a,将url映射到java类,或者java类的方法上 b,封装用户提交的数据 c,处理请求->调用相关的业务处理—& ...
- springmvc的拦截器
什么是拦截器 java里的拦截器是动态拦截action调用的对象.它提供了一种机制可以使 ...
随机推荐
- SelectionSort,选择排序,C++实现
1 // g++ selection_sort.cc -Wall -O3 -std=c++11 && ./a.exe 2 3 4 #include <iostream> 5 ...
- sql server 计算时间差的一部分函数【转】
在做Sql Server开发的时候有时需要获取表中今天.昨天.本周.上周.本月.上月等数据,这时候就需要使用DATEDIFF()函数及GetDate()函数了.DATEDIFF ( datepart ...
- Asp.net zero项目框架和配置
- DOS基本命令与快捷键
DOS命令 #查看当前盘下的所有目录 dir #切换盘符 直接盘符名称: 例如切换D: #切换目录 cd 目录名称 #清屏 cls #退出 exit #创建 cd>文件名称.文件后缀 #查询电脑 ...
- Cross Site Scripting DOM (XSS) 攻击jQuery append() 的处理方法
做安全红线使用Fortify工具进行扫描时,jquery append会报Cross Site Scripting DOM风险.解决该问题有两种办法. 一.原生dom方式 使用JavaScript原生 ...
- docker方式部署的gitlab跨版本迁移升级
之前代码服务器用的 beginor/gitlab-ce:11.3.0-ce.0 的版本,而当前时间已经到12.4.1了. gitlab 官方已经开始支持多语言, 而且也提供了 docker 镜像, b ...
- Postman请求Https接口与认证
http://t.zoukankan.com/embedded-linux-p-12656769.html
- python打包成exe过程中遇到的问题
先描述下初始状况: python版本为3.7.3,直接在cmd中运行pip安装pyinstaller失败,应该是最开始安装python时没有把目录添加到环境变量中(我很懒).直接在python的安装目 ...
- Spring-设计模式
1.1开闭原则 开闭原则(open-closed principle,OCP)是指一个软件实体(如类,模块和函数)应该对扩展开放,对修改关闭.所谓的开闭,也正是对扩展和修改两个行为的一个原则. 强调用 ...
- 对于实现上一篇遇到的问题——MyBatis+增删改查(已解决)
问题一:该Http不支持Get/Post方法 我根据网上的解决方法将Get和Post的位置来回换,还是不停报错: 后来偶然间看到一个博主发的"你的代码写在Get或者Post里面,就将没写代码 ...