SpringMVC中使用 servlet 的对象:(Request,Response,Session,Cookie)
springmvc已经帮我们封装好了这些对象,只需在方法参数上使用所需要的对象即可
@Controller
public class ServletController {
/**
* 只要在控制器方法上加入request,response,session类型的参数,springmvc框架会把这些对象准备好作为方法参数传入
* 建议不要直接在方法参数上使用 Servlet 的(request,response,session)对象
* @param request
* @param response
* @param session
* @return
*/
@RequestMapping("/s1")
public String s1(HttpServletRequest request, HttpServletResponse response, HttpSession session){
System.out.println("获取实际请求路径:"+request.getRequestURI());
System.out.println("获取客户端的ip:"+request.getRequestURI());
System.out.println("response的对象:"+response);
response.addCookie(new Cookie("user","chen"));
System.out.println("session的对象:"+session);
return "hello";
}
}
SpringMVC页面转发:
服务器内部跳转(forward)和 客户端重定向(redirect)
1) 转发
(1) 转发到JSP页面: 请求处理方法返回字符串,默认使用服务器内部跳转 ( request.getRequestDispatcher("指定页面").forward(request,response) )
示例: return "hello"; (视图名: 视图解析器会根据视图名拼接成正确的访问路径 " /hello.jsp")
(2)转发到服务器内部的请求:在返回的字符串前面加入 “ forward:/ 路径名 ”
示例: return " forward:/ main ";( 在服务器内部跳转到名为 main 的请求处理方法中)
2)重定向
(1)重定向到 JSP页面:在返回字符串的前面加上 “ redirect:/xxx.jsp ”
示例:return " redirect:/xxx.jsp "
***注意:
由于客户端重定向(redirect)相当于在浏览器重新发一次请求,所以不能访问 WEB-INF 下的资源文件,而且资源文件必须写完整(xxx.jsp),因为此时的视图解析器不会将该字符串进行解析,拼接
(2)重定向到 Controller 上 : 在返回的字符串前面加上 “ redirect:/路径名 ”
示例: return " redirect:/main "; ( 客户端重定向到名为 main 的请求处理方法中 )
Spring MVC 传值:
方法一:使用session传值 :
优点:信息相对比较安全,可以传输任意类型的数据
缺点:每个session都会占用服务器的内存
测试代码:
@Controller
public class RedirectController {
/**
* 方法一: 利用 HttpServletResponse 对象的重定向
* session 传值:
* 优点: 安全性高,可传任意类型的数据
* 缺点: 每个session都会占用服务器的内存
*
* @param response
* @return
* @throws IOException
*/
@RequestMapping("/r1")
public String r1(HttpServletResponse response, HttpSession session) throws IOException {
System.out.println("r1");
// 一次会话之内有效
session.setAttribute("name","chen");
//response.sendRedirect("/r2");
// 在视图名前面添加 `redirect:`,这时候springmvc就会把字符串当做重定向进行跳转,而不是再通过视图解析器进行解析
return "redirect:/r2";
}
@RequestMapping("/r2")
public String r2(HttpSession session){
System.out.println("r2");
System.out.println(session.getAttribute("name"));
// 需要手动移除 session 的值
session.removeAttribute("name");
return "hello";
}
}
方法二: 利用重定向后面跟上请求参数的方法
优点: 不会占用服务器的内存,可以向站外地址传值
缺点: 信息不安全,暴露在地址栏后面,可以传输的数据类型和数据大小有限制
测试代码:
@Controller
public class RedirectController {
/**
* 利用重定向地址后跟请求参数的方式
* 优点: 不会占用服务器内存,可以向站外地址传参
* 缺点: 安全性不高,数据大小有限制,不能传 复杂的 数据类型
* @return
*/
@RequestMapping("/r3")
public String r3(){
System.out.println("r3");
return "redirect:/r4?username=zhangsan";
}
@RequestMapping("/r4")
public String r4(String username){
System.out.println("r4");
System.out.println("username: "+username);
return "hello";
}
}
方法三:使用 Spring MVC提供的传参的对象(RedirectAttributes):专门在重定向时传参,综合了上面两种方法
RedirectAttributes : 传参,专门在重定向时传参
@ModelAttributes: 接收值
方式一(重定向后面跟请求参数的方式): redirect.addAttribute("参数名",参数值);
方式二(使用session传值的方式):redirect.addFlashAttribute("参数名",参数值);
测试代码:
@Controller
public class RedirectController {
/**
* SpringMVC提供的重定向方法:结合了上面两种方式的传参方式
* RedirectAttributes : 传参,专门在重定向时传参
* @ModelAttributes: 接收值
* 方式一(重定向后面跟请求参数的方式): redirect.addAttribute("username","chen");
* 方式二(使用session传值的方式):redirect.addFlashAttribute("username","kobe");
*
* @param redirect
* @return
*/
@RequestMapping("/r5")
public String r5(RedirectAttributes redirect){
System.out.println("r5");
// 采用的是 重定向后面跟请求参数的方式
redirect.addAttribute("username","chen");
// 采用的是 session 的传值方式
redirect.addFlashAttribute("username","kobe");
return "redirect:/r6";
}
@RequestMapping("/r6")
public String r6(@ModelAttribute("username")String name){
System.out.println("r6");
System.out.println("username: "+name);
return "hello";
}
}
Spring MVC中的异常处理:
SpringMVC提供了两种方式
1)使用 SpringMVC提供的简单异常处理器 SimpleMapperingExceptionResolver (需要配合 .xml 文件来配置)
2)使用注解@ExceptionHandler( 用在方法上) 实现局部异常处理或使用注解@ControllerAdvice(类)实现全局统一处理异常
***注意:不建议在请求处理方法中写异常处理块(catch),因为这种处理方式非常繁琐,而且一旦改变异常处理方式, 需要修改大量的代码,耦合性较高
方式一: 使用 SimpleMapperingExceptionResolver 进行异常处理
1)在 spring.xml 文件中配置:
<!--
p:defaultErrorView="error":表示所有没有指定的异常,都跳转到默认异常处理页面 error.jsp
p:exceptionAttribute="ex" :表示异常处理页面中访问的异常对象变量名是 ex
exceptionMappings: 表示映射的异常,接受参数是一个 Properties
key:是异常类名, values:处理异常的页面
-->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"
p:defaultErrorView="error"
p:exceptionAttribute="ex"
>
<property name="exceptionMappings">
<props>
<prop key="java.lang.ArrayIndexOutOfBoundsException">error1</prop>
<prop key="java.lang.ArithmeticException">error2</prop>
</props>
</property>
</bean>
2)模拟异常的处理方法:
@RequestMapping("/testSimpleMapingExceptionResolver")
public String testSimpleMapingExceptionResolver(@RequestParam("i") int i){
String[] values=new String[10];
System.out.println(values[i]);
return "success";
}
3)error.jsp页面:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page isELIgnored="false" %>
<html>
<head>
<title>Title</title>
</head>
<body>
错误页面!
<br>
${requestScope.ex.message}
</body>
</html>
方式二: 使用注解 @ExceptionHandler( 用在方法上) 或 @ControllerAdvice(类) 实现处理异常
1)@ExceptionHandler: 加在方法上,并且在运行时有效( 只能捕获 本Controller 中发生的异常)
@Controller
public class ExceptionController {
/**
* springmvc 帮我们自动捕获异常
* @ExceptionHandler(ArithmeticException.class): 只能精确匹配异常类型,匹配到了,则执行该方法,若不匹配,则继续报错
* @ExceptionHandler(Exception.class): 若没匹配到该精确类型,则执行该异常的方法
*
* ***注意***: 以上自动捕获异常值捕获 ***本Controller*** 发生的异常,别的 Controller 若发生异常,则无法捕获
* @return
*/
@RequestMapping("/e2")
public String e2(){
System.out.println("e2");
int i = 1/0;
int[] arr = new int[5];
System.out.println(arr[6]);
return "hello";
}
@ExceptionHandler(ArithmeticException.class)
public String exception1(ArithmeticException e){
System.out.println("错误信息:"+e.getMessage());
return "error";
}
@ExceptionHandler(ArrayIndexOutOfBoundsException.class)
public String exception2(ArithmeticException e){
System.out.println("错误信息:"+e.getMessage());
return "error";
}
}
2)使用 @ControllerAdvice : 它是一个控制器增强功能注解,加在类上, 会将该类中所有使用了 @ExceptionHandler 注解的方法都应用到请求处理方法上
测试代码:先精确匹配异常类型,若匹配不到,则最后再匹配 Exception 类
// 告知spring容器这是个 **全局捕获异常类**
@ControllerAdvice
public class GlobalExceptionAdvice {
@ExceptionHandler(ArrayIndexOutOfBoundsException.class)
public String exception1(ArrayIndexOutOfBoundsException e){
System.out.println("数组越界:"+e.getMessage());
return "error";
}
@ExceptionHandler(Exception.class)
public String exception2(Exception e){
System.out.println(e.getMessage());
return "error";
}
@ExceptionHandler(ArithmeticException.class)
public String exception3(ArithmeticException e){
System.out.println(e.getMessage());
return "error";
}
}
***注意:
1) @EceptionHandler 该方法只能捕获本控制器类出现的异常
2) 匹配异常类型时,会匹配一个最接近的异常类型
3) @ControllerAdvice : 全局控制器的通知类, 加在通知类上,这样当某个控制器出现异常时, 先找本类的异常处理器,如果找不到,那么再到通知类中找全局的异常处理器
SpringMVC文件上传:
SpringMVC对本间上传提供了直接的支持,使用 MultipartResolver(文件解析器) 实现的, 为了来解决表单提交的数据格式是 enctype = "Multipart/form-data", 浏览器就会采用二进制的方式来处理表单数据
1)在 pom.xml 中导入文件上传所需的 commons-fileupload-1.3.3.jar 和 commons-io-2.2.jar 两个jar 包
<!-- 文件上传(可选) -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.3</version>
</dependency>
2)在 spring.xml 中配置文件解析器
<!-- 配合文件上传而使用的 文件解析器-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 设置文件上传大小,单位:字节-->
<property name="maxUploadSize" value="100000"/>
<!-- 请求的编码格式,必须与JSP的 pageEncoding 的属性一致,默认为 ISO-8859-1 -->
<property name="defaultEncoding" value="UTF-8">
</bean>
3)编写 文件 上传 的 jsp 页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="/upload" method="post" enctype="multipart/form-data">
<input type="file" name="file">
<input type="submit" value="提交">
</form>
</body>
4)编写文件上传的控制器类: springmvc会将上传的文件绑定到 MultipartFile 对象中
MultipartFile 对象: 可以获取上传文件的内容,文件名,大小等数据
- byte[] getBytes(): 获取文件数据
- String getContentType(): 获取文件MIME类型,如 image/jpeg
- InputStream getInputStream(): 获取文件流
- String getName(): 获得表单中文件组件的名称
- String getOriginalFilename(): 获得上传文件的名称
- long getSize(): 获得文件的字节大小,单位:byte
- boolean isEmpty(): 判断是否有上传的文件
- void transerTo(File dest): 将上传的文件保存到一个目标文件中
@Controller
public class FileUploadController {
@RequestMapping("/upload")
public String upload(MultipartFile file) throws IOException {
System.out.println("上传的文件类型:"+file.getContentType());
System.out.println("上传的文件名:"+file.getOriginalFilename());
System.out.println("上传的文件大小:"+file.getSize());
System.out.println("上传的文件是否为空:"+file.isEmpty());
// 将上传的文件保存到本地
file.transferTo(new File("G:\\"+file.getOriginalFilename()));
return "hello";
}
}
SpringMVC中缓存的配置:
@Cacheable( cacheNames="缓存的名字"): 加在方法上
***工作原理:
在执行该方法之前,先检查缓存中是否有这条记录,如果有,则直接返回缓存中的记录,不执行该方法,
若该缓存中没有存储该条记录,则执行该方法,将该方法的返回值存入该缓存中,以便于下一次的查找;
@CacheEvict( cacheNames = "缓存名字",allEntries = true) : 加在方法上 (allEntries: 该缓存中的所有记录)
*** 工作原理:
将该缓存下的所有记录都清空
@CachePut( cacheNames="缓存名字"):加在方法上
*** 工作原理:
总是会执行该方法,每次都把返回结果更新进该缓存中
测试代码:
在 sprin.xml 中添加缓存配置:
<!-- 缓存的管理器 -->
<bean id="cacheManager" class="org.springframework.cache.concurrent.ConcurrentMapCacheManager">
<!-- 管理缓存区域 -->
<property name="cacheNames">
<list>
<value>user</value>
<value>order</value>
<value>product</value>
</list>
</property>
</bean>
<!-- 启用缓存相关的注解及支持
@Cacheable
@CacheEvict
@CachePut
-->
<cache:annotation-driven/>
@Controller
public class CacheController {
@Autowired
private UserService userService;
@RequestMapping("/cache1")
@ResponseBody
@Cacheable(cacheNames = "user")
/**
* 找到user的缓存
* Cache userCache = ;
* User user = userCache.get(key#id);
* if(user == null) {
* user = 调用findById方法查询数据库;
* userCache.put("key#id", user);
* }
*/
public User findById(int id) {
return userService.findById(id);
}
@RequestMapping("/update")
@ResponseBody
@CacheEvict(cacheNames = "user", allEntries = true) // 让缓存的内容失效
public String update() {
System.out.println("执行了修改方法");
return "update successful..";
}
@RequestMapping("/update2")
@ResponseBody
@CachePut(cacheNames = "user") // 思路将方法的执行结果替换缓存的内容
public User update2(int id) {
System.out.println("执行了Update2");
User user = userService.findById(id);
return user;
}
}
Ajax: (Asynchronous JavaScript And XML )异步的 JavaScript 和 XML 不是一种新的编程语言, 而是一种页面无刷新技术,在页面不刷新的情况下与服务器进行交互,是一种创建更好,更快以及交互性更强的 web技术,基于 JavaScript ,html,css ,xml 的新用法
Ajax的工作原理: 只刷新局部页面
相当于在用户和服务器之间加了—个中间层,使用户操作与服务器响应异步化。并不是所有的用户请求都提交给服务器,像—些数据验证和数据处理等都交给Ajax引擎自己来做, 只有确定需要从服务器读取新数据时再由Ajax引擎代为向服务器提交请求
- JavaScript:更新局部的网页
- XML:一般用于请求数据和响应数据的封装
- XMLHttpRequest对象:发送请求到服务器并获得返回结果
- CSS:美化页面样式
- 异步:发送请求后不等返回结果,由回调函数处理结果
原理图:
同步提交请求: 提交请求后,只有等到返回响应,才会继续执行下面的操作, 否则将会一直等待下去
异步提交请求:提交请求后,无需等待返回响应,就可以继续执行下面的操作
XHRHttpRequest: 整个Ajax技术的核心,它提供了异步发送请求的能力
属性
readyState:类型short;只读: 响应状态
responseText:类型String;只读 : 获得字符串(JSON )形式的响应数据。
responseXML:类型Document;只读 获得 XML 形式的响应数据。
status:类型short;只读 返回状态码, 200: ok, 404:错误…
方法:
open(method,URL,async) :建立与服务器的连接
参数1: method参数指定请求的HTTP方法,典型的值是GET或POST
参数2: URL参数指请求的地址
参数3: async参数指定是否使用异步请求,其值为true或false
send(content) : 发送请求
content参数指定请求的参数
setRequestHeader(header,value):设置请求的头信息
Ajax同步发送请求:
1. 创建xhr对象
2. 发送请求
3. 接收响应
4. 将返回的 json结果 转换 js对象
<html>
<head>
<title>Title</title>
</head>
<body>
<video src="video/tomcatcluster.mp4" controls width="500"></video>
<input type="text" name="content" id="c">
<input type="button" value="提交评论" onclick="sendRequest()">
<hr>
<div style="width:100%; height: 100px; border: 1px solid black;" id="d1"></div>
<script>
//同步方式接收响应
function sendRequest() {
// 1. 创建xhr对象
var xhr = new XMLHttpRequest();
// 2. 发送请求
// 获取文本框评论的内容
var c = document.getElementById("c").value;
xhr.open("get","/pinglun?content="+c, false);
xhr.send();
// 3. 接收响应
var text = xhr.responseText; // "" // 拿到的responseText是json格式的 , 例如: {"content":"分阶段"}
// 4. 将json结果转换js对象
var obj = JSON.parse(text);
document.getElementById("d1").innerText = obj.content;
}
</script>
</body>
</html>
Ajax异步发送请求:
1. 创建xhr对象
2. 定义响应事件
3. 发送请求
4. 接收响应
5. 将json结果转换js对象
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<video src="video/tomcatcluster.mp4" controls width="500"></video>
<input type="text" name="content" id="c">
<input type="button" value="提交评论" onclick="sendRequest()">
<hr>
<div style="width:100%; height: 100px; border: 1px solid black;" id="d1"></div>
<script>
// 异步方式接收响应
function sendRequest() {
// 1. 创建xhr对象
var xhr = new XMLHttpRequest();
// 2. 定义响应事件
xhr.onload = function(){
// 4. 接收响应
var text = xhr.responseText; // "" // 拿到的responseText是json格式的 , 例如: {"content":"分阶段"}
// 5. 将json结果转换js对象
var obj = JSON.parse(text);
document.getElementById("d1").innerText = obj.content;
};
// 3. 发送请求
// 获取文本框评论的内容
var c = document.getElementById("c").value;
xhr.open("get","/pinglun?content="+c, true);
xhr.send();
}
</script>
</body>
</html>
注解:在spring.xml 中 启用 mvc相关的注解功能 <mvc:annotation-driven/>
SpringMVC中的注解有很多,都在 org.springframework.web.bind.annotation 包下
主要分为六大类:(根据处理 request 的内容不同划分)
1)处理请求参数和内容部分的注解:
(1)@Controller: 加在类上,用来指示该类是一个控制器类,使用注解可以使得该控制器支持 同时处理多个请求动作,更加灵活
工作原理: 使用了 @Controller 的注解会被Spring的扫描机制查找到,并检测该类中的方法是否使用了 @RequestMapper 注解,使用了该注解的方法才是真正处理请求的处理器,匹配对应的路径进行处理请求
(2)@RequestMapper: 加在 类或方法上,指示哪一个类或者方法来处理相对应的请求动作
测试代码:
@Controller
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@RequestMapping("/findById")
public User findById(int id) {
return userService.findById(id);
}
@RequestMapping("/a")
public User a(int id) {
return userService.findById(id);
}
}
(3)@ResponseBody: 加在方法上,将方法返回的对象,通过适当的 HttpMessageConverter 转换为指定格式 写入到 Response 对象的 body 数据区域中,一般用于返回 JSON 或 XML 数据
测试代码: 返回 JSON格式的数据
@Controller
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@RequestMapping("/exists")
@ResponseBody // {"exists", true}
public Map<String,Object> exists(String username) throws InterruptedException {
boolean exists = userService.exists(username);
Thread.sleep(2000);
Map<String, Object> map = new HashMap<>();
map.put("exists", exists);
return map;
}
}
(4) @RequestBody: 加在方法参数上,用来处理 Content-type: 不是 application/x-www-form-urlencoded编码 的内容,而是例如: application/xml ,application/json 等数据格式时,使用该注解, 通过使用 HandlerAdapter 配置的 HttpMessageConverters 来解析 JSON 或 XML数据, 让后绑定到相对应的 bean 上(把请求体中的json字符串再还原为java对象)
测试代码: jsp页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<input type="button" value="发送表单格式请求" onclick="sendRequest()">
<input type="button" value="发送json格式请求" onclick="sendJsonRequest()">
<script>
function sendRequest() {
var xhr = new XMLHttpRequest();
xhr.onload = function(){...};
xhr.open("post", "/post", true);
// 告诉服务器,请求体的格式是:表单格式
xhr.setRequestHeader("content-type", "application/x-www-form-urlencoded");
xhr.send("username=zhangsan"); // 包含的是请求体的内容
}
function sendJsonRequest() {
var xhr = new XMLHttpRequest();
xhr.onload = function(){...};
xhr.open("post", "/post2", true);
xhr.setRequestHeader("content-type", "application/json");
var user = {"id":1, "name":"李四"}; // [object Object]
// 要把js对象转为json字符串才能发送
xhr.send(JSON.stringify(user))
}
</script>
</body>
</html>
Controller 处理:
@Controller
public class PostController {
@ResponseBody
@RequestMapping("/post")
public String post(String username) {
System.out.println("username:" +username);
return "ok";
}
@ResponseBody
@RequestMapping("/post2") // @RequestBody的作用就是把请求体中的json字符串再还原为java对象
public String post2(@RequestBody User user) {
System.out.println("user对象是:"+user);
return "ok";
}
}
其他注解: @RequestParam,@GetMapping,@PostMapping,@PutMapping,@DeleteMapping,
@PatchMapping,@RequestPart,@RestController
2)处理请求 URL 部分的注解:@PathVariable, @MatrixVariable,@CrossOrigin
(1)@PathVariable: 加在方法参数上,可以非常方便的获取请求 URL 中的动态参数(路径参数)
@RequestMapping("/test4/{id}/{name}")
public String test4(@PathVariable("id")int x,@PathVariable("name")String n){
System.out.println("name: "+n+" id: "+x);
return "hello";
}
(2)@MatrixVariable: 加在方法参数上,拓展了URL请求地址的功能,多个请求参数可用 “ , ”分开 一般用于进行多条件的组合查询
***注意:
springMVC默认是不开启 @MatrixVariables 注解功能的
需要使用时,在 spring.xml 中更改为 < mvc:annotation-driven enable-matrix-variables="true" /> 开启注解功能
// 查询的URL : http://loaclhost/test4/1;name=chen;age=18
@RequestMapping("/test4/{id}")
public String test4(
@PathVariable("id")int x,
@MatrixVariable(vaule = "name",pathVar="id") String name,
@MatrixVariable(vaule = "age",pathVar="id")int age,
){
System.out.println("name: "+name+" id: "+id+" age:"+age);
return "hello";
}
3)处理请求头部分的注解: @RequestHeader, @CookieValue
(1)@CookieValues(" 参数名") : 加在方法参数上,用于将请求的 cookie 数据映射到功能处理方法的参数上
支持属性:
name :请求参数绑定的名称
value: name属性的别名
required:指定参数是否必须绑定
defaultValues: 如果没有传递参数而使用默认值
测试代码:
@Controller
public class ServletController {
/**
* @CookieValue("user"): 在请求红找到一个名为 user 的cookie的值
*
* @param username
* @return
*/
@RequestMapping("/s2")
public String s2(@CookieValue("user")String username){
System.out.println("获取到请求cookie的值:"+username);
return "hello";
}
}
(2) @RequestHeader: 加在方法参数上,将请求头的信息数据映射到方法参数上
4)处理属性类型的注解:@RequestAttribute,@SessionAttribute,@SessionAttributes,@ModelAttribute
(1) @RequestAttribute: 加在方法参数上,用于获取 request作用域 中的数据
(2)@SessionAttribute: 加在方法参数上,用于获取 session作用域中的数据
(3)@SessionAttributes: 加在类上,允许我们有选择的指定Model中的哪些属性转存入 session 作用域中
(4) @ModelAttribute:加在方法上,用于将请求参数绑定到对象,然后存入request作用域中
***注意: 使用了 @ModelAttribute 注解的方法会在Controller每个方法执行前被执行
5)处理异常类型的注解:@ExceptionHandler , @ControllerAdvice, @RestControllerAdvice, @ResponseStatus
(1) @ExceptionHandler: 加在方法上,运行时有效,参数:value=异常类型名.class
(2) @ControllerAdvice: 加在类上,经常配合@ExceptionHandler做全局异常处理
(3) @ResponseStatus: 加在类或方法上,处理异常最简单的方式,一般用于自定义的异常类
6)绑定表单数据的注解: @InitBinder