SpringMVC 基础知识二分析

你猜 阅读:304 2021-03-31 14:13:53 评论:0

第一:响应数据和结果视图

1.1 返回值分类

1.1.1 字符串

controller 方法返回字符串可以指定逻辑视图名,通过视图解析器解析为物理视图地址。 
//指定逻辑视图名,经过视图解析器解析为 jsp 物理路径:/WEB-INF/pages/success.jsp 
@RequestMapping("/testReturnString") 
public String testReturnString() { 
    System.out.println("AccountController 的 testReturnString 方法执行了。。。。"); 
    return "success";  
}

1.1.2 void

在SpringMVC基础知识一的学习中,我们知道 Servlet 原始 API 可以作为控制器中方法的参数: 
@RequestMapping("/testReturnVoid") 
public void testReturnVoid(HttpServletRequest request,HttpServletResponse response)  
throws Exception { 
     
} 
在 controller 方法形参上可以定义 request 和 response,使用 request 或 response 指定响应结果: 
1、使用 request 转向页面,如下: 
request.getRequestDispatcher("/WEB-INF/pages/success.jsp").forward(request,  
response); 
 
2、也可以通过 response 页面重定向: 
response.sendRedirect("testRetrunString")  
 
3、也可以通过 response 指定响应结果,例如响应 json 数据: 
response.setCharacterEncoding("utf-8"); 
response.setContentType("application/json;charset=utf-8"); 
response.getWriter().write("json 串");

1.1.3 ModelAndView

ModelAndView SpringMVC 为我们提供的一个对象,该对象也可以用作控制器方法的返回值。 该对象中有两个方法:
示例代码:
package com.zzg.panda.controller; 
 
import org.springframework.stereotype.Controller; 
import org.springframework.web.bind.annotation.RequestMapping; 
import org.springframework.web.servlet.ModelAndView; 
 
@Controller 
@RequestMapping("/model") 
public class ModelAndViewerController { 
	/** 
	 * 返回 ModeAndView 
	 *  
	 * @return 
	 */ 
	@RequestMapping("/testReturnModelAndView") 
	public ModelAndView testReturnModelAndView() { 
		ModelAndView mv = new ModelAndView(); 
		mv.addObject("username", "张三"); 
		mv.setViewName("model"); 
		return mv; 
	} 
} 

JSP 页面路径:WEB-INF/pages/model.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" 
    pageEncoding="UTF-8"%> 
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> 
<html> 
<head> 
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 
<title>Insert title here</title> 
</head> 
<body> 
	${requestScope.username} 
</body> 
</html>

1.2 转发和重定向

1.2.1 forward 转发

controller 方法在提供了 String 类型的返回值之后,默认就是请求转发。我们也可以写成: 
 
	/** 
	* 转发 
	* @return 
	*/ 
	@RequestMapping("/testForward") 
	public String testForward() { 
		System.out.println("AccountController 的 testForward 方法执行了。。。。"); 
		return "forward:/WEB-INF/pages/success.jsp";  
	} 
 
注意,如果用了 formward:则路径必须写成实际视图 url,不能写逻辑视图。 
它相当于“request.getRequestDispatcher("url").forward(request,response)”。使用请求 
转发,既可以转发到 jsp,也可以转发到其他的控制器方法

1.2.2 Redirect 重定向

contrller 方法提供了一个 String 类型返回值之后,它需要在返回值里使用:redirect: 
 
    /** 
	* 重定向 
	* @return 
	*/ 
	@RequestMapping("/testRedirect") 
	public String testRedirect() { 
		System.out.println("AccountController 的 testRedirect 方法执行了。。。。"); 
		return "redirect:testReturnModelAndView";  
	} 
 
它相当于“response.sendRedirect(url)”。需要注意的是,如果是重定向到 jsp 页面,则 jsp 页面不 
能写在 WEB-INF 目录中,否则无法找到。

1.3 ResponseBody 响应 json 数据

功能说明:

该注解用于将 Controller 的方法返回的对象,通过 HttpMessageConverter 接口转换为指定格式的 
数据如:json,xml 等,通过 Response 响应给客户端

示例代码:

前提条件说明:

    Springmvc 默认用 MappingJacksonHttpMessageConverter 对 json 数据进行转换,需要加入 
jackson 的包。

Controller 层

package com.zzg.panda.controller; 
 
import org.springframework.stereotype.Controller; 
import org.springframework.web.bind.annotation.RequestBody; 
import org.springframework.web.bind.annotation.RequestMapping; 
import org.springframework.web.bind.annotation.ResponseBody; 
 
import com.zzg.panda.entity.Account; 
 
@Controller 
@RequestMapping("/json") 
public class JSONController { 
	/** 
	* 测试响应 json 数据 
	*/ 
	@RequestMapping("/testResponseJson") 
	@ResponseBody  
	public Account testResponseJson(@RequestBody Account account) { 
		System.out.println("异步请求:"+account); 
		return account; 
	} 
} 

JSP 页面

<%@ page language="java" contentType="text/html; charset=UTF-8" 
	pageEncoding="UTF-8"%> 
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> 
<html> 
<head> 
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 
<title>异步请求</title> 
</head> 
<body> 
	<!-- 测试异步请求 --> 
	<input type="button" value="测试 ajax 请求 json 和响应 json" id="testJson" /> 
</body> 
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script> 
<script type="text/javascript"> 
$(function(){ 
	$("#testJson").click(function(){ 
		$.ajax({ 
			type:"post", 
			url:"${pageContext.request.contextPath}/json/testResponseJson", 
			contentType:"application/json;charset=utf-8", 
			data:'{"id":1,"name":"test","money":999.0}', 
			dataType:"json", 
			success:function(data){ 
				alert(data); 
			} 
		}); 
	}); 
}) 
</script> 
</html>

 

第二:SpringMVC 文件上传

2.1 文件上传原理

2.1.1 文件上传必要条件:

A form 表单的 enctype 取值必须是:multipart/form-data 
(默认值是:application/x-www-form-urlencoded) 
enctype:是表单请求正文的类型 
B method 属性取值必须是 Post 
C 提供一个文件选择域<input type=”file” />

2.1.2 文件上传原理分析

当 form 表单的 enctype 取值不是默认值后,request.getParameter()将失效。 enctype=”application/x-www-form-urlencoded”时,form 表单的正文内容是: 
key=value&key=value&key=value 
当 form 表单的 enctype 取值为 Mutilpart/form-data 时,请求正文内容就变成: 
每一部分都是 MIME 类型描述的正文 
-----------------------------7de1a433602ac 分界符 
Content-Disposition: form-data; name="userName" 协议头 
aaa 协议的正文 
-----------------------------7de1a433602ac 
Content-Disposition: form-data; name="file";  
filename="C:\Users\zhy\Desktop\fileupload_demofile\b.txt" 
Content-Type: text/plain 协议的类型(MIME 类型) 
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb 
-----------------------------7de1a433602ac--

示例代码:

前提条件说明: 文件上传采用第三方jar,依赖的相关jar 包,如下截图:

文件上传一般流程:

1、添加commons-io 和commons-fileupload 等第三方的jar 包依赖:

        <!--文件上传依jar 包 --> 
		<dependency> 
			<groupId>commons-io</groupId> 
			<artifactId>commons-io</artifactId> 
			<version>2.4</version> 
		</dependency> 
		<dependency> 
			<groupId>commons-fileupload</groupId> 
			<artifactId>commons-fileupload</artifactId> 
			<version>1.3.1</version> 
		</dependency>

2、编写文件上传jsp页面(fileUpload.jsp)

<%@ page language="java" contentType="text/html; charset=UTF-8" 
    pageEncoding="UTF-8"%> 
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> 
<html> 
<head> 
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 
<title>文件上传</title> 
</head> 
<body> 
	<form action="file/fileUpload" method="post" enctype="multipart/form-data"> 
		名称:<input type="text" name="picname"/><br/> 
		图片:<input type="file" name="uploadFile"/><br/> 
		<input type="submit" value="上传"/> 
	</form> 
</body> 
</html>

3、编写文件上传Controller 层代码

package com.zzg.panda.controller; 
 
import java.io.File; 
import java.text.SimpleDateFormat; 
import java.util.Date; 
import java.util.UUID; 
import javax.servlet.ServletContext; 
import javax.servlet.http.HttpServletRequest; 
import org.apache.commons.lang3.StringUtils; 
import org.springframework.stereotype.Controller; 
import org.springframework.web.bind.annotation.RequestMapping; 
import org.springframework.web.multipart.MultipartFile; 
 
/** 
 * 文件上传类 
 *  
 * @author Administrator 
 * 
 */ 
@Controller 
@RequestMapping("/file") 
public class FileController { 
	/** 
	* 文件上传 
	*/ 
	@RequestMapping("/fileUpload") 
	public String testResponseJson(String picname,MultipartFile uploadFile,HttpServletRequest request) throws Exception{ 
		//定义文件名 
		String fileName = ""; 
		//1.获取原始文件名 
		String uploadFileName = uploadFile.getOriginalFilename(); 
		//2.截取文件扩展名 
		String extendName =  
		uploadFileName.substring(uploadFileName.lastIndexOf(".")+1, uploadFileName.length()); 
		//3.把文件加上随机数,防止文件重复 
		String uuid = UUID.randomUUID().toString().replace("-", "").toUpperCase(); 
		//4.判断是否输入了文件名 
		if(!StringUtils.isEmpty(picname)) { 
			fileName = uuid+"_"+picname+"."+extendName;  
		}else { 
			fileName = uuid+"_"+uploadFileName; 
		} 
			System.out.println(fileName); 
			//2.获取文件路径 
			ServletContext context = request.getServletContext(); 
			String basePath = context.getRealPath("/uploads"); 
			//3.解决同一文件夹中文件过多问题 
			String datePath = new SimpleDateFormat("yyyy-MM-dd").format(new Date()); 
			//4.判断路径是否存在 
			File file = new File(basePath+"/"+datePath); 
			if(!file.exists()) { 
				file.mkdirs(); 
			} 
			//5.使用 MulitpartFile 接口中方法,把上传的文件写到指定位置 
			uploadFile.transferTo(new File(file,fileName)); 
			return "success";  
		} 
 
} 

4、SpringMVC 配置文件添加关于文件上传的相关配置

<!-- 配置文件上传解析器 -->  
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> 
	<!-- 设置上传文件的最大尺寸为 5MB -->  
	<property name="maxUploadSize">  
		<value>5242880</value> 
	</property> 
</bean> 
注意: 
文件上传的解析器 id 是固定的,不能起别的名称,否则无法实现请求参数的绑定。(不光是文件,其他 
字段也将无法绑定)

第三:SpringMVC 异常处理

        系统中异常包括两类:预期异常和运行时异常 RuntimeException,前者通过捕获异常从而获取异常信息,
后者主要通过规范代码开发、测试通过手段减少运行时异常的发生。
        系统的 dao、service、controller 出现都通过 throws Exception 向上抛出,最后由 springmvc 前端
控制器交由异常处理器进行异常处理,如下图:

SpringMVC 异常处理一般流程:

1、编写自定义异常类和错误信息页面

package com.zzg.panda.exception; 
 
@SuppressWarnings("serial") 
public class CustomerException extends Exception { 
	private String message; 
 
	public String getMessage() { 
		return message; 
	} 
 
	public void setMessage(String message) { 
		this.message = message; 
	} 
 
	public CustomerException(String message) { 
		super(); 
		this.message = message; 
	} 
	 
} 
<%@ page language="java" contentType="text/html; charset=UTF-8" 
	pageEncoding="UTF-8"%> 
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
"http://www.w3.org/TR/html4/loose.dtd"> 
<html> 
<head> 
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 
<title>执行失败</title> 
</head> 
<body>执行失败! ${message } 
</body> 
</html>

 

2、自定义异常处理类

package com.zzg.panda.exception.handler; 
 
import javax.servlet.http.HttpServletRequest; 
import javax.servlet.http.HttpServletResponse; 
 
import org.springframework.web.servlet.HandlerExceptionResolver; 
import org.springframework.web.servlet.ModelAndView; 
 
import com.zzg.panda.exception.CustomerException; 
 
/** 
 * 自定义异常处理 
 *  
 * @author Administrator 
 * 
 */ 
public class CustomExceptionResolver implements HandlerExceptionResolver { 
 
	@Override 
	public ModelAndView resolveException(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception ex) { 
		ex.printStackTrace(); 
		CustomerException customException = null; 
		// 如果抛出的是系统自定义异常则直接转换 
		if (ex instanceof CustomerException) { 
			customException = (CustomerException) ex; 
		} else { 
			// 如果抛出的不是系统自定义异常则重新构造一个系统错误异常。 
			customException = new CustomerException("系统错误,请与系统管理 员联系!"); 
		} 
		ModelAndView modelAndView = new ModelAndView(); 
		modelAndView.addObject("message", customException.getMessage()); 
		modelAndView.setViewName("error"); 
		return modelAndView; 
	} 
 
} 

3、SpringMVC 配置文件配置异常处理类

	<!-- 配置自定义异常处理器 -->  
	<bean id="handlerExceptionResolver" class="com.zzg.panda.exception.handler.CustomExceptionResolver"/> 

4、异步接口请求,抛出自定义异常:

package com.zzg.panda.controller; 
 
import org.springframework.stereotype.Controller; 
import org.springframework.web.bind.annotation.RequestBody; 
import org.springframework.web.bind.annotation.RequestMapping; 
import org.springframework.web.bind.annotation.ResponseBody; 
 
import com.zzg.panda.entity.Account; 
import com.zzg.panda.exception.CustomerException; 
 
@Controller 
@RequestMapping("/json") 
public class JSONController { 
	/** 
	* 测试响应 json 数据 
	 * @throws CustomerException  
	*/ 
	@RequestMapping("/testResponseJson") 
	@ResponseBody  
	public Account testResponseJson(@RequestBody Account account) throws CustomerException { 
		// 抛出自定义异常 
				throw new CustomerException("自定义异常抛出"); 
	} 
} 

效果截图:

 第四: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 接口

package com.zzg.panda.interceptor; 
 
import javax.servlet.http.HttpServletRequest; 
import javax.servlet.http.HttpServletResponse; 
 
import org.springframework.web.servlet.HandlerInterceptor; 
import org.springframework.web.servlet.ModelAndView; 
 
/** 
 * 自定义拦截器 
 * @author Administrator 
 * 
 */ 
public class CustomerInterceptor implements HandlerInterceptor { 
 
	@Override 
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) 
			throws Exception { 
		// TODO Auto-generated method stub 
		System.out.println("afterCompletion 方法执行了"); 
	} 
 
	@Override 
	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, 
			ModelAndView modelAndView) throws Exception { 
		// TODO Auto-generated method stub 
		System.out.println("postHandle 方法执行了"); 
	} 
 
	@Override 
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) 
			throws Exception { 
		System.out.println("preHandle 拦截器拦截了"); 
		return true; 
	} 
	 
} 

4.2.2 SpringMVC 配置文件配置自定义拦截器

 
	<!-- 自定义拦截器 --> 
	<mvc:interceptors> 
		<mvc:interceptor> 
			<mvc:mapping path="/**" /> 
			<bean id="customerInterceptor" 
				class="com.zzg.panda.interceptor.CustomerInterceptor"></bean> 
		</mvc:interceptor> 
	</mvc:interceptors>

效果截图:

 

4.3 拦截器细节说明

4.3.1 拦截器的放行

放行的含义是指,如果有下一个拦截器就执行下一个,如果该拦截器处于拦截器链的最后一个,则执行控制器中的方法。

 

 4.3.2拦截器的方法说明

public interface HandlerInterceptor { 
/** 
* 如何调用: 
* 按拦截器定义顺序调用 
* 何时调用: 
* 只要配置了都会调用 
* 有什么用: 
* 如果程序员决定该拦截器对请求进行拦截处理后还要调用其他的拦截器,或者是业务处理器去 
* 进行处理,则返回 true。 * 如果程序员决定不需要再调用其他的组件去处理请求,则返回 false。 */ 
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { 
    return true; 
} 
/** 
* 如何调用: 
* 按拦截器定义逆序调用 
* 何时调用: 
* 在拦截器链内所有拦截器返成功调用 
* 有什么用: 
* 在业务处理器处理完请求后,但是 DispatcherServlet 向客户端返回响应前被调用, 
* 在该方法中对用户请求 request 进行处理。 
*/ 
default void postHandle(HttpServletRequest request, HttpServletResponse  
response, Object handler, @Nullable ModelAndView modelAndView) throws Exception { 
} 
 
    /** 
* 如何调用: 
* 按拦截器定义逆序调用 
* 何时调用: 
* 只有 preHandle 返回 true 才调用 
* 有什么用: * 在 DispatcherServlet 完全处理完请求后被调用, 
* 可以在该方法中进行一些资源清理的操作。 
*/ 
default void afterCompletion(HttpServletRequest request, HttpServletResponse  
response, Object handler, @Nullable Exception ex) throws Exception { 
}  
 
} 
思考: 
如果有多个拦截器,这时拦截器 1 的 preHandle 方法返回 true,但是拦截器 2 的 preHandle 方法返 
回 false,而此时拦截器 1 的 afterCompletion 方法是否执行?

 4.3.3拦截器的配置说明

作用路径可以通过在配置文件中配置。 
<!-- 配置拦截器的作用范围 --> 
<mvc:interceptors>  
    <mvc:interceptor>  
        <mvc:mapping path="/**" /><!-- 用于指定对拦截的 url -->  
        <mvc:exclude-mapping path=""/><!-- 用于指定排除的 url-->  
        <bean id="customerInterceptor" 
				class="com.zzg.panda.interceptor.CustomerInterceptor"></bean> 
    </mvc:interceptor> 
</mvc:interceptors>

4.3.4拦截链的执行顺序

多个拦截器是按照配置的顺序决定的。
4.3.5拦截链的配置文件
<!-- 自定义拦截器 --> 
	<mvc:interceptors> 
		<mvc:interceptor> 
			<mvc:mapping path="/**" /> 
			<bean id="customerInterceptor" 
				class="com.zzg.panda.interceptor.CustomerInterceptor"></bean> 
		</mvc:interceptor> 
		<mvc:interceptor> 
			<mvc:mapping path="/**" /> 
			<bean id="customerInterceptorCopy" 
				class="com.zzg.panda.interceptor.CustomerInterceptorCopy"></bean> 
		</mvc:interceptor> 
	</mvc:interceptors>
package com.zzg.panda.interceptor; 
 
import javax.servlet.http.HttpServletRequest; 
import javax.servlet.http.HttpServletResponse; 
 
import org.springframework.web.servlet.HandlerInterceptor; 
import org.springframework.web.servlet.ModelAndView; 
 
/** 
 * 自定义拦截器 
 * @author Administrator 
 * 
 */ 
public class CustomerInterceptorCopy implements HandlerInterceptor { 
 
	@Override 
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) 
			throws Exception { 
		// TODO Auto-generated method stub 
		System.out.println("copy拦截器:afterCompletion 方法执行了"); 
	} 
 
	@Override 
	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, 
			ModelAndView modelAndView) throws Exception { 
		// TODO Auto-generated method stub 
		System.out.println("copy拦截器:postHandle 方法执行了"); 
	} 
 
	@Override 
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) 
			throws Exception { 
		System.out.println("copy拦截器:preHandle 拦截器拦截了"); 
		return true; 
	} 
	 
} 

4.3.6拦截链的测试

 
声明

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

发表评论
搜索
KIKK导航

KIKK导航

排行榜
关注我们

一个IT知识分享的公众号