前景介绍
springmvc图解
环境搭建
- eclipse需要spring的环境,安装spring的插件。
基于eclipse的spring插件安装可以看这个链接
- 需要创建一个web项目
- 需要的jar包
springmvc
需要spring
的核心包: commons-logging-1.1.1.jar //必须有Log包,不然报错
spring-beans-5.2.6.RELEASE.jar
spring-context-5.2.6.RELEASE.jar
spring-core-5.2.6.RELEASE.jar
spring-expression-5.2.6.RELEASE.jar
- 对于mvc需要的jar包如下
spring-web-5.2.6.RELEASE.jar
spring-webmvc-5.2.6.RELEASE.jar
- 使用注解,导入aop的包
spring-aop-5.2.6.RELEASE.jar
工程起步
- 项目结构
配置文件:①web.xml配置文件②框架本身的配置文件
- 配置
web.xml
文件
在web.xml
按下Alt+/
快捷键.
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">
<display-name>01-helloworld</display-name>
<!-- The front controller of this Spring Web application, responsible for handling all application requests -->
<!-- 前端控制器 -->
<servlet>
<servlet-name>springDispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<!-- 配置springmvc文件 -->
<param-name>contextConfigLocation</param-name>
<param-value>classpath:mvc.xml</param-value>
</init-param>
<!-- servlet启动加载,即服务器启动时创建对象,数值越小表示越先创造对象 -->
<load-on-startup>1</load-on-startup>
</servlet>
<!-- Map all requests to the DispatcherServlet for handling -->
<servlet-mapping>
<servlet-name>springDispatcherServlet</servlet-name>
<!-- 配置拦截所有 -->
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
- 编写mvc.xml配置
<?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.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 配置需要扫描注解的类 如:后端Controller层的注解@Controller -->
<context:component-scan base-package="com.hyq"/>
<!-- 开启mvc注解驱动 -->
<mvc:annotation-driven/>
<!-- 默认使用tomcat的servlet -->
<mvc:default-servlet-handler/>
</beans>
package com.hyq.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class HelloController {
@RequestMapping("/")
public String index() {
System.out.println("------------");
return "WEB-INF/index.html";
}
@RequestMapping("/success")
public String success() {
System.out.println("------------");
return "WEB-INF/page/success.html";
}
}
- 编写
index.html
和success.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<a href="success">nihao</a>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
sssss
</body>
</html>
- 还可以在
mvc.xml
配置视图解析器,这样controller
层返回页面时不用带前缀和后缀(如.html
)
-
controller
层
springmvc的配置文件
若没有指定配置文件会报错误,默认寻找/WEB-INF/XXX-servlet.xml文件
org.springframework.beans.factory.BeanDefinitionStoreException: IOException parsing XML document from ServletContext resource [/WEB-INF/springDispatcherServlet-servlet.xml]; nested exception is java.io.FileNotFoundException: Could not open ServletContext resource [/WEB-INF/springDispatcherServlet-servlet.xml]
相关的注解
RequestMapping的源码
//该注解可用于TYPE(类)上,也可用于METHOD(方法)上
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping {
String name() default "";
@AliasFor("path")
String[] value() default {};
@AliasFor("value")
String[] path() default {};
RequestMethod[] method() default {};
String[] params() default {};
String[] headers() default {};
String[] consumes() default {};
String[] produces() default {};
}
RequestMapping - params
①myParam=myValue
:请求中必须要包含myParam
的参数,且值为myValue
如:@RequestMapping(value="/hello1",params="name=one")
.
访问地址:/XX项目名/hello1?name=one
②myParam!=myValue
:请求中必须包含myParam
的参数,且值不能为myValue
如:@RequestMapping(value="/hello2",params="name!=one")
.
访问地址:/XX项目名/hello2?name=two
,只要name
不等于one
就行
③myParam
:请求中必须包含myParam
的参数,值任意
如:@RequestMapping(value="/hello3",params="name")
.
访问地址:/XX项目名/hello3?name
,只要地址中有参数就行,值任意
④!myParam
:请求中不能包含myParam
的参数
如:@RequestMapping(value="/hello4",params="!name")
.
访问地址:/XX项目名/hello4?pwd
,只要地址中不包含name
参数就行.
例子:@RequestMapping(value="/hello",params={"name","pwd=123"})
这种情况下,两个参数的顺序不能颠倒,且第一个参数必须是name
它的值任意,第二个参数必须是pwd
,值必须是123
/XX项目名/hello?name&&pwd=123
@ PathVariable的作用
他的作用在参数里用来提取url中的请求参数
@RequestMapping("/hyq/{url}/{name}")
public String ant5(@PathVariable("url") String url,
@PathVariable("url") String name) {
System.out.println("url="+url+",name="+name);
return "success";
}
@RequestParam的注解使用
源码
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestParam {
/**
* Alias for {@link #name}.
*/
@AliasFor("name")
String value() default "";
/**
* The name of the request parameter to bind to.
* @since 4.2
*/
@AliasFor("value")
String name() default "";
boolean required() default true;
String defaultValue() default ValueConstants.DEFAULT_NONE;
}
-
地址栏所带的参数和Java形参名称一致时,不用写@requestParam的注解
地址栏输入http://localhost:8080/test?username=3
控制台得到的结果是username=3
当地址栏输入http://localhost:8080/test?user=3
控制台得到的结果是username=null
当地址栏带参数但不带值时,http://localhost:8080/test?username
控制台得到的结果是username=
当地址栏不带参数时,http://localhost:8080/test
控制台得到的结果是username=null
-
地址栏所带的参数和Java形参名称不一致时,需要写@requestParam的注解
地址栏输入http://localhost:8080/test1?user=3
,控制台输出username=3
当地址栏的不带参数或参数不一致(和注解里面的不一致)时,http://localhost:8080/test1
直接报错,此时可以设置 参数可有可无 @RequestParam(required = false)
地址栏带参数 但不带值时,http://localhost:8080/test1?user
,控制台输出username=
@RequestParam和@PathVariable区别
@PahtVariable: 获取路径中的值 http://localhost:8080/user/{id}
@RequestParam: 获取?后面的值 http://localhost:8080/user?name=3
@RequestHeader注解
源码:
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestHeader{
/**
* Alias for {@link #name}.
*/
@AliasFor("name")
String value() default "";
/**
* The name of the request parameter to bind to.
* @since 4.2
*/
@AliasFor("value")
String name() default "";
boolean required() default true;
String defaultValue() default ValueConstants.DEFAULT_NONE;
}
作用:获取请求头中的值.
注解就相当于 request.getHeader()
,当请求头中没有 值时 默认会报错
控制台输出:username=3
userAgent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.82 Safari/537.36
@CookieValue
用法和上面类似,这里不再赘述
@RequestBody
作用:将返回的数据放在响应体中
Ant风格的请求
|
|
? |
匹配单个字符 |
* |
匹配0个或任意多个字符,可以匹配一层目录 |
** |
匹配0个或任意多个目录 |
?的用法
@RequestMapping(value="/ant?")
: ?处必须填写一个字符
若没有填写字符,则报错
* 的用法
@RequestMapping(value="/ant*")
:* 处可以填写0个字符,也可以填写更多字符
填写多个字符一样通过
@RequestMapping(value="/*/ant")
:* 处这里代表一个目录,形如/XX项目名称/a/ant
若是/XX项目名称/a/b/ant
会报404,找不到页面。形如/XX项目名称/ant
,把一级目录省略后,也会报404
** 的用法
@RequestMapping(value="/**/ant")
:** 处可以填写多级目录.形如:/XX项目名称/a/b/c/d/ant
,也可以不写目录,形如:/XX项目名称/ant
也可以不写目录,形如:/XX项目名称/ant
当访问地址有重复时,精确优先。看控制台输出的内容
@RequestMapping("/*/ant")
public String ant3() {
System.out.println("-----ant3-------");
return "success";
}
@RequestMapping("/**/ant")
public String ant4() {
System.out.println("-----ant4-------");
return "success";
}
Rest 详解
表单形式的put/delete提交
1.web.xml配置过滤器
2. 表单中form的method必须是post提交,需要有一个隐藏的put/delete
3. 编写controller配置
@RequestMapping(value = "/getBook/{id}",method = RequestMethod.GET)
public String getBook(@PathVariable("id") String id) {
System.out.println("查询图书id="+id);
return "success";
}
@RequestMapping(value = "/addBook",method = RequestMethod.POST)
public String addBook() {
System.out.println("添加图书");
return "success";
}
@RequestMapping(value = "/delBook/{id}",method = RequestMethod.DELETE)
public String delBook(@PathVariable("id") Integer id) {
System.out.println("删除图书id="+id);
return "success";
}
@RequestMapping(value = "/updBook/{id}",method = RequestMethod.PUT)
public String updBook(@PathVariable("id") Integer id) {
System.out.println("更新图书id="+id);
return "success";
}
HiddenHttpMethodFilter的源码分析
private static final List<String> ALLOWED_METHODS =
Collections.unmodifiableList(Arrays.asList(HttpMethod.PUT.name(),
HttpMethod.DELETE.name(), HttpMethod.PATCH.name()));
public static final String DEFAULT_METHOD_PARAM = "_method";
private String methodParam = DEFAULT_METHOD_PARAM;
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
HttpServletRequest requestToUse = request;
if ("POST".equals(request.getMethod()) && request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) == null) {
String paramValue = request.getParameter(this.methodParam); //通过request获取参数
if (StringUtils.hasLength(paramValue)) { //参数不为空
String method = paramValue.toUpperCase(Locale.ENGLISH); //转成大写
if (ALLOWED_METHODS.contains(method)) { //是否是Put/delete/patch之一
requestToUse = new HttpMethodRequestWrapper(request, method);
}
}
}
filterChain.doFilter(requestToUse, response);
}
POJO自动封装
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<form method="post" action="submit">
书名:<input type="text" name="name"><br>
作者:<input type="text" name="author"><br>
<!-- 下面是属于级联属性的操作 -->
省:<input type="text" name="address.province"><br>
市:<input type="text" name="address.city"><br>
<input type="submit">
</form>
</body>
</html>
-
编写Book1和Address类,代码中除了字段值,还包括get(),set(),toString(),无参有参构造器方法,其中必须有set()方法.
-
编写Controller
效果
结果
当输入中文时,会有乱码出现,解决方案:在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>
<init-param>
<param-name>forceRequestEncoding</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>forceResponseEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<!-- 拦截所有 -->
<url-pattern>/*</url-pattern>
CharacterEncodingFilter核心源码
在web.xml中,filter的初始化参数encoding和forceRequestEncoding,forceResponseEncoding 设置的值,最终都要通过CharacterEncodingFilter的setXXX方法赋值。
数据输出-Model/Map/ModelMap
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>首页</h1>
<a href="success1">调转1</a>
<a href="success2">调转2</a>
<a href="success3">调转3</a>
</body>
</html>
<body>
成功<br>
sessionScope:${sessionScope.msg }<br>
requestScope:${requestScope.msg }<br>
pageScope:${pageScope.msg }<br>
applicationScope:${applicationScope.msg }<br>
</body>
@RequestMapping("/success1")
public String success1(Map<String, Object> map) {
map.put("msg", "你好");
System.out.println(map.getClass());
return "success";
}
@RequestMapping("/success2")
public String success2(Model model) {
model.addAttribute("msg", "model");
System.out.println(model.getClass());
return "success";
}
@RequestMapping("/success3")
public String success3(ModelMap mp) {
mp.addAttribute("msg", "ModelMap");
System.out.println(mp.getClass());
return "success";
}
效果:
分别点击 三个超链接,得到三个页面
结论:Model/Map/ModelMap
最终都要都要转换为BindingAwareModelMap
,数据保存在了request域中
Model(接口),Map(接口),ModelMap(类)之间关系
< mvc:annotation-driven/>的解释
< mvc:annotation-driven/>,< mvc:default-servlet-handler/>
- 1)都没有配置的情况下,只有动态资源(@requestMapping映射的资源)能够访问,静态资源(js,HTML,.img等)不能访问
- 2)配置
< mvc:default-servlet-handler/>
,没有配置< mvc:annotation-driven/>
,静态资源可以访问,但是动态资源不能访问
- 3)配置
< mvc:annotation-driven/>
,没有配置< mvc:default-servlet-handler/>
,静态资源 依然不能访问
- 4) 两个都配置,才能保证静态资源和动态资源都能访问
springmvc运行流程
1)发送请求,前端控制器(DispatcherServlet
)收到请求,调用doDispatch
处理
2)根据HandlerMapping
中保存的请求映射信息找到处理当前请求的处理器执行链(包含拦截器)
3)根据当前处理器找到它的HandlerAdapter
(适配器)
4)拦截器的preHandler
先执行
5)适配器执行目标方法,并返回ModelAndView
6)拦截器的postHandler
执行
7)处理结果;(页面渲染流程)
(1)如果有异常使用异常解析器处理,处理完毕后返回ModelAndView
(2)调用render
进行页面渲染
(3)执行拦截器的afterCompletion;