一、Spring MVC 简介

Spring MVC 是 Spring 基于 MVC 设计理念提供的一个表现层的 Web 框架。是目前主流的 MVC 框架之一。
Spring MVC 通过一套 MVC 注解,让 pojo 成为处理请求的控制器,无需实现任何接口。 比其他 MVC 框架更具扩展性和灵活性。
 

二、环境搭建

1、导入基础jar 包

SpringMVC包

        <dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>4.3.18.RELEASE</version></dependency>

日志文件包

        <dependency><groupId>org.slf4j</groupId><artifactId>slf4j-log4j12</artifactId><version>1.6.1</version></dependency>

在 resources目录下创建log4j.properties 日志文件

log4j.rootLogger=DEBUG,stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n

默认的名字(log4j.properties),其他名字需要配置log4jConfigLocation

<!--给日志配置路径--><context-param><param-name>webAppRootKey</param-name><param-value>webApp.root</param-value></context-param>//默认的无需配置<!--<context-param><param-name>log4jConfigLocation</param-name><param-value>classpath:log4j2.xml</param-value></context-param>-->

log4j.properties文件描述:
log4j.rootLogger=DEBUG,stdout
DEBUG日志输出优先级,stdout输出的位置

日志输出优先级:从高到低依次为error,warn,info,debug,低级别的可以输出高级别的日志,如debug可以输出任何优先级的
日志
文件输出位置:表示控制台(stdout)或者文件的输出(file),这两个有点类似于变量名,可以自定义名称
控制台的输出为:log4j.appender.stdout=org.apache.log4j.ConsoleAppender
保存为文件的为:log4j.appender.file = org.apache.log4j.FileAppender

log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
设置日志的输出格式

log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n
日志输出的格式化信息

具体参见:PatternLayout参数含义以及详细配置
 

2、编写 Spring 配置文件

在 WEB-INF中创建 springMVC-servlet.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/beanshttp://www.springframework.org/schema/beans/spring-beans-4.2.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context-4.2.xsdhttp://www.springframework.org/schema/mvchttp://www.springframework.org/schema/mvc/spring-mvc-4.2.xsd"><!-- 启动注解,注册服务--><mvc:annotation-driven/><!-- 启动自动扫描    --><!-- 制定扫包规则 ,扫描controller包下面的类--><context:component-scan base-package="controller"><!-- 扫描使用@Controller注解的JAVA 类 --><context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/></context:component-scan>
</beans>

WEB-INF中创建web.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://java.sun.com/xml/ns/javaeehttp://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"version="3.0"><!--将欢迎页设置成  index.html--><welcome-file-list><welcome-file>index.html</welcome-file></welcome-file-list><!--配置  DispatcherServlet --><servlet><servlet-name>springMVC</servlet-name><load-on-startup>0</load-on-startup><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class></servlet><servlet-mapping><servlet-name>springMVC</servlet-name><url-pattern>*.html</url-pattern></servlet-mapping>
</web-app>

上面的配置可以加载resources文件夹下spring-开头的xml 文件。DispatcherServlet (Servlet分配器)在 SpringMVC中负责流程控制和职责分派,如文件上传、 请求映射等。 其中<servlet-name>中的值是和配置文件相对应的,如此处<servlet-name> 的值是 springMVC,对应配置文件的名字就叫springMVC-servlet.xml。
如果想要使用其它文件名,可以通过配置 contextConfigLocation,如下:

<servlet><servlet-name>springMVC</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><!--指定其他文件名--><init-param><param-name>contextConfigLocation</param-name><param-value>WEB-INF/applicationContext.xml</param-value></init-param>
</servlet>

load-on-startup元素

标记容器是否应该在web应用程序启动的时候就加载这个servlet(实例化并调用其init()方法)
它的值必须是一个整数,表示servlet被加载的先后顺序。
元素值默认或为负数,Servlet被请求时加载
元素值为正整数或者0时,表示容器在应用启动时就加载并初始化这个servlet,值越小,servlet的优先级越高,就越先被加载。值相同时,容器就会自己选择顺序来加载

<load-on-startup>0</load-on-startup>

servlet-mapping元素

servlet-mapping是用于servlet的路径映射配置,其中url-pattern为指定的映射拦截路径。
servlet-name需要和servlet元素里的servlet-name保持一致
url-pattern指定了spring需要拦截派发的路径。

url-pattern5种配置模式:

(1)/xxx:完全匹配/xxx的路径

(2)/xxx/*:匹配以/xxx开头的路径,请求中必须包含xxx。

(3)/*:匹配/下的所有路径,请求可以进入到action或controller,但是转发jsp时再次被拦截,不能访问jsp界面。

(4).xx:匹配以xx结尾的路径,所有请求必须以.xx结尾,但不会影响访问静态文件。

(5)/:默认模式,未被匹配的路径都将映射到servlet,对jpg,js,css等静态文件也将被拦截,不能访问。

3、编写controller

在 java 文件夹下新建 controller文件夹,创建 IndexController.java

package controller;import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;@Controller
public class IndexController {//RequestMapping 设置访问路径是 index.html@RequestMapping("index.html")public String showIndex() {//showIndex方法处理index.html请求//返回 index.jsp页面return "index.jsp";}
}

配置好tomcat进行运行测试:
http://localhost:8080/mvcdemo/index.html
页面内容:
welcome home
表示环境配置成功。

有访问时,服务器端有日志打印于控制台

//名为“springmvc”的servlet分配器正在处理获取[/mvcdemo/index.html]的请求
2020-06-05 12:26:41,590 DEBUG [org.springframework.web.servlet.DispatcherServlet] - DispatcherServlet with name 'springMVC' processing GET request for [/mvcdemo/index.html]
//查找/index.html的处理程序方法
2020-06-05 12:26:41,591 DEBUG [org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping] - Looking up handler method for path /index.html
//返回处理程序方法
2020-06-05 12:26:41,592 DEBUG [org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping] - Returning handler method [public java.lang.String controller.IndexController.showIndex()]
//返回单例bean“indexController”的缓存实例
2020-06-05 12:26:41,592 DEBUG [org.springframework.beans.factory.support.DefaultListableBeanFactory] - Returning cached instance of singleton bean 'indexController'
//[/mvcdemo/index.html]最后修改值为-1
2020-06-05 12:26:41,593 DEBUG [org.springframework.web.servlet.DispatcherServlet] - Last-Modified value for [/mvcdemo/index.html] is: -1
//名字叫'index.jsp'; URL为[index.jsp]在名为“springmvc”的servlet分配器中
2020-06-05 12:26:41,596 DEBUG [org.springframework.web.servlet.DispatcherServlet] - Rendering view [org.springframework.web.servlet.view.InternalResourceView: name 'index.jsp'; URL [index.jsp]] in DispatcherServlet with name 'springMVC'
//转发资源index.jsp到内部资源视图' index.jsp '中
2020-06-05 12:26:41,597 DEBUG [org.springframework.web.servlet.view.InternalResourceView] - Forwarding to resource [index.jsp] in InternalResourceView 'index.jsp'
//成功完成请求
2020-06-05 12:26:41,608 DEBUG [org.springframework.web.servlet.DispatcherServlet] - Successfully completed request

在这里插入图片描述

三、使用@RequestMapping 映射请求

1、在类和方法上添加@RequestMapping

SpringMVC 使用@RequestMapping 为控制器指定可以处理哪些 URL 请求。可以在类上和方法上都添加@RequestMapping 注解。
如在 IndexController 中添加注解:

@Controller
//加上这个注解,本类方法访问路径前都要加上/mvc
@RequestMapping("/mvc")
public class IndexController {//RequestMapping 设置访问路径是 index.html//因为类名上面已经加了/mvc,所以进入这个方法的 url 是/mvc/index.html@RequestMapping("/index.html")public String showIndex() {//因为添加了父路径,而 index.jsp 在根路径,所以前面加/代表相对于根路径return "/index.jsp";} }

IndexController 类上添加了@RequestMapping(“/mvc”) , 相当于一个父路径 , 即IndexController类中所有@ResuestMapping 注解的方法访问路径前都要添加上这个路径。想进入 showIndex()方法,路径是/mvc/index.html。
@RequestMapping(“/mvc”)相当于@RequestMapping(value=“/mvc”)
DispatcherServlet 截获请求后,就通过@RequestMapping 提供的信息,将请求进行分发。

注意:此处相对路径,return返回的路径为内容真实路径,加了/相对于根路径,不加相对于父路径

应用场景示例:
在这里插入图片描述
DeptController 用于处理部门相关的业务。EmployeeController 用于处理员工相关的业务。部门列表叫 list.html,员工列表也叫 list.html。可以通过父路径来区分请求。不需要程序员给每个请求单独起名字,也能让项目中的 url 更规范。
 

2、通过method指定POST或GET

@RequestMapping中有一个method属性,用于指定当前的方法是用POST还是GET访问:

//此方法只支持 POST 访问方式
@RequestMapping(value = "/post.html", method = RequestMethod.POST)
public String showPost() {return "/index.jsp"; }

如果在浏览器中默认通过GET访问,会出现405错误:

HTTP Status 405 - Request method ‘GET’ not supported

或者只用@GetMapping,@PostMapping 等注解代替@RequestMapping
 

3、其它参数

其它参数需要对应到 http 请求头:
在这里插入图片描述
comsume:
方法仅处理 request Content-Type 为“application/json”类型的请求。

@RequestMapping(value = "xx.html", consumes = "application/json")

produces:
方法仅处理 request 请求中 Accept 头中包含了"application/json"的请求,同时返回的内容类
型为 application/json;

@RequestMapping(value = "xx.html", produces = "application/json")

params:
只处理请求中包含 act 参数并且值为 list 的请求。

@RequestMapping(value = "xx.html", params = "act=list")

params=”param1”请求中必须包含参数名为 param1 的参数。
params=”!param1”请求中不能包含参数名为 param1 的参数。
params=”param1!=value1”请求中包含名为 param1 的参数,但参数值不能为 value1。
params={“p1=v1”,p2}请求中必须包含名称为 p1 和 p2 两个参数,且 p1 的值为 v1。

headers:
仅处理 request 的 header 中包含了指定“Refer”请求头和对应值为“http://www.xx.com/”的请求;

@RequestMapping(value = "xx.html", headers = "Referer=http://www.xx.com/")

 

4、REST 风格 URL

如/delete/123 和/delete/456 这两个 url,其中/delete/是公用的,后面的 123 和 456 是动态的
参数。使用@PathVariable 获取参数:

//url 中包含参数
@RequestMapping(value = "/rest1/{id}")
public String testRest1(@PathVariable Integer id) {System.out.println(id);return "/index.jsp"; }
//占位的参数和方法中参数名不同的时候
@RequestMapping(value = "/rest2/{id} ")
public String testRest2(@PathVariable("id") Integer someId) {System.out.println(someId);return "/index.jsp"; }

 

5、支持正则表达式

{参数名:正则表达式}

//支持正则表达式匹配
@RequestMapping("/reg/{param:[\\d]+.html")
public String testRest3(@PathVariable Integer param) {System.out.println(param);return "/index.jsp"; }

应用场景示例:
比如有一些静态页面展示,如果每一个页面都写一个访问的方法,会增加代码量。可以使用动态参数,在路径中提取出页面名称,合并到一个方法。(ps:在项目开发中,有时需要对权限进行限制,比如有些页面必须登录后才能访问。这时就需要限制用户不能直接通过.jsp 直接访问 jsp 页面文件,而是通过.html 经过 Spring 过滤器进行处理。
在这里插入图片描述
上面的代码返回的页面路径前没有加/,所以是相对于当前访问路径的,页面需要放在webapp 的 page 文件夹下才能显示。
 

6、ANT 风格 URL(了解)

Ant 风格支持三种匹配符:
?:匹配一个字符。

@RequestMapping("/ant/test??.html")

可以匹配/ant/testaa.html 或/ant/testbb.html

*:匹配任意字符。

@RequestMapping("/ant/*/test.html")

可以匹配 /ant/aaa/test.html 或/ant/bcd/test.html 等

**:匹配多层路径。

@RequestMapping("/ant/**/test.html")

可以匹配/ant/test.html 或/ant/aa/test.html 或/ant/aa/bb/test.html 等
ANT 风格的 url 通常用在资源路径的加载中。
 

四.参数绑定

1、@RequestParam

使用@RequestParam 可以实现把请求中的参数传递给被请求的方法。

value

package controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;@Controller
public class ParamController {@RequestMapping("param1.html")public String testParam1(@RequestParam(value = "name") String name,@RequestParam(value = "id") Integer id) {System.out.println(name);System.out.println(id);return "index.jsp";}
}

在浏览器中访问如下地址:http://localhost:8080/param1.html?name=abc&id=1
可以看到控制台上输出:

abc
1

@RequestParam(value = "name") String name

相当于 servlet 中的:

String name=request.getParameter("name");

代码:

@RequestParam(value = "id") Integer id

相当于 servlet 中的:

    String idStr= request.getParameter("id");Integer id=null;if(idStr!=null&& idStr.trim()!=""){id=Integer.parseInt(idStr) }

可见当参数类型不是 String 的时候,@RequestParam 可以大大简化代码。

required

默认的使用 @RequestParam 注解的参数都是必传的。上面的例子中,使用@RequestParam 注解了 name 和 id 两个参数。如果在访问的时候少传一个参数,会出现如下异常:
在这里插入图片描述
如果某个参数不是必须的,可以使用 required 属性设置:

@RequestParam(value = "id",required = false) Integer id

required 默认是 true,代表参数必传;设置为 false,则代表参数可以为 null。
需要注意的是如果注入的是基本类型的数据,如 int 型,参数为空就会抛出异常,因为null 不能赋值给基本数据类型,只能是对象类型。

defaultValue
如果没传某个参数时,想给参数一个默认值,可以使用 defaultValue 属性设置:

@RequestParam(value = "name",defaultValue = "123") String name

如果访问时没传 name 参数,将会默认给 name 赋值为 123。相当于 servlet 中的:

String name = request.getParameter("name");
if (name = null) {name ="123"; }

简化写法

//当 url 中的参数名与方法接收时参数名一致,且参数都是必传的,可以省略@RequestParam
@RequestMapping("param3.html")
public String testParam3(String name, Integer id) {System.out.println(name);System.out.println(id);return "index.jsp";

 
映射 POJO 类型参数

SpringMVC 支持 POJO 类型参数映射,即将多个参数直接封装成实体类。
如我们要将参数封装到 Employee 和 Dept 对象中,先创建实体类:

Dept 实体

package pojo;public class Dept {private Integer id;private String name;private List<Employee> employees;//getter/setter 方法略
}

Employee 实体

package pojo;public class Employee {private Integer id;private String name;private Float salary;private Dept dept;//getter/setter 方法略
}

controller 中的方法:

//映射 pojo 类@RequestMapping("param5.html")public String testParam5(Employee employee) {System.out.println("员工名:" + employee.getName());System.out.println("员工 id:" + employee.getId());//支持级联形式的映射System.out.println("员工部门名:" + employee.getDept().getName());System.out.println("员工部 id:" + employee.getDept().getId());return "index.jsp";}

为方便测试,jsp 页面中以表单形式提交请求:

<form action="/param5.html" method="post">员工 id:<input name="id" type="text"/><br/>员工名:<input name="name" type="text"/><br/><!--级联形式的映射-->员工部门 id:<input name="dept.id" type="text"/><br/>员工部门名:<input name="dept.name" type="text"/><br/><input type="submit" value="提交"/>
</form>

提交控制台输出:

员工名:张三
员工 id:007
员工部门名:新媒体
员工部 id:001

 
基本类型的数组

基本数据类型的数组如 Integer[],String[]等
比如批量删除数据的时候,需要传递多个数据的 id,这时用一个数组去接收:

//映射数组@RequestMapping("param6.html")public String testParam6(Integer[] ids) {for (Integer id : ids) {System.out.println(id);}return "index.jsp";}

页面:

<form action="/param6.html" method="post"><input name="ids" value="1" type="checkbox"/>1<br/><input name="ids" value="2" type="checkbox"/>2<br/><input name="ids" value="3" type="checkbox"/>3<br/><input name="ids" value="4" type="checkbox"/>4<br/><input type="submit" value="提交"/>
</form>

控制台运行结果:

1
2
3
4

 

2、@RequestBody

对于复杂的数据类型,如 Dept[]、List<Integer>、List<Dept>、List<Map<String,Object>>
以及 Dept 里包含 List<Employee>的映射,不能再使用简单的 form 表单提交请求了,需要使用 ajax 模拟提交 json 数据,并指定请求的 contentType 是 application/json。在 Controller 中 用@RequestBody 接收参数。
SpringMVC 解析 json 需要一个 json 适配器,json 转化使用的是 jackson,需要在 pom.xml中添加 jackson 的 jar 包:

<!-- Jackson Json 处理工具包 --><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.7.4</version></dependency>

在 springMVC-servlet.xml 中配置 json 适配器:

	<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"><property name="messageConverters"><list><bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"><property name="supportedMediaTypes"><list><value>application/json;charset=utf-8</value></list></property></bean></list></property></bean>

在工程中加入 jQuery 类库,如 jquery-1.10.1.min.js
在这里插入图片描述
param.jsp 是我们写页面代码的文件。
在页面中引入 jQuery:

 <script type="text/javascript" src="jquery-1.10.1.min.js"></script>

 
List<Object>类型的映射

List 是基本数据类型的集合,如 List类型的映射:

    //List 集合中包含基本数据类型@RequestMapping("param7.html")public String testParam7(@RequestBody List<String> names) {for (String name : names) {System.out.println(name);}return "index.jsp";}

页面中发送请求的方法:

<input type="button" onclick="testParam7()" value="测试 List<Object>"/>
<script type="text/javascript">function testParam7(){var nameList = ["张三","李四 "];//String 类型的数组$.ajax({type: "POST",url: "/param7.html",data: JSON.stringify(nameList),//将对象序列化成 JSON 字符串contentType : "application/json;charset=utf-8",success: function(data){alert(data);}});}
</script>

 
List<POJO> 和 POJO[]类型的映射

如将请求数据封装成 List<Dept>

//List 集合中包含对象类型@RequestMapping("param8.html")
//如果是数组,就写(@RequestBody Dept[] depts)public String testParam8(@RequestBody List<Dept> depts) {
//public String testParam8(@RequestBody Dept[] depts) {for (Dept dept : depts) {System.out.println(dept.getId() + dept.getName());}return "index.jsp";}

如果要封装成数组,就写页面提交请求方法:

<input type="button" onclick="testParam8()" value="测试 List<POJO>"/>
<script type="text/javascript">function testParam8(){var deptList = new Array();//集合中存放的是实体类deptList.push({"id":1,"name":"技术部"});deptList.push({"id":2,"name":"测试部"});$.ajax({type: "POST",url: "/param8.html",data: JSON.stringify(deptList),//将对象序列化成 JSON 字符串contentType : "application/json;charset=utf-8",success: function(data){alert(data);}});}
</script>

 
POJO 中包含 List 的映射

    //POJO 中包含 List。Dept 中包含 List<Employee>@RequestMapping("param9.html")public String testParam9(@RequestBody Dept dept) {System.out.println(dept.getName());for (Employee employee : dept.getEmployees()) {System.out.println(employee.getName());return "index.jsp";}

页面提交请求的代码:

<input type="button" onclick="testParam9()" value="POJO 中包含 List"/>
<script type="text/javascript">function testParam9(){var empList = new Array();//集合中存放的是实体类empList.push({"id":1,"name":"张三"});empList.push({"id":2,"name":"李四"});var dept={"id":1,"name":"技术部","employees":empList};$.ajax({type: "POST",url: "/param9.html",data: JSON.stringify(dept),//将对象序列化成 JSON 字符串contentType : "application/json;charset=utf-8",success: function(data){alert(data);}});}
</script>

 
List<Map<String,Object>>的映射

List 中封装 Map 的操作和封装 POJO 类似:

    //List<Map<String,Object>>的映射@RequestMapping("param10.html")public String testParam10(@RequestBody List<Map<String, Object>> map) {for (Map<String, Object> stringObjectMap : map) {//遍历集合for (String key : stringObjectMap.keySet()) {//遍历当前 Map 中的 key//根据 key 拿到对应的值System.out.println(key + "=" + stringObjectMap.get(key));}System.out.println("--------------------");}return "index.jsp";}

页面发送请求的代码:

<input type="button" onclick="testParam10()" value="测试 List<Map<String,Object>>"/>
<script type="text/javascript">function testParam10(){var data = new Array();//data.push({"id":1,"name":"技术部"});data.push({"id":3,"addr":"三楼"});$.ajax({type: "POST",url: "/param10.html",data: JSON.stringify(data),//将对象序列化成 JSON 字符串contentType : "application/json;charset=utf-8",success: function(data){alert(data);}});}
</script>

 

使用 Servlet API

如果我们在方法中还想用 Servlet 的 request 和 response,可以在方法上添加参数:

    //得到 HttpServletRequest 和 HttpServletResponse@RequestMapping("param4.html")public String testParam4(HttpServletRequest request, HttpServletResponse response) {System.out.println(request.getParameter("name"));return "index.jsp";}

需要在 pom.xml 中添加 servlet 的 jar 包:

<dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>3.1.0</version>
</dependency>

 

五、处理模型数据

SpringMVC 中的模型数据是非常重要的,因为 MVC 中的控制(C)请求处理业务逻辑来生成数据模型(M),而视图(V)就是为了渲染数据模型的数据。当有一个查询的请求,控制器(C)会把请求拦截下来,然后把根据请求的内容对它进行分配适合的处理方法,在处理方法上进行处理查询的业务逻辑,得到了数据,再把数据封装成数据模型对象,最后把数据模型(M)对象传给了视图(V),让视图去渲染数据模型。

SpringMVC 提供了以下几种途径输出模型数据:
ModelAndView:处理方法返回值类型为 ModelAndView 时,方法体即可通过该对象添加模型数据。
@ModelAttribute:方法入参标注该注解后,入参的对象就会放到数据模型中。
Map 及 Model:入参为 org.springframework.ui.Model、org.springframework.uiModelMap 或 java.util.Map 时,处理方法返回时,Map 中的数据会自动添加到模型中。
@SessionAttributes:将模型中的某个属性暂存到 HttpSession 中,以便多个请求之间可以共享这个属性。

1、Map 和 Model 入参
     //当参数为 Map 时 SpirngMVC 会传入 一个 BindingAwareModelMap//往BindingAwareModelMap 里面存入的值 会在后面存入 request 域中//相当于在方法返回前执行了一个 request.setAttribute 的操作@RequestMapping("/map.html")public String map(Map<String, Object> map) {System.out.println(map.getClass().getName());map.put("name", "aaa");map.put("id", 123);return "/model.jsp";}// 参数为Model类型的,作用和Map一样@RequestMapping("/model.html")public String model(Model model) {model.addAttribute("id", 123);model.addAttribute("name", "aaa");return "/model.jsp";}

测试页面:

name=${name}<br/>
id=${id}<br/>

运行结果:
访问 map.html 或 model.html 的时候,页面上显示:
name=aaa
id=123

2、ModelAndView

ModelAndView 既包含数据模型,又包含视图信息

    @RequestMapping("/modelandview.html")public ModelAndView testModeAndView() {ModelAndView modelAndView = new ModelAndView();//将 Model 数据作为 request.attribute Foward 到下一个页面。modelAndView.addObject("id", 123);modelAndView.addObject("name", "abc");modelAndView.setViewName("/model.jsp");//设置要返回的页面return modelAndView;}
3、@SessionAttributes

若希望在多个请求之间共用某个模型属性数据,则可以在控制器类上标注一个@SessionAttributes,SpringMVC 将在模型中对应的属性暂存到 HttpSession 中。

@SessionAttributes 只能标注在类上。

@SessionAttributes 除了可以通过属性名指定需要放到会话中的属性外,还可以通过模型属性的对象类型指定哪些模型属性需要放到会话中

@SessionAttributes(types=Dept.class) 会将隐含模型中所有类型为 Dept.class 的属性添加到session 中。

@SessionAttributes(value={“user”,”admin”})会将模型中名为 user 和 admin 的属性添加到session 中

@SessionAttributes(types={Dept.class, Employee.class})会将模型中所有类型为 Dept 和Employee 的属性添加到 session 中

@SessionAttributes(value={“user”,”admin”}, types={Dept.class})会将模型中名为 user 和 admin和类型为 Dept 的对象放到 session 中。

在类上添加 @SessionAttributes 注解

    @SessionAttributes(types = Dept.class, value = {"user", "admin"})@Controllerpublic class ModelController {测试方法:@RequestMapping("/session.html")public ModelAndView testSettion(Map<String, Object> map) {map.put("admin", "I am admin");map.put("user", "I am user");Dept dept = new Dept();dept.setName("session name");map.put("dept", dept);//@SessionAttributes 注解里没有声明 other 这个属性,所以不会在 session 中map.put("other", "I'm other");return new ModelAndView("/model.jsp", "result", map);}

测试页面:

request 中的属性:<br/>
admin:${requestScope.admin}<br/>
user:${requestScope.user}<br/>
dept.name:${requestScope.dept.name}<br/>
other:${requestScope.other}<br/>
session 中的属性:<br/>
admin:${sessionScope.admin}<br/>
user:${sessionScope.user}<br/>
dept.name:${sessionScope.dept.name}<br/>
other:${sessionScope.other}<br/>

运行效果:
在这里插入图片描述
可以看到模型中的属性都放到了 request 的域中。@SessionAttributes 中没有声明 other,所以 session 中的 other 是空的。
直接注解到类上管理不方便,可以直接使用session类型的入参。

4、@ModelAttribute

方法参数上使用@ ModelAttribute
在参数前使用@ModelAttribute,在进去方法时可以通过参数给对象赋值,如下面的代码,当请求/model1.html?id=1 的时候,会给 dept 的 id 属性赋值。在方法中可以对 dept 做进一步的处理。@ModelAttribute 可以自动将被注解的对象作为数据模型返回给页面。
注意:@ModelAttribute中会自动将对象类名小写做为入参绑定,可在@ModelAttribute(name=“dept1”)中指定修改入参绑定名,和形参名没有半毛钱关系

    @RequestMapping("/model1.html")public String testModelAttribute(@ModelAttribute Dept dept) {dept.setId(123);dept.setName("test");//使用@ModelAttribute 注解 dept//相当于执行了 request.setAttribute("dept",dept);//页面上可以直接取数据return "/model.jsp";}

测试页面 model.jsp,使用 EL 表达式取值 :

<body>
dept.id=${dept.id}<br/>
dept.name=${dept.name}
</body>

运行结果
dept.id=123
dept.name=test

定义方法时使用@ ModelAttribute
在方法上使用@ModelAttribute 后,给对象提供默认值,执行这个 Controller 的任意一个方法之前,都会调用这个方法给对象赋值。

    //在方法上使用@ModelAttribute,调用这个 Controller 任意一个方法之前//都会执行这个方法给模型赋值@ModelAttribute("dept")public Dept getDept() {Dept dept = new Dept();dept.setId(456);dept.setName("name");return dept;}//在调用这个方法前,会执行 getDept()//如果请求中有参数,会覆盖掉 getDept()的值//dept 会作为数据模型返回到页面上@RequestMapping("/model2.html")public String testModelAttribute2(@ModelAttribute Dept dept) {System.out.println(dept.getId());System.out.println(dept.getName());return "/model.jsp";}

六、视图和视图解析器

对于Controller的目标方法,无论其返回值是String、View、ModelMap或是ModelAndView,SpringMVC 都会在内部将它们封装为一个 ModelAndView 对象进行返回。

Spring MVC 借助视图解析器(ViewResolver)得到最终的视图对象(View),最终的视图可以是 JSP 也可是 Excell、JFreeChart 等各种表现形式的视图。

View —View 接口表示一个响应给用户的视图,例如 jsp 文件,pdf 文件,html 文件等。

视图的作用是渲染模型数据,将模型里的数据以某种形式呈现给客户。

为了实现视图模型和具体实现技术的解耦,Spring 在 org.springframework.web.servlet 包中定义了一个高度抽象的 View 接口。
视图对象由视图解析器负责实例化。由于视图是无状态的,所以他们不会有线程安全的问题。所谓视图是无状态的,是指对于每一个请求,都会创建一个 View 对象。
JSP 是最常见的视图技术。

1、ViewResolver

ViewResolver 的主要作用是把一个逻辑上的视图名称解析为一个真正的视图,SpringMVC中用于把 View 对象呈现给客户端的是 View 对象本身,而 ViewResolver 只是把逻辑视图名称解析为对象的 View 对象。

InternalResourceViewResolver

InternalResourceViewResolver 可以在视图名称前自动加前缀或后缀:

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

如果配置了上面的解析器,Controller中返回字符串就不需要写/index.jsp了,直接返回“index”,就会按照/index.jsp 去解析

MappingJackson2JsonView
用于返回json类型的数据,用法见十一。

FreeMarkViewResolver
FreeMaker英文官方参考手册:https://freemarker.apache.org/
FreeMaker中文参考手册:http://freemarker.foofun.cn/

FreeMarker 与 spring 整合需要导入 jar:

<dependency><groupId>org.freemarker</groupId><artifactId>freemarker</artifactId><version>2.3.23</version>
</dependency>
<dependency><groupId>org.springframework</groupId><artifactId>spring-context-support</artifactId><version>4.3.11.RELEASE</version>
</dependency>

使用 FreeMarker 模板生成静态网页,需要在 springMVC-servlet.xml 中配置:

<bean id="freemarkerConfig"class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer"><!--模板存放路径--><property name="templateLoaderPath" value="/WEB-INF/ftl/" /><property name="defaultEncoding" value="UTF-8" />
</bean>

在 WEB-INF/ftl 下创建模板文件 hello.ftl:

<html>
<head><title>Title</title>
</head>
<body><h1>${hello}</h1>
</body>

通过模板生成静态网页:

package controller;import freemarker.template.Configuration;
import freemarker.template.Template;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;
import java.io.File;
import java.io.FileWriter;
import java.io.Writer;
import java.util.HashMap;
import java.util.Map;@Controller
public class FreeMarkerController {@Autowiredprivate FreeMarkerConfigurer freeMarkerConfigurer;@RequestMapping("/freemarker.html")@ResponseBodypublic String genHtml() throws Exception {// 1、从 spring 容器中获得 FreeMarkerConfigurer 对象。 // 2、从 FreeMarkerConfigurer 对象中获得 Configuration 对象。 Configuration configuration = freeMarkerConfigurer.getConfiguration();// 3、使用 Configuration 对象获得 Template 对象。 Template template = configuration.getTemplate("hello.ftl");// 4、创建数据集 Map dataModel = new HashMap<>();dataModel.put("hello", "1000");// 5、创建输出文件的 Writer 对象。 Writer out = new FileWriter(new File("F:/spring-freemarker.html"));// 6、调用模板对象的 process 方法,生成文件。 template.process(dataModel, out);// 7、关闭流。 out.close();return "OK";}
}

在 F 盘下就能看到 spring-freemark.html 文件了
以上是生成静态网页的配置。
如果想像读取 jsp 一样动态展示 freeMarker 的页面,可以配置视图解析器:

<bean id="viewResolverFtl"class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver"><property name="viewClass"value="org.springframework.web.servlet.view.freemarker.FreeMarkerView"/><property name="contentType" value="text/html; charset=utf-8"/><property name="cache" value="true" /><property name="suffix" value=".ftl" /><property name="order" value="0"/>
</bean>

order 越小,视图解析器的优先级就越高。

    @RequestMapping("/hellofm.html")public String sayHello(ModelMap map) {//传递属性到页面 map.addAttribute("hello", " Hello FreeMarker!");return "/hello";//去找 hello.ftl }

运行结果:
在这里插入图片描述
BeanNameViewResolver
引入 servlet 的 jar:

<!--servlet 依赖 jar 包-->
<dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>3.1.0</version>
</dependency>

自定义一个视图,然后声明成 bean,Controller 中返回这个 bean 的名字,就可以显示当前的视图:

package view;
import org.springframework.web.servlet.View;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;public class HelloView implements View {public String getContentType() {return "text/html";}public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {//向相应中写入数据 response.getWriter().print("Welcome to View:Hello");}
}

springMVC-servlet.xml 中配置:

<bean class="org.springframework.web.servlet.view.BeanNameViewResolver"><property name="order" value="10"/><!--优先级靠后-->
</bean>
<bean id="helloView" class="view.HelloView"/>

Controller

    @RequestMapping("helloview.html")public String hello() {//因为当前没有 helloView.jsp //所以视图解析器依次执行,找到 id=helloView 的视图并显示 return "helloView";}

运行结果:
在这里插入图片描述

2、自定义 View

处理 json 数据需要 json 的 jar 包

<!-- Jackson Json 处理工具包 -->
<dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.7.4</version>
</dependency>
package view;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.web.servlet.view.AbstractView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Map;public class JsonView extends AbstractView {//该View对应的输出类型@Overridepublic String getContentType() {return "application/json; charset=UTF-8";}//向响应中写入数据 @Overrideprotected void renderMergedOutputModel(Map<String, Object> model,HttpServletRequest request, HttpServletResponse response)throws Exception {ObjectMapper mapper = new ObjectMapper();//设置 Date 类型的格式,默认是显示毫秒数 mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));//需要注意的是放入 model 的对象一定要实现 Serializable 接口才能转化成 json String jsonStr = mapper.writeValueAsString(model);response.setContentType(getContentType());response.setHeader("Cache-Control", "no-cache");response.setCharacterEncoding("UTF-8");PrintWriter out = null;try {out = response.getWriter();out.print(jsonStr);out.flush();} catch (IOException e) {} finally {if (out != null) {out.close();out = null;}}}
}

测试代码:

    @RequestMapping("myview.html")public ModelAndView myView() {Map<String, Object> result = new HashMap<>();result.put("key1", "123");result.put("key2", new String[]{"a", "b"});result.put("key3", new Date());return new ModelAndView(new JsonView(), result);}

如果是 map 中的值是其它对象类型的,传给 ModelAndView 的数据必须有一个 modelName

3、转发和重定向
    public String showView2() {//转发前面加 forward:return "index.html";}@RequestMapping("/redirect.html")public String showView3() {//重定向前面加 redirect:return "redirect:index.html";}

七、@ReponseBody

该注解用于将 Controller 的方法返回的对象,通过适当的 HttpMessageConverter 转换为指定格式后,写入到 Response 对象的 body 数据区。使用时机:返回的数据不是 html 标签的页面,而是其他某种格式的数据时(如 json、xml 等)使用。

1、返回 json 数据

处理 json 数据需要 json 的 jar 包

<!-- Jackson Json 处理工具包 -->
<dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.7.4</version>
</dependency>

返回 json 类型的数据,需要在 spring 配置文件中加入如下配置:

<!--配置返回值转换器-->
<bean id="contentNegotiationManagerFactoryBean"class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean"><!--是否支持后缀匹配--><property name="favorPathExtension" value="true"/><!--是否支持参数匹配--><property name="favorParameter" value="true"/><!--是否 accept-header 匹配--><property name="ignoreAcceptHeader" value="false"/><property name="mediaTypes"><map><!--表示.json 结尾的请求返回 json--><entry key="json" value="application/json"/><!--表示.xml 结尾的返回 xml--><entry key="xml" value="application/xml"/></map></property>
</bean>

测试 favorPathExtension 请求后缀分别是.xml 和.json
测试 favorParameter 请求中参数 format=json 和 format=xml
测试 ignoreAcceptHeader,请求的 Header 中 Accept=application/json 或 Accept=application/xml

如果要返回 Xml,需要将要转换为 xml 的实体类上添加注解,如:

@XmlRootElement
public class Dept {

<mvc:annotation-driven/>标签中指定 content-negotation-manager

<mvc:annotation-driven content-negotiation-manager="contentNegotiationManagerFactoryBean"/>

测试类:

package controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import pojo.Dept;@Controller
@RequestMapping("/json")
public class JsonController {@RequestMapping("/get ")@ResponseBody//会自动将返回值转换成 jsonpublic Dept getJson() {Dept dept = new Dept();dept.setId(1);dept.setName("张三");return dept;}
}

测试结果:
在这里插入图片描述
在这里插入图片描述

2、实现 RESTFUL
package controller;
import org.springframework.web.bind.annotation.*;
import pojo.Dept;@RestController//相当于本类中所有的方法都加了@ResponseBody
@RequestMapping("/rest")
public class RestTestController {//通过 method 限制请求的方式@RequestMapping(value = "/{id}", method = RequestMethod.GET)public Dept getDept(@PathVariable Integer id) {//模拟从数据库中查出一条数据Dept dept = new Dept();dept.setId(id);dept.setName("张三");return dept;}@RequestMapping(method = RequestMethod.POST)public Dept addDept(Dept dept) {//模拟插入数据后生成主键dept.setId(1);System.out.println(dept.getName());return dept;}@RequestMapping(method = RequestMethod.PUT, consumes = "application/json")public Dept updateDept(@RequestBody Dept dept) {System.out.println(dept.getName());//执行修改的业务略dept.setName("修改");//模拟修改名字return dept;}@RequestMapping(value = "/{id}", method = RequestMethod.DELETE)public String deleteDept(@PathVariable Integer id) {//执行删除的业务略System.out.println(id);return "删除成功";}
}

通过 postman 可以测试请求(ajax 方式无法测试 PUT 和 DELETE)。
测试 put 的时候,请求的 body 设置为 raw,Headers 的 ContentType=application/json,否则会报415:
在这里插入图片描述
注意类名上方的@RestController,相当于在类中每个方法上都添加了@ReponseBody。
deleteDept 方法返回了一句 String 类型的提示信息,默认的 String 类型的返回值,编码是ISO-8859-1,中文会乱码,解决方案是在配置文件中修改编码:
修改<mvc:annotation-driven>节点,添加<mvc:message-converters>

<mvc:annotation-drivencontent-negotiation-manager="contentNegotiationManagerFactoryBean"><!--String 返回值默认编码是 ISO-8859-1,需要--><mvc:message-converters><bean class="org.springframework.http.converter.StringHttpMessageConverter"><constructor-arg value="UTF-8" /></bean></mvc:message-converters>
</mvc:annotation-driven>

八、HttpEntity

HttpEntity 和@RequestBody 和@ResponseBody 类似,除了可以得到 request 和 response的 body 以外,还可以操作 header。

    @RequestMapping("/entity.html")public ResponseEntity<Dept> getEntity(RequestEntity<Dept> requestEntity) {//获取请求头String requestHeader = requestEntity.getHeaders().getFirst("MyRequestHeader");System.out.println(requestHeader);Dept dept = new Dept();dept.setId(1);dept.setName("张三");HttpHeaders responseHeaders = new HttpHeaders();//创建响应头responseHeaders.set("MyResponseHeader", "MyValue");//自定义响应头//响应对象ResponseEntity<Dept> responseEntity =new ResponseEntity<>(dept, responseHeaders, HttpStatus.OK);return responseEntity;}

测试的页面:

<input type="button" onclick="testEntity()" value="测试 HttpEntity"/>
<script type="text/javascript">function testEntity(){$.ajax({type: "POST",url: "/json/entity.html",headers:{"MyRequestHeader":"abc"},success: function(data, status, xhr){//xhr 可以看到响应的头alert(data.id);alert(status);alert("Header="+xhr.getResponseHeader("MyResponseHeader"));},error: function(data, status, xhr){alert(data.id);alert(status);alert("Header="+xhr.getResponseHeader("MyResponseHeader"));}});}
</script>

九、文件上传

SpringMVC 的文件上传非常简便,首先导入文件上传依赖的 jar:

<!-- 文件上传所依赖的 jar 包 -->
<dependency><groupId>commons-fileupload</groupId><artifactId>commons-fileupload</artifactId><version>1.3.1</version>
</dependency>

在 springMVC-servlet.xml 配置文件中配置文件解析器:

<!--1*1024*1024 即 1M resolveLazily 属性启用是为了推迟文件解析,以便捕获文件大小异常-->
<!--文件上传解析器-->
<bean id="multipartResolver"class="org.springframework.web.multipart.commons.CommonsMultipartResolver"><property name="maxUploadSize" value="1048576"/><property name="defaultEncoding" value="UTF-8"/><property name="resolveLazily" value="true"/>
</bean>

注意解析器的 id 必须等于 multipartResolver,否则上传会出现异常:
在这里插入图片描述

1、单个文件上传
package controller;
import org.apache.commons.io.FileUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.commons.CommonsMultipartFile;
import java.io.File;@Controller
public class FileController {//上传单个文件操作//MultipartFile file 就是上传的文件@RequestMapping(value = "/upload1.html")public String fileUpload1(@RequestParam("file") MultipartFile file) {try {//将上传的文件存在 E:/upload/下FileUtils.copyInputStreamToFile(file.getInputStream(),new File("E:/upload/",file.getOriginalFilename()));} catch (Exception e) {e.printStackTrace();}//上传成功返回原来页面return "/file.jsp";}
}

上传文件时,Controller 的方法中参数类型是 MultipartFile 即可将文件映射到参数上。
页面:
file.jsp:

<form method="post" action="/upload1.html" enctype="multipart/form-data"><input type="file" name="file"/><button type="submit" >提交</button>
</form>

另外上传的文件还可以映射成 CommonsMultipartFile,它是 MultipartFile 的子类:

    //CommonsMultipartFile 是 MultipartFile 的子类@RequestMapping("/upload2.html")public String fileUpload2(@RequestParam("file") CommonsMultipartFile file) {try {System.out.println("fileName:" + file.getOriginalFilename());String path = "E:/upload/" + file.getOriginalFilename();File newFile = new File(path);//通过 CommonsMultipartFile 的方法直接写文件file.transferTo(newFile);} catch (Exception e) {e.printStackTrace();}return "/file.jsp";}

页面:

<form method="post" action="/upload2.html" enctype="multipart/form-data"><input type="file" name="file"/><button type="submit" >提交</button>
</form>
2、批量上传

批量上传文件的时候,把方法中的参数类型写成数组即可:

    //批量上传的时候参数是数组@RequestMapping("/upload3.html")public String fileUpload3(@RequestParam("file") CommonsMultipartFile[] file) {try {//批量上传时遍历文件数组for (CommonsMultipartFile f : file) {System.out.println("fileName:" + f.getOriginalFilename());String path = "E:/upload/" + f.getOriginalFilename();File newFile = new File(path);f.transferTo(newFile);}} catch (Exception e) {e.printStackTrace();}return "/file.jsp";}

页面:

<form method="post" action="/upload3.html" enctype="multipart/form-data"><!--注意 name 都是 file,与 Controller 中方法的参数名对应--><input type="file" name="file"/><input type="file" name="file"/><input type="file" name="file"/><button type="submit" >提交</button>
</form>

十、使用拦截器

模拟登陆拦截器:

package interceptor;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;public class LoginInterceptor extends HandlerInterceptorAdapter {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response,Object handler) throws Exception {System.out.println("开始拦截");// 其他情况判断 session 中是否有 key,有的话继续用户的操作 if (request.getSession().getAttribute("user") != null) {return true;}// 最后的情况就是进入登录页面 response.sendRedirect(request.getContextPath() + "/login.jsp");return false;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response,Object handler, ModelAndView modelAndView) throws Exception {System.out.println("视图解析前 postHandle");}@Overridepublic void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("处理异步请求");}
}

配置拦截器:

<mvc:interceptors><mvc:interceptor><!--配置局部拦截器,需要满足下列路径条件--><mvc:mapping path="/**"/><mvc:exclude-mapping path="/login.html" /><bean class="interceptor.LoginInterceptor"/><!--自定义拦截器注册--></mvc:interceptor>
</mvc:interceptors>

登陆的 Controller

package controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;@Controller
public class LoginController {@RequestMapping("login.html")public String login(String username, HttpServletRequest request) {//模拟登陆 request.getSession().setAttribute("user", username);return "redirect:index.html";}
}

登陆页面:

<form action="/login.html"><input name="username"/><input type="submit">
</form>

十一、 异常处理

1、集成异常处理

对 于 一 些 通 用 的 , 不 需 要 特 殊 处 理 的 异 常 , 可以使用统一的 异 常 处 理 器 , 在springMVC-servlet.xml 中加入配置:

<!--集成异常处理-->
<beanclass="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"><!-- 定义默认的异常处理页面--><property name="defaultErrorView" value="error.jsp"></property><!-- 定义异常处理页面用来获取异常信息的变量名,默认名为 exception --><property name="exceptionAttribute" value="ex"></property><!-- 定义需要特殊处理的异常,用类名或完全路径名作为 key,异常也页名作为值 --><property name="exceptionMappings"><props><prop key="java.lang.NullPointerException">error.jsp</prop><prop key="java.lang.ClassCastException">error.jsp</prop><prop key="java.lang.IndexOutOfBoundsException">error.jsp</prop><!-- 这里还可以继续扩展对不同异常类型的处理 --></props></property>
</bean>

我们写一个 controller 专门抛出异常,用来模拟程序中可能出现的异常信息:

package controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;@Controller
public class ExceptionController {@RequestMapping("/ex.html")public String exceptionTest(Integer type) throws Exception {//手动抛出几个异常,模拟程序中可能出现的异常switch (type) {case 1:throw new NullPointerException("测试空指针异常");case 2:throw new ClassCastException("测试类型转换异常");case 3:throw new IndexOutOfBoundsException("测试越界异常");}return "index.jsp";}
}

测试页面:

<body><a href="/ex.html?type=1">空指针</a><br/><a href="/ex.html?type=2">类型转换</a><br/><a href="/ex.html?type=3">越界</a><br/>${ex}<!--就是 spring 异常处理器中配置的 exceptionAttribute-->
</body>

运行结果:
点击不同的链接可以看大不同的提示信息。

2、自定义异常处理

对于需要特殊处理的异常,可以自定义异常处理器
自定义异常处理的类需要继承 HandlerExceptionResolver

package exception;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;public class MyExceptionHandler implements HandlerExceptionResolver {@Overridepublic ModelAndView resolveException(HttpServletRequest httpServletRequest,HttpServletResponse httpServletResponse, Object o, Exception e) {String msg = e.getMessage();httpServletRequest.setAttribute("ex", msg);return new ModelAndView("/error.jsp");}
}

在 spring 配置文件中定义 bean:

<!--自定义异常处理-->
<bean id="exceptionHandler" class="exception.MyExceptionHandler"/>

注:自定义异常处理和 11.1 中的集成异常处理不能一起使用,需要将 11.1 中的 bean 注释
掉。
如果需要处理 ajax 发送的请求,出现异常时返回 json 数据,可以增加处理 json 的代码。
修改 MyExceptionHandler 中的代码:

package exception;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.json.MappingJackson2JsonView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;public class MyExceptionHandler implements HandlerExceptionResolver {@Overridepublic ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {String msg = e.getMessage();if (httpServletResponse.isCommitted()) {return null;}//如果是 ajax 请求就返回 json 数据if (isAjax(httpServletRequest)) {Map<String, String> result = new HashMap<>();result.put("ex", msg);MappingJackson2JsonView view = new MappingJackson2JsonView();return new ModelAndView(view, "result", result);} else {//不是 ajax 就返回错误页面httpServletRequest.setAttribute("ex", msg);return new ModelAndView("/error.jsp");}}public static boolean isAjax(HttpServletRequest request) {return "XMLHttpRequest".equalsIgnoreCase(request.getHeader("X-Requested-With")) || request.getParameter("ajax") != null;}
}

测试页面:

<script type="text/javascript" src="jquery-1.10.1.min.js"></script> <input type="button" onclick="testEx()" value="测试自定义异常"/>
<script type="text/javascript">function testEx(){$.ajax({type: "POST",url: "/ex.html?type=1",success: function(data){alert(data.result.ex);//读取 json 数据}});}
</script>

测试结果:
在这里插入图片描述

十二、 数据校验、数据格式化

在这里插入图片描述

1、数据校验

使用 spring 数据校验,先要导入校验器的 jar:

<!--数据校验-->
<dependency><groupId>org.hibernate.validator</groupId><artifactId>hibernate-validator</artifactId><version>6.0.2.Final</version>
</dependency>

此处使用的 hibernate 校验器

JSR 规范:
在实体类的属性上添加注解,可以完成数据校验:
@Null 被注释的元素必须为 null
@NotNull 被注释的元素必须不为 null
@AssertTrue 被注释的元素必须为 true
@AssertFalse 被注释的元素必须为 false
@Min(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@Max(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@DecimalMin(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@DecimalMax(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@Size(max=, min=) 被注释的元素的大小必须在指定的范围内
@Digits (integer, fraction) 被注释的元素必须是一个数字,其值必须在可接受的范围内
@Past 被注释的元素必须是一个过去的日期
@Future 被注释的元素必须是一个将来的日期
@Pattern(regex=,flag=) 被注释的元素必须符合指定的正则表达式

Hibernate Validator 附加的注解
@NotBlank(message =) 验证字符串非 null,且长度必须大于 0
@Email 被注释的元素必须是电子邮箱地址
@Length(min=,max=) 被注释的字符串的大小必须在指定的范围内
@NotEmpty 被注释的字符串的必须非空
@Range(min=,max=,message=) 被注释的元素必须在合适的范围内
如 Employee 实体类中:

public class Employee {private Integer id;@NotEmpty(message = "用户名不能为空")@Size(min = 3, max = 6, message = "姓名长度应在{min}-{max}")private String name;@Min(value = 2700, message = "工资不能少于{value}")@Max(value = 10000, message = "工资不能超过{value}")private Float salary; 

在 springMVC-servlet.xml 中配置校验器:

<!-- 配置校验器 -->
<bean id="validator"class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"><!-- 校验器,使用 hibernate 校验器 --><property name="providerClass" value="org.hibernate.validator.HibernateValidator"/>
</bean>

简单的数据校验

新建 ValidateController

package controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import pojo.Employee;
import java.security.NoSuchAlgorithmException;@Controller
public class ValidateController {@RequestMapping("/val1.html")public ModelAndView validate1(@Validated Employee emp, BindingResult result) {if (result.hasErrors()) {//如果验证错误 FieldError nameError = result.getFieldError("name");FieldError salaryError = result.getFieldError("salary");ModelAndView view = new ModelAndView();view.setViewName("/validate.jsp");//如果有错就返回原页面if (nameError != null) {view.addObject("nameError", nameError.getDefaultMessage());}if (salaryError != null) {view.addObject("salaryError", salaryError.getDefaultMessage());}return view;}//验证成功去首页 return new ModelAndView("/index.jsp");}
}

@Validated 修饰的参数会被按照规则校验。BindingResult 会存放校验信息。
在需要校验的 pojo 前边添加@Validated,在需要校验的 pojo 后边添加 BindingResult bindingResult 接收校验出错信息
注意:@Validated 和 BindingResult bindingResult 是配对出现,并且形参顺序是固定的(一前一后)

页面 validate.jsp

<form action="/val1.html" method="post">name:<input type="text" name="name"/>${nameError}<br/>salary:<input type="text" name="salary"/>${salaryError}<br/><input type="submit" value="提交"/>
</form>

运行结果:
在这里插入图片描述
提交后:
在这里插入图片描述
使用@ModelAttribute 和< form:>
spring 有自定义的表单标签,<form:>冒号后面的是生成 html 的标签名,如<form:input>就会
生成一个<input>

<form:form modelAttribute="empModel" method="post" action="/val2.html">name:<form:input path="name" /><br/><!--输出 name 的校验信息--><form:errors path="name"></form:errors><br/>salary:<form:input path="salary" /><br/><form:errors path="salary"></form:errors><br/><input type="submit" value="Submit" /><br/><!--输出所有错误信息--><form:errors path="*"></form:errors>
</form:form>

使用这个标签需要在 jsp 页面中引入头文件:

<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>

form:标签将的 modelAttribute 与 action 对应的方法上@ModelAttribute 修饰的对象对应。
要想进入这个页面,首先要经过一个 Controller 的方法,在 Model 中添加对应的属性:

    @RequestMapping("/goVal2.html")public String goVal2(Model model) {if (!model.containsAttribute("empModel")) { //empModel 与页面中的<form:form modelAttribute="empModel">对应 model.addAttribute("empModel", new Employee());}return "/validate.jsp";}

如果不经过 Controller 或者 Controller 中没有放 empModel 这个属性,那么页面就会报错:
在这里插入图片描述
提交表单验证的方法:

    @RequestMapping("/val2.html")public String test(@Validated @ModelAttribute("empModel") Employee emp,BindingResult result, Model model) {//如果有验证错误 返回到 form 页面 if (result.hasErrors()) {//经过 goVal2 方法的目的是为了设置 empModel 属性 //如果 empModel 属性没有设置,页面就报错了 return goVal2(model);}return "/index.jsp";}

数据校验信息国际化

国际化就是根据浏览器默认语言的不同,显示不同的提示信息:
在 Employee 中,给 id 字段添加验证信息:

public class Employee {@NotNull(message="{NotNull.emp.id}")private Integer id;

{NotNull.emp.Id}是从 properties 文件中读取属性值
在 resources 目录下创建两个文件,一个用来存放中文提示信息,一个存放英文提示信息:
注意文件的命名,xx.properties 对应的英文配置文件是 xx_en_US.properties
i18n.properties

NotNull.emp.id=id 不能为空

i18n_en_US.properties

NotNull.emp.id=userId can not be null

两个文件的 key 是对应的,值是不同的语言
springMVC-servlet.xml 中配置,其中 id=validator 的 bean 前面已经配置过了,这里再添加一
个属性即可

<bean id="validator"class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"><!-- 校验器,使用 hibernate 校验器 --><property name="providerClass"value="org.hibernate.validator.HibernateValidator"/><!--这里添加一个校验信息的数据源--><property name="validationMessageSource" ref="messageSource" />
</bean>
<!--自动装配校验器--> <mvc:annotation-driven validator="validator"/>
<bean id="messageSource"class="org.springframework.context.support.ReloadableResourceBundleMessageSource"><property name="basenames"><list><!-- 在 web 环境中一定要定位到 classpath 否则默认到当前 web 应用下找 --><value>classpath:i18n</value><value>classpath:org/hibernate/validator/ValidationMessages</value></list></property>
</bean>

使用 12.1.2 节中的测试代码,运行结果:
在这里插入图片描述
将浏览器语言切换成英文,刷新页面:
在这里插入图片描述

2、数据格式化

使用注解格式化
springMVC 在映射 Date 类型的属性时会报错:
如果属性是封装在实体类中的,可以使用@DateTimeFormat 注解,如 Employee 中的 hireDate
属性。
@DateTimeFormat(pattern = “yyyy-MM-dd”)
private Date hireDate;

initBinder 实现格式化
@DateTimeFormat 是在实体类中格式化日期类型的属性,所有的 Controller 中用到该实
体类都会自动使用注解定义的格式是格式化数据。除此之外,还可以使用@InitBinder 在
Controller 中自定义格式化:

package controller;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.ServletRequestDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.RequestMapping;
import java.text.SimpleDateFormat;
import java.util.Date;@Controller
public class DateFormatController {//自定义格式化 @InitBinderpublic void initBinder(ServletRequestDataBinder binder) {SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");binder.registerCustomEditor(Date.class,new CustomDateEditor(dateFormat, true));}@RequestMapping("/date.html")public String date(Date date) {System.out.println(date);return "/format.jsp";}
}

@InitBinder 定义的格式化规则对当前 Controller 有效。

自定义格式化
springMVC 有多种方式实现自定义数据格式化,假设现在输入一个电话号码,格式是
010-12345678,我们有一个实体类,把区号和电话号码分开:

package pojo;
public class PhoneNumModel { private String areaCode;//区号 private String phoneNumber;//电话号码 //getter/setter 方法略 }

当请求中传入一个 String 类型的参数“010-12345678”,通过 springMVC 的数据格式化,
可以把 String 转换成实体类 PhoneNumModel
第一种方式:定义一个转换工具类,继承 PropertyEditorSupport

package util;
import org.springframework.core.convert.converter.Converter;
import org.springframework.util.StringUtils;
import pojo.PhoneNumModel;
import java.util.regex.Matcher;
import java.util.regex.Pattern;public class PhoneNumConverter implements Converter<String, PhoneNumModel> {//正则表达式,定义数据规则 Pattern pattern = Pattern.compile("^(\\d{3,4})-(\\d{7,8})$");@Overridepublic PhoneNumModel convert(String s) {if (s == null || !StringUtils.hasLength(s)) {return null; //如果没值,设值为 null }Matcher matcher = pattern.matcher(s);if (matcher.matches()) {PhoneNumModel phoneNumber = new PhoneNumModel();phoneNumber.setAreaCode(matcher.group(1));phoneNumber.setPhoneNumber(matcher.group(2));return phoneNumber;} else {throw new IllegalArgumentException(String.format("类型转换失败,需要格式[010 - 12345678],但格式是[ % s]", s));}}
}

Controller 中使用@InitBinder 注册自定义转换器:

package controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
import pojo.PhoneNumModel;
import util.PhoneNumEditor;@Controller
public class MyBinderController {//自定义格式化 @InitBinderpublic void initBinder(WebDataBinder binder) {binder.registerCustomEditor(PhoneNumModel.class, new PhoneNumEditor());}@RequestMapping("/phone.html")//注意一定要写@RequestParam public ModelAndView phone(@RequestParam("phone") PhoneNumModel phone) {//绑定成功时可以看到输出 System.out.println(phone.getAreaCode());System.out.println(phone.getPhoneNumber());return new ModelAndView("/format.jsp", "phone", phone);}
}

测试分 url:
http://localhost:8080/phone.html?phone=010-1234567
通过@InitBinder 的方式,数据绑定规则只对当前 Controller 有效
在 Spring4.2 之后提出了一种新的转换方式,工具类实现 Converter 接口:

package util;
import org.springframework.core.convert.converter.Converter;
import org.springframework.util.StringUtils;
import pojo.PhoneNumModel;
import java.util.regex.Matcher;
import java.util.regex.Pattern;public class PhoneNumConverter implements Converter<String, PhoneNumModel> {//正则表达式,定义数据规则 Pattern pattern = Pattern.compile("^(\\d{3,4})-(\\d{7,8})$");@Overridepublic PhoneNumModel convert(String s) {if (s == null || !StringUtils.hasLength(s)) {return null; //如果没值,设值为 null }Matcher matcher = pattern.matcher(s);if (matcher.matches()) {PhoneNumModel phoneNumber = new PhoneNumModel();phoneNumber.setAreaCode(matcher.group(1));phoneNumber.setPhoneNumber(matcher.group(2));return phoneNumber;} else {throw new IllegalArgumentException(String.format("类型转换失败,需要格式[010 - 12345678],但格式是[ % s]", s));}}
}

在 springMVC 配置文件中注册自定义转换器:

<!-- 需要将转换器设置给注解驱动 -->
<mvc:annotation-driven conversion-service="conversionServiceFactoryBean"/>
<bean id="conversionServiceFactoryBean"class="org.springframework.context.support.ConversionServiceFactoryBean"><property name="converters"><set><bean class="util.PhoneNumConverter"></bean></set></property>
</bean>

Controller 中不需要写@InitBinder 了。这个配置对所有 Controller 都生效。

十三、其它注解

1、 @CookieValue
    @RequestMapping("cookie.html")public String cookie(@CookieValue(value = "JSESSIONID", defaultValue = "mysession") String jsessionId) {System.out.println(jsessionId);return "/index.jsp";}

@CookieValue 用于获取 cookie 信息。value 用于指定 cookie 的名字,defaultValue 是当对应的 cookie 为空时系统设置的默认值。required 设置为 true 表示必须。
上面的代码如果浏览器中没有 cookie,会输出 mysession。再次刷新页面,就会打印出当前的 jsesessionid,如:
在这里插入图片描述

2、@Value

@Value 可以实现从配置文件中读取数据并注册给 Controller
在 springMVC 配置文件种加载 properties 文件:

<context:property-placeholder location="classpath:*.properties"/>

测试代码,这里读取的是 12.1.3 中配置文件中的 key

    //从配置文件中读取属性 @Value("${NotNull.emp.id}")private String NOT_NULL_ID;@RequestMapping("value.html")public String value() {System.out.println(NOT_NULL_ID);return "/index.jsp";}

十四、数据绑定流程

在这里插入图片描述
1)ApplicationContext 初始化时建立所有 url 和 controller 类的对应关系(用 Map 保存);
2)根据请求 url 找到对应的 controller,并从 controller 中找到处理请求的方法
3)Spring MVC 主框架将 ServletRequest 对象及目标方法的入参实例传递给 WebDataBinderFactory 实例,以创建 DataBinder 实例对象
4)DataBinder 是数据绑定的核心部件,调用装配在 Spring MVC 上下文中的 ConversionService 组件进行数据类型转换、数据格式化工作。将 Servlet 中的请求信息填充到入参对象中
5)调用 Validator 组件对已经绑定了请求消息的入参对象进行数据合法性校验,并最终生成数据绑定结果 BindingData 对象
6)Spring MVC 抽取 BindingResult 中的入参对象和校验错误对象,将它们赋给处理方法的响应入参。

十五、核心组件

DispatcherServlet:作为前端控制器,整个流程控制的中心,控制其它组件执行,统一调度,降低组件之间的耦合性,提高每个组件的扩展性。
HandlerMapping:通过扩展处理器映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。
HandlAdapter:通过扩展处理器适配器,支持更多类型的处理器,调用处理器传递参数等工作!帮助 DiapatcherServlet 调用映射到请求的处理程序。
ViewResolver:通过扩展视图解析器,支持更多类型的视图解析,例如:jsp、freemarker、pdf、excel 等。
在这里插入图片描述

查看全文
如若内容造成侵权/违法违规/事实不符,请联系编程学习网邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

相关文章

  1. IndexError: invalid index of a 0-dim tensor. Use tensor.item() to convert a 0-dim tensor to a Python

    IndexError: invalid index of a 0-dim tensor. Use tensor.item() to convert a 0-dim tensor to a Python number错误分析 错误原因:pytorch版本不同导致 train_loss += loss.data[0]是是pytorch0.3.1版本代码,在0.5以上版本中就会报错 解决办法 train_loss += loss.data[0…...

    2024/4/15 4:43:05
  2. 二分答案C++

    现有苹果、香蕉、梨三种水果,其中苹果188个,香蕉152个,梨324个。现在要把这些水果分给9位同学,要求每位同学仅能分到一种水果,且每人分到的水果数量一致。请设计一个程序,找到每人最多能分到多少个水果。 代码如下 #include <bits/stdc++.h> using namespace std; …...

    2024/4/23 16:05:31
  3. vue开发简单图片浏览功能

    注: href=“javascript:void(0);” 这个的含义是,让超链接去执行一个js函数,而不是去跳转到一个地址,而void(0)表示一个空的方法,也就是不执行js函数。javascript:是伪协议,表示url的内容通过javascript执行。void(0)表示不作任何操作,这样会防止链接跳转到其他页面。这…...

    2024/5/4 12:18:22
  4. 对JUC的学习和理解(一):Lock、生产者和消费者问题、虚假唤醒、多线程8锁

    目录什么是JUC?通过写售票代码回顾Synchronized和认识LockSynchronizedLocksynchorized与Lock锁的区别Synochorized的生产者和消费者问题虚假唤醒问题Lock中的生产者和消费者问题深入理解Java锁什么是JUC?在Java 5.0时提供了 java.lang.concurrent 这个包,这个包简称JUC包,…...

    2024/5/4 0:31:48
  5. python中的for else

    https://blog.csdn.net/nyist327/article/details/47806549...

    2024/4/15 4:43:01
  6. LU分解解线性方程组python实现

    本人是湖南大学的一名力学学生,2020年上数值分析课时写下的作业,希望对大家有所帮助,一起进步。 1、下面是自己整理的LU解线性方程组的基本理论。2、接下来是python实现代码 import numpy as npdef LU(A):生成值全位0的U矩阵,和单位矩阵LL = np.eye(len(A))U = np.zeros(np…...

    2024/5/4 8:59:21
  7. JavaWeb相关知识简介

    1.什么是Web程序通过浏览器访问的程序2静态页面表现形式:不会自动更新所需技术:HTML,css3动态页面表现形式:根据程序,自动更新所需技术:HTML,css,数据库,Java,JavaScript4,手工编写第一个Javaweb程序(1)在webapps创建项目目录(2)编写index.jsp(直接copy一个HTML内…...

    2024/4/29 1:19:47
  8. jq选择器传变量给单选按钮

    var vl= 2$("input[name=fdCategory][value="+vl+"]").attr("checked",true);...

    2024/4/15 4:42:58
  9. 使用userdel命令删除Linux系统中用户

    这个命令比较简单,就是删除用户。命令格式如下: [root@localhost ~]# userdel [-r] 用户名 选项 说明-r 在删除用户的同时删除用户的家目录在删除用户的同时如果不删除用户的家目录,那么家目录就会变成没有属主和属组的目录,也就是垃圾文件。 前面我们说过,可以手动修改用…...

    2024/5/4 0:56:00
  10. 【行情】合并结束时的比特币价格:BTC / USD目标为10,500美元和11,000美元

    比特币价格在触及9.900美元的壁垒后进入9.600美元的进站。 随着交易量激增,合并最终可能让位于10.000美元以上的收益。 比特币价格在周末时段触及略低于$ 9.900的障碍后继续延长巩固期。很难维持在9800美元上方,相反,价格周一暴跌以测试9600美元的支撑位。尽管下跌,但比特币…...

    2024/5/7 2:52:21
  11. Web系统功能测试18个点总结【乐搏TestPRO】

    1、页面链接检查:每一个链接是否都有对应的页面,并且页面之间切换正确。2、相关性检查:功能相关性:删除/增加一项会不会对其它项产生影响,如果禅僧影响,这些影响是否都正确。如:增加某个数据记录后,如果该数据记录某个字段内容较长,是否会影响查询功能。数据相关性:如…...

    2024/5/1 15:43:40
  12. recycleview条目状态显示重复

    当我们通过用户交互改变recyclerview中的某个item的显示状态后,向下滑动会发现每间隔9个item就会出现同样的显示状态,但是我们明明没有对这个item进行操作,为什么会自动显示操作后的状态呢? 其实原因很简单,因为缓存问题 只要在相应的activity或者fragment添加下面一行代码…...

    2024/4/24 12:10:41
  13. 微信返利机器人开发制作

    微信返利机器人开发,业务需求根据用户发送商品,微信自动查询返利,并且发送给用户,随着微信的普及与用户日常使用增多,开发微信返利机器人,方便用户查找优惠商品省钱,实现一对一服务。 对此开发微信返利机器人实现用户查找商品获取优惠卷。 由于pc电脑版微信大多使用hook…...

    2024/4/24 12:10:40
  14. 使用eclipse开发Javaweb程序

    1.web项目的虚拟路径在eclipse中右击项目,选择properties,再选web project settings,看到的context root 就是虚拟路径2.jsp:java server page ,本质是一个servlet(一个Java类)目的是为了能在Java中使用HTML标签在服务器端执行3.jsp页面元素(1)静态内容:不更新的HTML内…...

    2024/5/7 7:33:05
  15. python 按照标点符号切分句子,去除标点符号,判断字符串是否包含中文

    最近在处理文本,发现切分句子,去除标点符号,判断字符串是否包含中文经常会用到,我这里分享一下我的代码:切分句子import re def split_sentences(line):line_split = re.split(r[。!;?,],line.strip())line_split = [line.strip() for line in line_split if line.str…...

    2024/5/4 1:06:46
  16. jvm你知道多少&你想学什么

    jvm你知道多少&你想学什么 首先大致介绍下jvm虚拟机栈    ----> 运行时数据区的一部分 JVM调优    ---->更好利用好JVM资源,达到最佳效果 垃圾回收机制    ---->OK JVM的应用    ---->调优,比较不同的JVM的差别 堆    ---->运行时数据区中的一部…...

    2024/4/24 12:10:40
  17. jsp的代码格式的基础知识

    1.jsp注释(1)HTML的注释: <!--注释内容-->,在浏览器查看源可见(2)jsp注释:<%--注释内容--%>,在浏览器查看源不可见(3)jsp脚本注释://注释内容/*注释内容*/,在浏览器查看源不可见2.jsp脚本代码格式语法:<%java代码%>注意:println在jsp中不是换行,…...

    2024/5/4 0:11:16
  18. java实现多级菜单(树状图)

    实体类:public class Menu {// 菜单idprivate String id;// 菜单名称private String name;// 父菜单idprivate String parentId;// 菜单urlprivate String url;// 菜单图标private String icon;// 菜单顺序private int order;// 子菜单private List<Menu> children;// .…...

    2024/5/4 8:23:41
  19. 乐鑫笔试题

    一、将一个串行执行的C语言算法转化为单拍完成的并行可综合verilog unsigned char cal_table_high_first(unsigned char value) {unsigned char i ; unsigned char checksum = value ; for (i=8;i>0;--i){if (check_sum & 0x80){check_sum = (check_sum<<1) ^ 0x…...

    2024/5/4 11:35:00
  20. UE4离远之后,远处的物体阴影消失了的解决方法

    选中产生阴影的光源, (我这里只用了一个主光源) 。 搜索 Shadow Bisa (阴影偏移) 修改数值。搜索 Shadow Filter Sharpen(阴影波纹过滤)简单的说这个相当于阴影的锐利程度在project settings - Engine - rendering ,勾选Generate Mesh Distance Fields,重启工程选中…...

    2024/4/24 12:10:34

最新文章

  1. 运行容器时发现内存不足(<2G)--docker版本低:重装docker

    一、卸载&#xff1a; sudo yum install -y yum-utilssudo yum remove docker-ce docker-ce-cli containerd.iosudo rm -rf /var/lib/dockersudo rm -rf /var/lib/containerd 二、安装&#xff1a; sudo yum-config-manager --add-repo https://download.docker.com/linux/ce…...

    2024/5/7 10:29:55
  2. 梯度消失和梯度爆炸的一些处理方法

    在这里是记录一下梯度消失或梯度爆炸的一些处理技巧。全当学习总结了如有错误还请留言&#xff0c;在此感激不尽。 权重和梯度的更新公式如下&#xff1a; w w − η ⋅ ∇ w w w - \eta \cdot \nabla w ww−η⋅∇w 个人通俗的理解梯度消失就是网络模型在反向求导的时候出…...

    2024/5/6 9:38:23
  3. uniapp原生下拉刷新在手机上不起作用

    开启原生下拉刷新时&#xff0c;页面里使用了全屏高的scroll-view&#xff0c;向下拖动内容时&#xff0c;会优先触发scroll-view滚动而不是下拉刷新。 "enablePullDownRefresh": true, 这就可能会导致下拉刷新不起作用&#xff0c;这时候就需要做到取舍&#xff0c…...

    2024/5/3 2:04:35
  4. 【嵌入式开发 Linux 常用命令系列 4.3 -- git add 不 add untracked file】

    请阅读【嵌入式开发学习必备专栏 】 文章目录 git add 不add untracked file git add 不add untracked file 如果你想要Git在执行git add .时不添加未跟踪的文件&#xff08;untracked files&#xff09;&#xff0c;你可以使用以下命令&#xff1a; git add -u这个命令只会加…...

    2024/5/5 8:53:25
  5. vue3项目运行正常但vscode红色波浪线报错

    以下解决办法如不生效&#xff0c;可尝试 重启 vscode 一、Vetur插件检测问题 vetur 是一个 vscode 插件&#xff0c;用于为 .vue 单文件组件提供代码高亮以及语法支持。但 vue 以及 vetur 对于 ts 的支持&#xff0c;并不友好。 1、原因 如下图&#xff1a;鼠标放到红色波浪…...

    2024/5/5 8:53:05
  6. 【外汇早评】美通胀数据走低,美元调整

    原标题:【外汇早评】美通胀数据走低,美元调整昨日美国方面公布了新一期的核心PCE物价指数数据,同比增长1.6%,低于前值和预期值的1.7%,距离美联储的通胀目标2%继续走低,通胀压力较低,且此前美国一季度GDP初值中的消费部分下滑明显,因此市场对美联储后续更可能降息的政策…...

    2024/5/7 5:50:09
  7. 【原油贵金属周评】原油多头拥挤,价格调整

    原标题:【原油贵金属周评】原油多头拥挤,价格调整本周国际劳动节,我们喜迎四天假期,但是整个金融市场确实流动性充沛,大事频发,各个商品波动剧烈。美国方面,在本周四凌晨公布5月份的利率决议和新闻发布会,维持联邦基金利率在2.25%-2.50%不变,符合市场预期。同时美联储…...

    2024/5/7 9:45:25
  8. 【外汇周评】靓丽非农不及疲软通胀影响

    原标题:【外汇周评】靓丽非农不及疲软通胀影响在刚结束的周五,美国方面公布了新一期的非农就业数据,大幅好于前值和预期,新增就业重新回到20万以上。具体数据: 美国4月非农就业人口变动 26.3万人,预期 19万人,前值 19.6万人。 美国4月失业率 3.6%,预期 3.8%,前值 3…...

    2024/5/4 23:54:56
  9. 【原油贵金属早评】库存继续增加,油价收跌

    原标题:【原油贵金属早评】库存继续增加,油价收跌周三清晨公布美国当周API原油库存数据,上周原油库存增加281万桶至4.692亿桶,增幅超过预期的74.4万桶。且有消息人士称,沙特阿美据悉将于6月向亚洲炼油厂额外出售更多原油,印度炼油商预计将每日获得至多20万桶的额外原油供…...

    2024/5/6 9:21:00
  10. 【外汇早评】日本央行会议纪要不改日元强势

    原标题:【外汇早评】日本央行会议纪要不改日元强势近两日日元大幅走强与近期市场风险情绪上升,避险资金回流日元有关,也与前一段时间的美日贸易谈判给日本缓冲期,日本方面对汇率问题也避免继续贬值有关。虽然今日早间日本央行公布的利率会议纪要仍然是支持宽松政策,但这符…...

    2024/5/4 23:54:56
  11. 【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响

    原标题:【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响近日伊朗局势升温,导致市场担忧影响原油供给,油价试图反弹。此时OPEC表态稳定市场。据消息人士透露,沙特6月石油出口料将低于700万桶/日,沙特已经收到石油消费国提出的6月份扩大出口的“适度要求”,沙特将满…...

    2024/5/4 23:55:05
  12. 【外汇早评】美欲与伊朗重谈协议

    原标题:【外汇早评】美欲与伊朗重谈协议美国对伊朗的制裁遭到伊朗的抗议,昨日伊朗方面提出将部分退出伊核协议。而此行为又遭到欧洲方面对伊朗的谴责和警告,伊朗外长昨日回应称,欧洲国家履行它们的义务,伊核协议就能保证存续。据传闻伊朗的导弹已经对准了以色列和美国的航…...

    2024/5/4 23:54:56
  13. 【原油贵金属早评】波动率飙升,市场情绪动荡

    原标题:【原油贵金属早评】波动率飙升,市场情绪动荡因中美贸易谈判不安情绪影响,金融市场各资产品种出现明显的波动。随着美国与中方开启第十一轮谈判之际,美国按照既定计划向中国2000亿商品征收25%的关税,市场情绪有所平复,已经开始接受这一事实。虽然波动率-恐慌指数VI…...

    2024/5/4 23:55:16
  14. 【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试

    原标题:【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试美国和伊朗的局势继续升温,市场风险情绪上升,避险黄金有向上突破阻力的迹象。原油方面稍显平稳,近期美国和OPEC加大供给及市场需求回落的影响,伊朗局势并未推升油价走强。近期中美贸易谈判摩擦再度升级,美国对中…...

    2024/5/4 23:54:56
  15. 【原油贵金属早评】市场情绪继续恶化,黄金上破

    原标题:【原油贵金属早评】市场情绪继续恶化,黄金上破周初中国针对于美国加征关税的进行的反制措施引发市场情绪的大幅波动,人民币汇率出现大幅的贬值动能,金融市场受到非常明显的冲击。尤其是波动率起来之后,对于股市的表现尤其不安。隔夜美国股市出现明显的下行走势,这…...

    2024/5/6 1:40:42
  16. 【外汇早评】美伊僵持,风险情绪继续升温

    原标题:【外汇早评】美伊僵持,风险情绪继续升温昨日沙特两艘油轮再次发生爆炸事件,导致波斯湾局势进一步恶化,市场担忧美伊可能会出现摩擦生火,避险品种获得支撑,黄金和日元大幅走强。美指受中美贸易问题影响而在低位震荡。继5月12日,四艘商船在阿联酋领海附近的阿曼湾、…...

    2024/5/4 23:54:56
  17. 【原油贵金属早评】贸易冲突导致需求低迷,油价弱势

    原标题:【原油贵金属早评】贸易冲突导致需求低迷,油价弱势近日虽然伊朗局势升温,中东地区几起油船被袭击事件影响,但油价并未走高,而是出于调整结构中。由于市场预期局势失控的可能性较低,而中美贸易问题导致的全球经济衰退风险更大,需求会持续低迷,因此油价调整压力较…...

    2024/5/4 23:55:17
  18. 氧生福地 玩美北湖(上)——为时光守候两千年

    原标题:氧生福地 玩美北湖(上)——为时光守候两千年一次说走就走的旅行,只有一张高铁票的距离~ 所以,湖南郴州,我来了~ 从广州南站出发,一个半小时就到达郴州西站了。在动车上,同时改票的南风兄和我居然被分到了一个车厢,所以一路非常愉快地聊了过来。 挺好,最起…...

    2024/5/7 9:26:26
  19. 氧生福地 玩美北湖(中)——永春梯田里的美与鲜

    原标题:氧生福地 玩美北湖(中)——永春梯田里的美与鲜一觉醒来,因为大家太爱“美”照,在柳毅山庄去寻找龙女而错过了早餐时间。近十点,向导坏坏还是带着饥肠辘辘的我们去吃郴州最富有盛名的“鱼头粉”。说这是“十二分推荐”,到郴州必吃的美食之一。 哇塞!那个味美香甜…...

    2024/5/4 23:54:56
  20. 氧生福地 玩美北湖(下)——奔跑吧骚年!

    原标题:氧生福地 玩美北湖(下)——奔跑吧骚年!让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 啊……啊……啊 两…...

    2024/5/4 23:55:06
  21. 扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!

    原标题:扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!扒开伪装医用面膜,翻六倍价格宰客!当行业里的某一品项火爆了,就会有很多商家蹭热度,装逼忽悠,最近火爆朋友圈的医用面膜,被沾上了污点,到底怎么回事呢? “比普通面膜安全、效果好!痘痘、痘印、敏感肌都能用…...

    2024/5/5 8:13:33
  22. 「发现」铁皮石斛仙草之神奇功效用于医用面膜

    原标题:「发现」铁皮石斛仙草之神奇功效用于医用面膜丽彦妆铁皮石斛医用面膜|石斛多糖无菌修护补水贴19大优势: 1、铁皮石斛:自唐宋以来,一直被列为皇室贡品,铁皮石斛生于海拔1600米的悬崖峭壁之上,繁殖力差,产量极低,所以古代仅供皇室、贵族享用 2、铁皮石斛自古民间…...

    2024/5/4 23:55:16
  23. 丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者

    原标题:丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者【公司简介】 广州华彬企业隶属香港华彬集团有限公司,专注美业21年,其旗下品牌: 「圣茵美」私密荷尔蒙抗衰,产后修复 「圣仪轩」私密荷尔蒙抗衰,产后修复 「花茵莳」私密荷尔蒙抗衰,产后修复 「丽彦妆」专注医学护…...

    2024/5/4 23:54:58
  24. 广州械字号面膜生产厂家OEM/ODM4项须知!

    原标题:广州械字号面膜生产厂家OEM/ODM4项须知!广州械字号面膜生产厂家OEM/ODM流程及注意事项解读: 械字号医用面膜,其实在我国并没有严格的定义,通常我们说的医美面膜指的应该是一种「医用敷料」,也就是说,医用面膜其实算作「医疗器械」的一种,又称「医用冷敷贴」。 …...

    2024/5/6 21:42:42
  25. 械字号医用眼膜缓解用眼过度到底有无作用?

    原标题:械字号医用眼膜缓解用眼过度到底有无作用?医用眼膜/械字号眼膜/医用冷敷眼贴 凝胶层为亲水高分子材料,含70%以上的水分。体表皮肤温度传导到本产品的凝胶层,热量被凝胶内水分子吸收,通过水分的蒸发带走大量的热量,可迅速地降低体表皮肤局部温度,减轻局部皮肤的灼…...

    2024/5/4 23:54:56
  26. 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...

    解析如下&#xff1a;1、长按电脑电源键直至关机&#xff0c;然后再按一次电源健重启电脑&#xff0c;按F8健进入安全模式2、安全模式下进入Windows系统桌面后&#xff0c;按住“winR”打开运行窗口&#xff0c;输入“services.msc”打开服务设置3、在服务界面&#xff0c;选中…...

    2022/11/19 21:17:18
  27. 错误使用 reshape要执行 RESHAPE,请勿更改元素数目。

    %读入6幅图像&#xff08;每一幅图像的大小是564*564&#xff09; f1 imread(WashingtonDC_Band1_564.tif); subplot(3,2,1),imshow(f1); f2 imread(WashingtonDC_Band2_564.tif); subplot(3,2,2),imshow(f2); f3 imread(WashingtonDC_Band3_564.tif); subplot(3,2,3),imsho…...

    2022/11/19 21:17:16
  28. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...

    win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”问题的解决方法在win7系统关机时如果有升级系统的或者其他需要会直接进入一个 等待界面&#xff0c;在等待界面中我们需要等待操作结束才能关机&#xff0c;虽然这比较麻烦&#xff0c;但是对系统进行配置和升级…...

    2022/11/19 21:17:15
  29. 台式电脑显示配置100%请勿关闭计算机,“准备配置windows 请勿关闭计算机”的解决方法...

    有不少用户在重装Win7系统或更新系统后会遇到“准备配置windows&#xff0c;请勿关闭计算机”的提示&#xff0c;要过很久才能进入系统&#xff0c;有的用户甚至几个小时也无法进入&#xff0c;下面就教大家这个问题的解决方法。第一种方法&#xff1a;我们首先在左下角的“开始…...

    2022/11/19 21:17:14
  30. win7 正在配置 请勿关闭计算机,怎么办Win7开机显示正在配置Windows Update请勿关机...

    置信有很多用户都跟小编一样遇到过这样的问题&#xff0c;电脑时发现开机屏幕显现“正在配置Windows Update&#xff0c;请勿关机”(如下图所示)&#xff0c;而且还需求等大约5分钟才干进入系统。这是怎样回事呢&#xff1f;一切都是正常操作的&#xff0c;为什么开时机呈现“正…...

    2022/11/19 21:17:13
  31. 准备配置windows 请勿关闭计算机 蓝屏,Win7开机总是出现提示“配置Windows请勿关机”...

    Win7系统开机启动时总是出现“配置Windows请勿关机”的提示&#xff0c;没过几秒后电脑自动重启&#xff0c;每次开机都这样无法进入系统&#xff0c;此时碰到这种现象的用户就可以使用以下5种方法解决问题。方法一&#xff1a;开机按下F8&#xff0c;在出现的Windows高级启动选…...

    2022/11/19 21:17:12
  32. 准备windows请勿关闭计算机要多久,windows10系统提示正在准备windows请勿关闭计算机怎么办...

    有不少windows10系统用户反映说碰到这样一个情况&#xff0c;就是电脑提示正在准备windows请勿关闭计算机&#xff0c;碰到这样的问题该怎么解决呢&#xff0c;现在小编就给大家分享一下windows10系统提示正在准备windows请勿关闭计算机的具体第一种方法&#xff1a;1、2、依次…...

    2022/11/19 21:17:11
  33. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”的解决方法...

    今天和大家分享一下win7系统重装了Win7旗舰版系统后&#xff0c;每次关机的时候桌面上都会显示一个“配置Windows Update的界面&#xff0c;提示请勿关闭计算机”&#xff0c;每次停留好几分钟才能正常关机&#xff0c;导致什么情况引起的呢&#xff1f;出现配置Windows Update…...

    2022/11/19 21:17:10
  34. 电脑桌面一直是清理请关闭计算机,windows7一直卡在清理 请勿关闭计算机-win7清理请勿关机,win7配置更新35%不动...

    只能是等着&#xff0c;别无他法。说是卡着如果你看硬盘灯应该在读写。如果从 Win 10 无法正常回滚&#xff0c;只能是考虑备份数据后重装系统了。解决来方案一&#xff1a;管理员运行cmd&#xff1a;net stop WuAuServcd %windir%ren SoftwareDistribution SDoldnet start WuA…...

    2022/11/19 21:17:09
  35. 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?

    原标题&#xff1a;电脑提示“配置Windows Update请勿关闭计算机”怎么办&#xff1f;win7系统中在开机与关闭的时候总是显示“配置windows update请勿关闭计算机”相信有不少朋友都曾遇到过一次两次还能忍但经常遇到就叫人感到心烦了遇到这种问题怎么办呢&#xff1f;一般的方…...

    2022/11/19 21:17:08
  36. 计算机正在配置无法关机,关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机...

    关机提示 windows7 正在配置windows 请勿关闭计算机 &#xff0c;然后等了一晚上也没有关掉。现在电脑无法正常关机以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;关机提示 windows7 正在配…...

    2022/11/19 21:17:05
  37. 钉钉提示请勿通过开发者调试模式_钉钉请勿通过开发者调试模式是真的吗好不好用...

    钉钉请勿通过开发者调试模式是真的吗好不好用 更新时间:2020-04-20 22:24:19 浏览次数:729次 区域: 南阳 > 卧龙 列举网提醒您:为保障您的权益,请不要提前支付任何费用! 虚拟位置外设器!!轨迹模拟&虚拟位置外设神器 专业用于:钉钉,外勤365,红圈通,企业微信和…...

    2022/11/19 21:17:05
  38. 配置失败还原请勿关闭计算机怎么办,win7系统出现“配置windows update失败 还原更改 请勿关闭计算机”,长时间没反应,无法进入系统的解决方案...

    前几天班里有位学生电脑(windows 7系统)出问题了&#xff0c;具体表现是开机时一直停留在“配置windows update失败 还原更改 请勿关闭计算机”这个界面&#xff0c;长时间没反应&#xff0c;无法进入系统。这个问题原来帮其他同学也解决过&#xff0c;网上搜了不少资料&#x…...

    2022/11/19 21:17:04
  39. 一个电脑无法关闭计算机你应该怎么办,电脑显示“清理请勿关闭计算机”怎么办?...

    本文为你提供了3个有效解决电脑显示“清理请勿关闭计算机”问题的方法&#xff0c;并在最后教给你1种保护系统安全的好方法&#xff0c;一起来看看&#xff01;电脑出现“清理请勿关闭计算机”在Windows 7(SP1)和Windows Server 2008 R2 SP1中&#xff0c;添加了1个新功能在“磁…...

    2022/11/19 21:17:03
  40. 请勿关闭计算机还原更改要多久,电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机怎么办...

    许多用户在长期不使用电脑的时候&#xff0c;开启电脑发现电脑显示&#xff1a;配置windows更新失败&#xff0c;正在还原更改&#xff0c;请勿关闭计算机。。.这要怎么办呢&#xff1f;下面小编就带着大家一起看看吧&#xff01;如果能够正常进入系统&#xff0c;建议您暂时移…...

    2022/11/19 21:17:02
  41. 还原更改请勿关闭计算机 要多久,配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以...

    配置windows update失败 还原更改 请勿关闭计算机&#xff0c;电脑开机后一直显示以以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;配置windows update失败 还原更改 请勿关闭计算机&#x…...

    2022/11/19 21:17:01
  42. 电脑配置中请勿关闭计算机怎么办,准备配置windows请勿关闭计算机一直显示怎么办【图解】...

    不知道大家有没有遇到过这样的一个问题&#xff0c;就是我们的win7系统在关机的时候&#xff0c;总是喜欢显示“准备配置windows&#xff0c;请勿关机”这样的一个页面&#xff0c;没有什么大碍&#xff0c;但是如果一直等着的话就要两个小时甚至更久都关不了机&#xff0c;非常…...

    2022/11/19 21:17:00
  43. 正在准备配置请勿关闭计算机,正在准备配置windows请勿关闭计算机时间长了解决教程...

    当电脑出现正在准备配置windows请勿关闭计算机时&#xff0c;一般是您正对windows进行升级&#xff0c;但是这个要是长时间没有反应&#xff0c;我们不能再傻等下去了。可能是电脑出了别的问题了&#xff0c;来看看教程的说法。正在准备配置windows请勿关闭计算机时间长了方法一…...

    2022/11/19 21:16:59
  44. 配置失败还原请勿关闭计算机,配置Windows Update失败,还原更改请勿关闭计算机...

    我们使用电脑的过程中有时会遇到这种情况&#xff0c;当我们打开电脑之后&#xff0c;发现一直停留在一个界面&#xff1a;“配置Windows Update失败&#xff0c;还原更改请勿关闭计算机”&#xff0c;等了许久还是无法进入系统。如果我们遇到此类问题应该如何解决呢&#xff0…...

    2022/11/19 21:16:58
  45. 如何在iPhone上关闭“请勿打扰”

    Apple’s “Do Not Disturb While Driving” is a potentially lifesaving iPhone feature, but it doesn’t always turn on automatically at the appropriate time. For example, you might be a passenger in a moving car, but your iPhone may think you’re the one dri…...

    2022/11/19 21:16:57