1. 响应数据和结果视图
1.1 返回值分类
1.1.1 返回 String
-
原理
controller 方法返回字符串可以指定逻辑视图名,通过视图解析器解析为物理视图地址。
-
实现
-
在 springmvc 中配置视图解析器
<!-- 配置视图解析器 --> <bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/pages/"></property> <property name="suffix" value=".jsp"></property> </bean>
-
UserController
@Controller @RequestMapping("/user") public class UserController { @RequestMapping("/testString") public String testString(Model model){ // 模拟从数据库中查询出 User 对象 User user = new User(); user.setName("张三"); user.setAge(18); user.setGender("男"); model.addAttribute("user1",user); // 指定逻辑视图名,经过视图解析器解析为 jsp 物理路径:/WEB-INF/pages/success.jsp return "success"; } }
-
success.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %> <html> <head> <title>Title</title> </head> <body> <h1>访问成功</h1> ${user1} </body> </html>
-
1.1.2 返回 void
-
原理
如果控制器的方法返回值编写成 void,执行程序报 404 的异常,因为默认查找 JSP 页面没有找到。(默认查找 @RequestMapping 中配置的地址.jsp)
-
可以使用请求转发或者重定向跳转到指定的页面
@Controller @RequestMapping("/user") public class UserController { @RequestMapping("/testVoid") public void testVoid(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 请求转发 // request.getRequestDispatcher("/WEB-INF/pages/success.jsp").forward(request, response); // 重定向 // response.sendRedirect(request.getContextPath()+"/index.jsp"); } }
1.1.3 返回 ModelAndView 对象
-
原理
ModelAndView 对象是 Spring 提供的一个对象,可以用来调整具体的 JSP 视图
-
实例
@Controller @RequestMapping("/user") public class UserController { @RequestMapping("/testModelAndView") public ModelAndView testModelAndView(){ // 创建ModelAndView对象 ModelAndView mv = new ModelAndView(); // 模拟从数据库中查询出 User 对象 User user = new User(); user.setName("张三"); user.setAge(18); user.setGender("男"); // 把 user 对象存储到 mv 对象中,也会把 user 对象存入到 request 对象 mv.addObject("user1",user); // 跳转到哪个页面 mv.setViewName("success"); return mv; } }
1.2 转发和重定向
-
原理
使用关键字的方式进行转发或者重定向
-
实现
/** * 使用关键字的方式进行转发或者重定向 * @return */ @RequestMapping("/testForwardOrRedirect") public String testForwardOrRedirect(){ // 请求的转发 // return "forward:/WEB-INF/pages/success.jsp"; // 重定向 return "redirect:/index.jsp"; }
1.3 ResponseBody 响应 JSON 数据
1.3.1 作用
@ResponseBody 注解实现将 controller 方法返回 JavaBean 转换为 JSON 响应给客户端。
1.3.2 需求
使用 @RequestBody 注解获取请求体数据并封装为 JavaBean,再使用 @ResponseBody 注解实现将 controller 方法返回的 JavaBean 转换为 JSON 响应给客户端,从而实现客户端和服务端的交互。
1.3.3 环境搭建
-
因为 DispatcherServlet(前端控制器)会拦截所有的资源,所以静态资源(img、css、js)也会被拦截,从而静态资源不能被使用。所以要先配置静态资源不被拦截,在 springmvc.xml 配置文件添加如下配置
<!-- 设置静态资源不过滤 --> <mvc:resources location="/css/" mapping="/css/**"/> <!-- 样式 --> <mvc:resources location="/images/" mapping="/images/**"/> <!-- 图片 --> <mvc:resources location="/js/" mapping="/js/**"/> <!-- javascript -->
-
导入 jackson 的依赖
Springmvc 默认用 MappingJacksonHttpMessageConverter 对 json 数据进行转换,需要加入 jackson 的包。
<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>
1.3.4 实现
User
public class User implements Serializable {
private String name;
private Integer age;
private String gender;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
", gender='" + gender + '\'' +
'}';
}
}
response.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<script src="js/jquery-3.3.1.min.js"></script>
</head>
<script>
function fun() {
$.ajax({
// 编写json格式,设置属性和值
url:"user/testJSON",
contentType:"application/json;charset=UTF-8",
data:JSON.stringify({"name":"jack","age":19,"gender":"man"}),
dataType:"json",
type:"post",
success:function(data){
alert(data);
alert(data.name);
}
});
}
</script>
<body>
<input type="button" onclick="fun()" value="发送ajax请求">
</body>
</html>
UserController
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/testJSON")
public @ResponseBody User testJSON(@RequestBody User user){
System.out.println(user);
return user;
}
}
2. SpringMVC 实现文件上传
2.1 传统方式文件上传
-
导入文件上传的依赖
<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>
-
编写文件上传的 jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <h3>文件上传</h3> <form action="user/fileupload1" method="post" enctype="multipart/form-data"> 选择文件:<input type="file" name="upload"/><br/> <input type="submit" value="上传文件"/> </form> </body> </html>
-
编写文件上传的控制器
@Controller @RequestMapping("/user") public class UserController { @RequestMapping("/fileupload1") public String fileuoload1(HttpServletRequest request) throws Exception { // 使用fileupload组件完成文件上传 // 上传的位置 String path = request.getSession().getServletContext().getRealPath("/uploads/"); // 判断,该路径是否存在 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); // 遍历 for(FileItem item:items){ // 进行判断,当前item对象是否是上传文件项 if(item.isFormField()){ // 说明普通表单向 }else{ // 说明上传文件项 // 获取上传文件的名称 String filename = item.getName(); // 把文件的名称设置唯一值,uuid String uuid = UUID.randomUUID().toString().replace("-", ""); filename = uuid+"_"+filename; // 完成文件上传 item.write(new File(path,filename)); // 删除临时文件 item.delete(); } } return "success"; } }
2.2 SpringMVC 传统方式文件上传
-
原理
-
实现
-
在 springmvc.xml 中配置文件解析器对象
<!-- 配置文件解析器对象,要求id名称必须是multipartResolver --> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <property name="maxUploadSize" value="10485760"/> </bean>
-
编写文件上传的 jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <h3>文件上传</h3> <form action="user/fileupload2" method="post" enctype="multipart/form-data"> 选择文件:<input type="file" name="upload"/><br/> <input type="submit" value="上传文件"/> </form> </body> </html>
-
编写文件上传的控制器
@RequestMapping("/fileupload2") public String fileuoload2(HttpServletRequest request, MultipartFile upload) throws Exception { // 使用fileupload组件完成文件上传 // 上传的位置 String path = request.getSession().getServletContext().getRealPath("/uploads/"); // 判断,该路径是否存在 File file = new File(path); if(!file.exists()){ // 创建该文件夹 file.mkdirs(); } // 获取上传文件的名称 String filename = upload.getOriginalFilename(); // 把文件的名称设置唯一值,uuid String uuid = UUID.randomUUID().toString().replace("-", ""); filename = uuid+"_"+filename; // 完成文件上传 upload.transferTo(new File(path,filename)); return "success"; }
-
2.3 SpringMVC 跨服务器方式文件上传
-
跨服务器文件上传的目的
在实际开发中,我们会有很多处理不同功能的服务器。 分服务器处理的目的是让服务器各司其职,从而提高我们项目的运行效率。
-
实现
-
搭建两台服务器,一台作为应用服务器(端口号:8080),一台作为文件服务器(端口号:9090)
-
在 springmvc.xml 中配置文件解析器对象
<!-- 配置文件解析器对象,要求id名称必须是multipartResolver --> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <property name="maxUploadSize" value="10485760"/> </bean>
-
在应用服务器导入开发所需的依赖
<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>
-
在应用服务器编写文件上传的 JSP 页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <h3>文件上传</h3> <form action="user/fileupload3" method="post" enctype="multipart/form-data"> 选择文件:<input type="file" name="upload"/><br/> <input type="submit" value="上传文件"/> </form> </body> </html>
-
在应用服务器编写文件上传的控制器
/** * 跨服务器文件上传 * @return */ @RequestMapping("/fileupload3") public String fileuoload3(MultipartFile upload) throws Exception { // 定义上传文件服务器路径 String path = "http://localhost:9090/uploads/"; // 说明上传文件项 // 获取上传文件的名称 String filename = upload.getOriginalFilename(); // 把文件的名称设置唯一值,uuid String uuid = UUID.randomUUID().toString().replace("-", ""); filename = uuid+"_"+filename; // 创建客户端的对象 Client client = Client.create(); // 和图片服务器进行连接 WebResource webResource = client.resource(path + filename); // 上传文件 webResource.put(upload.getBytes()); return "success"; }
-
-
几种报错问题的解决办法
以下三种错误我都遇到了:
-
错误 400
这是由于你上传的文件名是中文,解决办法就是改成英文
-
错误403
-
打开你本地的 apache-tomcat-x.x.x\conf 中的 web.xml,找到下面的内容
<servlet> <servlet-name>default</servlet-name> <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class> <init-param> <param-name>debug</param-name> <param-value>0</param-value> </init-param> <init-param> <param-name>listings</param-name> <param-value>false</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet>
-
添加 readonly:flase 标签,如下:
<servlet> <servlet-name>default</servlet-name> <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class> <init-param> <param-name>debug</param-name> <param-value>0</param-value> </init-param> <init-param> <param-name>readonly</param-name> <param-value>false</param-value> </init-param> <init-param> <param-name>listings</param-name> <param-value>false</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet>
-
重新启动服务器
-
-
错误 409
在文件服务器中的 target/项目名下,新建一个 uploads 文件夹
-
3. SpringMVC 的异常处理
3.1 异常处理的思路
系统的 dao、service、controller 出现异常都向上抛出,最后由 springmvc 前端控制器交由异常处理器进行异常处理
3.2 实现步骤
-
编写异常类
package com.zt.exception; public class SysException extends Exception { private String message; public SysException(String message) { this.message = message; } @Override public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } }
-
编写错误页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> ${errorMsg} </body> </html>
-
编写自定义异常处理类
public class SysExceptionResolver implements HandlerExceptionResolver { @Override public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) { // 获取到异常对象 SysException sysException = null; if(e instanceof SysException){ sysException = (SysException)e; }else{ e = new SysException("系统正在维护...."); } // 创建ModelAndView对象 ModelAndView mv = new ModelAndView(); mv.addObject("errorMsg",e.getMessage()); mv.setViewName("error"); return mv; } }
-
在 springmvc.xml 中配置异常处理器
<!--配置异常处理器--> <bean id="sysExceptionResolver" class="com.zt.exception.SysExceptionResolver"></bean>
-
编写控制器
@Controller @RequestMapping("/user") public class UserController { @RequestMapping("/testException") public String testException() throws SysException { try { int a = 1/0; } catch (Exception e) { e.printStackTrace(); throw new SysException("出错了"); } return "success"; } }
-
编写 JSP 页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <a href="user/testException">testException</a> </body> </html>
4. SpringMVC 的拦截器
4.1 拦截器的概念
Spring MVC 的处理器拦截器类似于 Servlet 开发中的过滤器 Filter,用于对处理器进行预处理和后处理。
4.2 拦截器和过滤器的区别
- 过滤器是 Servlet 规范的一部分,任何 java web 工程都可以使用过滤器。拦截器是 SpringMVC 框架独有的。
- 过滤器可以拦截任何资源。拦截器只会对控制器中的方法进行拦截。
4.3 拦截器链的概念
拦截器链,连接器链就是将拦截器按着一定的顺序结成一条链,在访问被拦截的方法时,拦截器链中的拦截器会按着定义的顺序执行。
4.4 拦截器的实现
-
自定义拦截器
public class MyIntercepter implements HandlerInterceptor { /** * 预处理,controller方法执行前 * return true 放行,执行下一个拦截器,如果没有,执行 controller 中的方法 * return false 不放行 * @param request * @param response * @param handler * @return */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { System.out.println("preHandle..."); return true; } /** * 后处理方法,controller方法执行后,success.jsp执行之前 * @param request * @param response * @param handler * @param modelAndView */ @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) { System.out.println("postHandle..."); } /** * success.jsp页面执行后,该方法会执行 * @param request * @param response * @param handler * @param ex */ @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { System.out.println("afterCompletion..."); } }
-
在 springmvc.xml 中配置拦截器
<!--设置拦截器--> <mvc:interceptors> <mvc:interceptor> <!--要拦截的方法--> <mvc:mapping path="/user/*"/> <!--配置拦截对象--> <bean class="com.zt.intercepter.MyIntercepter"></bean> </mvc:interceptor> </mvc:interceptors>
-
编写控制器
@Controller @RequestMapping("/user") public class UserController { @RequestMapping("/testInterceptor") public String testInterceptor(){ System.out.println("testInterceptor..."); return "success"; } }
-
打印结果
preHandle... testInterceptor... postHandle... success.jsp afterCompletion...
4.5 HandlerInterceptor 接口中的方法
-
preHandle 方法是 controller 方法执行前拦截的方法
-
可以使用 request 或者 response 跳转到指定的页面
-
return true 放行,执行下一个拦截器,如果没有拦截器,执行 controller 中的方法。
-
return false 不放行,不会执行 controller 中的方法。
-
-
postHandle 是 controller 方法执行后执行的方法,在 JSP 视图执行前。
- 可以使用 request 或者 response 跳转到指定的页面
- 如果指定了跳转的页面,那么 controller 方法跳转的页面将不会显示。
-
postHandle 方法是在 JSP 执行后执行
- request 或者 response 不能再跳转页面了
4.6 配置拦截器链
-
在配置一个自定义拦截器
-
在 springmvc.xml 中再配置一个拦截器
<!--设置拦截器--> <mvc:interceptors> <mvc:interceptor> <!--要拦截的方法--> <mvc:mapping path="/user/*"/> <!--配置拦截对象--> <bean class="com.zt.intercepter.MyIntercepter"></bean> </mvc:interceptor> <mvc:interceptor> <!--要拦截的方法--> <mvc:mapping path="/user/*"/> <!--配置拦截对象--> <bean class="com.zt.intercepter.MyIntercepter2"></bean> </mvc:interceptor> </mvc:interceptors>