目录
3. 使用@RequestBody注解把json的字符串转换成JavaBean的对象
4. 使用@ResponseBody注解把JavaBean对象转换成json字符串,直接响应
5. json字符串和JavaBean对象互相转换的过程中,需要使用jackson的jar包
4.2.1 第一步:编写一个普通类实现 HandlerInterceptor
第一章:响应数据和结果视图
1.1返回值分类
1.1.1 字符串
Controller方法返回字符串可以指定逻辑视图的名称,根据视图解析器为物理视图的地址。
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/testString")
public String testString(Model model) {
// 执行方法体...向隐式对象添加属性attribute_user,可以在jsp中通过 ${attribute_user} 获取到
model.addAttribute("attribute_user", new User("张三", "123"));
// 经过视图解析器的处理,SpringMVC会将请求转发到/WEB-INF/pages/succeess.jsp,但浏览器地址栏显示的一直是 项目域名/user/testString
return "success";
}
}
1.1.2 void
1. 如果控制器的方法返回值编写成void,执行程序报404的异常,默认查找JSP页面没有找到。
默认会跳转到@RequestMapping(value="/initUpdate") initUpdate的页面。
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/testVoid")
public void testVoid(Model model) {
// 执行方法体...向隐式对象添加属性attribute_user,可以在jsp中通过 ${attribute_user} 获取到
model.addAttribute("attribute_user", new User("张三", "123"));
// 处理器没有返回值,则会将请求转发到当前 项目域名/user/testVoid 路径
// 若在web.xml中没有配置 项目域名/user/testVoid 对应的url-pattern,则会返回404错误
return;
}
}
2. 可以使用请求转发或者重定向跳转到指定的页面
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/testVoid")
public void testVoid(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 执行方法体...向隐式对象添加属性attribute_user,可以在jsp中通过 ${attribute_user} 获取到
model.addAttribute("attribute_user", new User("张三", "123"));
// 通过下面三个方法之一,可以指定访问的视图
// 指定视图的方式1: 请求转发
request.getRequestDispatcher("/WEB-INF/pages/success.jsp").forward(request,response);
// 指定视图的方式2: 重定向
response.sendRedirect(request.getContextPath() + "/index.jsp");
// 指定视图的方式3: 通过Writer对象写入内容
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
response.getWriter().print("你好");
return;
}
}
1.1.3 ModelAndView对象
ModelAndView对象是Spring提供的一个对象,可以用来调整具体的JSP视图
ModelAndView为我们提供了一种更灵活地为页面添加属性和指定返回视图的方法,其主要方法如下:
- public ModelMap getModelMap(): 返回当前页面的ModelMap对象.
- public ModelAndView addObject(Object attributeValue): 向当前页面的ModelMap对象中添加属性
- public void setViewName(@Nullable String viewName): 指定返回视图,viewName会先被视图解析器处理解析成对应视图.
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/testModelAndView")
public ModelAndView testModelAndView() {
// 创建ModelAndView对象
ModelAndView mv = new ModelAndView();
// 向model中存入属性attribute_user
mv.addObject("attribute_user", new User("张三", "123"));
// 指定返回视图,视图解析器将"success"解析为视图URL /WEB-INF/pages/succeess.jsp
mv.setViewName("success");
return mv;
}
}
1.2 SpringMVC框架提供的转发和重定向
1.2.1 forward请求转发
此时forward:
后的地址不能直接被视图解析器解析,因此要写完整的相对路径
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/testForward")
public String testForward() {
// 在forward:要写完整的相对路径
// return "forward:success" // 错误,会将请求转发到 /项目名/user/success
return "forward:/WEB-INF/pages/success.jsp";
}
}
1.2.2 redirect重定向
此时redirect:
后的地址要写相对于ContextPath
的地址
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/testRedirct")
public String testRedirct() {
// 在forward:要写完整的相对路径
// return "redirect:" + request.getContextPath() + "/index.jsp"; // 错误,会将请求转发到 /项目名/项目名/index.jsp
return "redirect:/index.jsp";
}
}
1.3 ResponseBody响应json数据
1. DispatcherServlet会拦截到所有的资源,导致一个问题就是静态资源(img、css、js)也会被拦截到,从而不能被使用。解决问题就是需要配置静态资源不进行拦截,在springmvc.xml配置文件添加如下配置
1. mvc:resources标签配置不过滤
1. location元素表示webapp目录下的包下的所有文件
2. mapping元素表示以/static开头的所有请求路径,如/static/a 或者/static/a/b
<!-- 配置静态文件的路径于对应的URL --> <!-- location属性必须是一个有效的目录,因此必须以 / 结尾 --> <mvc:resources location="/css/" mapping="/css/**"/> <mvc:resources location="/images/" mapping="/images/**"/> <mvc:resources location="/js/" mapping="/js/**"/>
2. 使用@RequestBody获取请求体数据
<script>
// 页面加载,绑定单击事件
$(function () {
$("#btn").click(function () {
// 发送ajax请求
$.ajax({
// 配置请求参数
url: "user/testAjax",
contentType: "application/json;charset=UTF-8",
dataType: "json",
type: "post",
// 请求的json数据
data: '{"username":"myname","password":"mypassowrd","age":30}',
// 回调函数,处理服务器返回的数据returnData
success: function (returnData) {
// 我们假定服务器返回的是一个user对象,将其输出在控制台上
console.log(returnData); }
});
});
});
</script>
3. 使用@RequestBody注解把json的字符串转换成JavaBean的对象
使用@ResponseBody
注解表示将该方法的返回值直接写回到HTTP响应中,而不是存入Model
或解析为视图名
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/testAjax")
@ResponseBody
public User testAjax(@RequestBody User user) {
System.out.println(user);
// 将user对象返回给前端页面
return user;
}
}
4. 使用@ResponseBody注解把JavaBean对象转换成json字符串,直接响应
要求方法需要返回JavaBean的对象
@RequestMapping("/testAjax")
//@ResponseBody 注在返回值类型Address前,自动将Address类型转换为json格式数据响应给客户端
public @ResponseBody User testAjax(@RequestBody User user) {
//客户端发送ajax请求,传入的是json字符串(请求体内)由SpringMVC框架自动转换封装到JavaBean中
System.out.println(user);
user.setUserName("111");
return user;
}
5. json字符串和JavaBean对象互相转换的过程中,需要使用jackson的jar包
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.0</version>
</dependency
第二章:SpringMVC实现文件上传
前提条件
A form 表单的 enctype(表单请求正文的类型) 取值必须是:multipart/form-data
(默认值是:application/x-www-form-urlencoded)
B method 属性取值必须是 Post (get方法是将请求体写在URL里的。有大小限制)
C 提供一个文件选择域<input type=”file” />
上传原理
不论是传输字符串,还是传输文件,都是通过form表单,其实文件只是长度很长的字符串罢了。
当 form 表单的 enctype=”application/x-www-form-urlencoded”时,
form 表单的正文内容是键值对+&: key=value&key=value&key=value当 form 表单的 enctype 取值为 Mutilpart/form-data 不是默认值时,request.getParameter()将失效。
请求正文内容就变成: 每一部分都是 MIME 类型描述的正文
-----------------------------7de1a433602ac 分界符
Content-Disposition: form-data; name="userName" 协议头
aaa 协议的正文(文件内容)
-----------------------------7de1a433602ac那么我们的工作就是,解析Post请求方法的请求体,将文件内容、文件名拆分出来,然后在服务器指定路径下建立同名文件
2.1 文件上传的回顾(弃用)
1. 导入文件上传的jar包
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
2. 编写文件上传的JSP页面
<form action="user/fileupload1" method="post" enctype="multipart/form-data">
选择文件:<input type="file" name="upload" /><br/>
<input type="submit" value="上传" />
</form>
3. 编写文件上传的Controller控制器
/**
* 文件上传(弃用)
*
* @return
*/
@RequestMapping("/fileupload1")
public String fileuoload1(HttpServletRequest request) throws Exception {
System.out.println("文件上传...");
// 使用fileupload组件完成文件上传
// 上传的位置
//String path = request.getSession().getServletContext().getRealPath("/uploads/");
String path = request.getSession().getServletContext().getContextPath() + "/uploads/";///springmvc_day02_02_fileupload_war/uploads/
//System.out.println(request.getSession());//org.apache.catalina.session.StandardSessionFacade@4c2048cc
//System.out.println(request.getSession().getServletContext().getContextPath() + "/uploads/");///springmvc_day02_02_fileupload_war
// 判断,该路径是否存在
File file = new File(path);
if (!file.exists()) {
// 创建该文件夹
file.mkdirs();
}
// 解析request对象,获取上传文件项
DiskFileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
// 解析request
List<FileItem> items = upload.parseRequest(request);
System.out.println(items);
// 遍历
for (FileItem item : items) {
System.out.println(item);
// 进行判断,当前item对象是否是上传文件项
if (item.isFormField()) {
// 说明普通表单项
System.out.println("普通表单向");
} else {
// 说明上传文件项
// 获取上传文件的名称
String filename = item.getName();
System.out.println(filename);
// 把文件的名称设置唯一值,uuid
String uuid = UUID.randomUUID().toString().replace("-", "");
filename = uuid + "_" + filename;
// 完成文件上传
item.write(new File(path, filename));
// 删除临时文件
item.delete();
}
}
return "success";
}
2.2 SpringMVC传统方式文件上传
0.原理
SpringMVC框架提供了MultipartFile对象,该对象表示上传的文件
注意:形参变量名称必须和前端表单<input type="file" name="upload"/>的 name属性名称相同。
与上面的第三方工具比较的优势:
1.不需要引入jar包,减少依赖
2.不需要再解析request,springMVC封装好了,在前端控制器中调用文件解析器对象来帮助解析。
3.代码简洁,干净。
1. 导入文件上传的jar包
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
2. 编写文件上传的JSP页面
<form action="user/fileupload2" method="post" enctype="multipart/form-data">
选择文件:<input type="file" name="upload" /><br/>
<input type="submit" value="上传" />
</form>
3. 编写文件上传的Controller控制器
/**
* SpringMVC文件上传
*
* @return
*/
@RequestMapping("/fileupload2")
public String fileuoload2(HttpServletRequest request, MultipartFile upload) throws Exception {
System.out.println("springmvc文件上传...");
// 使用fileupload组件完成文件上传
// 上传的位置
String path = request.getSession().getServletContext().getRealPath("/uploads/");
// 判断,该路径是否存在
File file = new File(path);
if (!file.exists()) {
// 创建该文件夹
file.mkdirs();
System.out.println(1);
}
System.out.println(path);
// 说明上传文件项
// 获取上传文件的名称
String filename = upload.getOriginalFilename();
System.out.println(filename);
// 把文件的名称设置唯一值,uuid
String uuid = UUID.randomUUID().toString().replace("-", "");
filename = uuid + "_" + filename;
// 完成文件上传
upload.transferTo(new File(path, filename));
return "success";
}
4.spingmvc.xml
<!-- 配置文件解析器对象,要求id名称必须是multipartResolver -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="10485760"/>
</bean>
2.3springmvc跨服务器方式的文件上传
1. 搭建图片服务器
2.导入开发需要的jar包
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-core</artifactId>
<version>1.18.1</version>
</dependency>
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-client</artifactId>
<version>1.18.1</version>
</dependency>
3. 编写文件上传的JSP页面
<h3>跨服务器的文件上传</h3>
<form action="user/fileupload3" method="post" enctype="multipart/form-data">
选择文件:<input type="file" name="upload"/><br/>
<input type="submit" value="上传文件"/>
</form>
4.编写控制器
/**
* 跨服务器文件上传
*
* @return
*/
@RequestMapping("/fileupload3")
public String fileuoload3(MultipartFile upload) throws Exception {
System.out.println("跨服务器文件上传...");
// 定义上传文件服务器路径
String path = "http://localhost:9090/uploads/";
// 说明上传文件项
// 获取上传文件的名称
String filename = upload.getOriginalFilename();
System.out.println(filename);
// 把文件的名称设置唯一值,uuid
String uuid = UUID.randomUUID().toString().replace("-", "");
filename = uuid + "_" + filename;
// 创建客户端的对象
Client client = Client.create();
// 和图片服务器进行连接
WebResource webResource = client.resource(path + filename);
// 上传文件
webResource.post(upload.getBytes());
return "success";
}
编写处理文件的工具类
我们将上述程序中对文件的处理封装成抽象类FileUtil
:
public class FileUtil {
// 上传文件
public static void uploadFile(byte[] file, String filePath, String fileName) throws Exception {
File targetFile = new File(filePath);
if (!targetFile.exists()) {
targetFile.mkdirs();
}
FileOutputStream out = new FileOutputStream(filePath + fileName);
out.write(file);
out.flush();
out.close();
}
// 删除文件,返回值表示是否删除成功
public static boolean deleteFile(String fileName) {
File file = new File(fileName);
// 如果文件路径所对应的文件存在,并且是一个文件,则直接删除
if (file.exists() && file.isFile()) {
if (file.delete()) {
return true;
} else {
return false;
}
} else {
return false;
}
}
// 重命名文件
public static String renameToUUID(String fileName) {
return UUID.randomUUID() + "." + fileName.substring(fileName.lastIndexOf(".") + 1);
}
}
保存文件的操作可简化为如下三句:
String fileName = FileUtil.renameToUUID(uploadFile.getOriginalFilename());
String filePath = request.getSession().getServletContext().getRealPath("/uploads/");
FileUtil.uploadFile(uploadFile.getBytes(), filePath, fileName);
第三章 SpringMVC 中的异常处理
3.1 异常处理的思路
系统中异常包括两类:预期异常和运行时异常 RuntimeException,前者通过捕获异常从而获取异常信息, 后者主要通过规范代码开发、测试通过手段减少运行时异常的发生。 系统的 dao、service、controller 出现都通过 throws Exception 向上抛出,最后由 springmvc 前端 控制器交由异常处理器进行异常处理
3.2 实现步骤
3.2.1编写异常类和错误页面
/**
* 自定义异常类
*/
public class SysException extends Exception{
// 存储提示信息的
private String message;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public SysException(String message) {
this.message = message;
}
}
jsp:
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head>
<title>执行失败</title>
</head>
<body>
${errorMsg}
</body>
</html>
3.2.2 自定义异常处理器
/**
* 异常处理器
*/
public class SysExceptionResolver implements HandlerExceptionResolver {
/**
* 处理异常业务逻辑
*
* @param request
* @param response
* @param handler
* @param ex
* @return
*/
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
// 获取到异常对象
SysException e = null;
//如果抛出的是系统自定义异常则直接转换
if (ex instanceof SysException) {
e = (SysException) ex;
} else {
//如果抛出的不是系统自定义异常则重新构造一个系统错误异常
e = new SysException("系统正在维护....");
}
// 创建ModelAndView对象
ModelAndView mv = new ModelAndView();
mv.addObject("errorMsg", e.getMessage());
mv.setViewName("error");
return mv;
}
}
3.2.3 配置异常处理器
3.2.3测试结果
3.3流程
原理:所有的异常都会经过前端控制器,所以前端控制器可以自定义拦截(处理)识别的异常e,响应自己写的异常e处理结果(友好界面),这样就不会给用户看到tomcat的报错结果了
第4章 SpringMVC 中的拦截器
4.1 拦截器的作用
Spring MVC 的处理器拦截器类似于 Servlet 开发中的过滤器 Filter,用于对处理器进行预处理和后处理。
用户可以自己定义一些拦截器来实现特定的功能。
谈到拦截器,还要向大家提一个词——拦截器链(Interceptor Chain)。拦截器链就是将拦截器按一定的顺序联结成一条链。在访问被拦截的方法或字段时,拦截器链中的拦截器就会按其之前定义的顺序被调用。
说到这里,可能大家脑海中有了一个疑问,这不是我们之前学的过滤器吗?是的它和过滤器是有几分相似,但
是也有区别,接下来我们就来说说他们的区别:
过滤器是 servlet 规范中的一部分,任何 java web 工程都可以使用。
拦截器是 SpringMVC 框架自己的,只有使用了 SpringMVC 框架的工程才能用。
过滤器在 url-pattern 中配置了/*之后,可以对所有要访问的资源拦截。
拦截器它是只会拦截访问的控制器方法,如果访问的是 jsp,html,css,image 或者 js 是不会进行拦截的。
它也是 AOP 思想的具体应用。
我们要想自定义拦截器, 要求必须实现:HandlerInterceptor 接口。
4.2 自定义拦截器的步骤
4.2.1 第一步:编写一个普通类实现 HandlerInterceptor
实现了两个类(两个拦截器)
/**
* 自定义拦截器
*/
public class MyInterceptor1 implements HandlerInterceptor {
/**
* 预处理,controller方法执行前
* return true 放行,执行下一个拦截器,如果没有,执行controller中的方法
* return false不放行
*
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
/**
* 预处理回调方法,实现处理器的预处理(如检查登陆),第三个参数为响应的处理器,自定义Controller
* 返回值:true表示继续流程(如调用下一个拦截器或处理器);false表示流程中断(如登录检查失败),
* 不会继续调用其他的拦截器或处理器,此时我们需要通过response来产生响应;
*/
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle执行了...前1111");
// request.getRequestDispatcher("/WEB-INF/pages/error.jsp").forward(request,response);
return true;
}
/**
* 后处理方法,controller方法执行后,success.jsp执行之前
*
* @param request
* @param response
* @param handler
* @param modelAndView
* @throws Exception
*/
/**
* 后处理回调方法,实现处理器的后处理(但在渲染视图之前),此时我们可以通过modelAndView(模型和视图对象)
* 对模型数据进行处理或对视图进行处理,modelAndView也可能为null。
*/
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle执行了...后1111");
// request.getRequestDispatcher("/WEB-INF/pages/error.jsp").forward(request,response);
}
/**
* success.jsp页面执行后,该方法会执行
*
* @param request
* @param response
* @param handler
* @param ex
* @throws Exception
*/
/**
* 整个请求处理完毕回调方法,即在视图渲染完毕时回调,如性能监控中我们可以在此记录结束时间并输出消耗时间,
* 还可以进行一些资源清理,类似于try-catch-finally中的finally,但仅调用处理器执行链中
*/
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("MyInterceptor1执行了...最后1111");
}
}
/**
* 自定义拦截器
*/
public class MyInterceptor2 implements HandlerInterceptor{
/**
* 预处理,controller方法执行前
* return true 放行,执行下一个拦截器,如果没有,执行controller中的方法
* return false不放行
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle2执行了...前2222");
// request.getRequestDispatcher("/WEB-INF/pages/error.jsp").forward(request,response);
return true;
}
/**
* 后处理方法,controller方法执行后,success.jsp执行之前
* @param request
* @param response
* @param handler
* @param modelAndView
* @throws Exception
*/
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle2执行了...后2222");
// request.getRequestDispatcher("/WEB-INF/pages/error.jsp").forward(request,response);
}
/**
* success.jsp页面执行后,该方法会执行
* @param request
* @param response
* @param handler
* @param ex
* @throws Exception
*/
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion2执行了...最后2222");
}
}
4.2.2 第二步:配置拦截器
<!--配置拦截器-->
<mvc:interceptors>
<!--配置拦截器-->
<mvc:interceptor>
<!--要拦截的具体的方法-->
<mvc:mapping path="/user/*"/>
<!--不要拦截的方法
<mvc:exclude-mapping path=""/>
-->
<!--配置拦截器对象-->
<bean class="cn.itcast.controller.cn.itcast.interceptor.MyInterceptor1" />
</mvc:interceptor>
<!--配置第二个拦截器-->
<mvc:interceptor>
<!--要拦截的具体的方法-->
<mvc:mapping path="/**"/>
<!--不要拦截的方法
<mvc:exclude-mapping path=""/>
-->
<!--配置拦截器对象-->
<bean class="cn.itcast.controller.cn.itcast.interceptor.MyInterceptor2" />
</mvc:interceptor>
</mvc:interceptors>
4.2.3 测试运行结果:
4.3 拦截器的细节
4.3.1 拦截器的放行
放行的含义是指,如果有下一个拦截器就执行下一个,如果该拦截器处于拦截器链的最后一个,则执行控制器中的方法。
4.3.2 拦截器中方法的说明
看4.2.1 第一步:编写一个普通类实现 HandlerInterceptor 的代码
4.3.3 拦截器的作用路径
看4.2.2 第二步:配置拦截器的代码
4.4 拦截器的简单案例(验证用户是否登录)
4.4.1 实现思路
1、有一个登录页面,需要写一个 controller 访问页面
2、登录页面有一提交表单的动作。需要在 controller 中处理。
2.1、判断用户名密码是否正确
2.2、如果正确 向 session 中写入用户信息
2.3、返回登录成功。
3、拦截用户请求,判断用户是否登录
3.1、如果用户已经登录。放行
3.2、如果用户未登录,跳转到登录页面
4.4.2 拦截器代码
public class LoginInterceptor implements HandlerInterceptor{
@Override
Public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
//如果是登录页面则放行
if(request.getRequestURI().indexOf("login.action")>=0){
return true;
}
HttpSession session = request.getSession();
//如果用户已登录也放行
if(session.getAttribute("user")!=null){
return true;
}
//用户没有登录跳转到登录页面
request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request,
response);
return false;
}
}