springMVC框架学习-2
springMVC框架学习安排
第二天
1. 响应数据和结果视图
1. 响应返回值分类
1. 响应String字符串
@RequestMapping("testString")
public String testString(Model model){
User user = new User();
user.setUname("liuzeyu");
user.setAge(100);
model.addAttribute("user",user);
return "success";
}
<%@page contentType="text/html; charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head>
<title>入门成功</title>
</head>
<body>
<h2>执行成功</h2>
${
requestScope.user.uname}<br>
${
requestScope.user.age}
</body>
</html>
2. 响应void
<a href="user/testVoid">测试返回void</a>
@RequestMapping("testVoid")
public void testVoid() throws Exception {
System.out.println("testVoid......");
}
如果以上面的形式返回,则将报错404
出现此问题是无返回值时,springMVC默认把请求的路径当作转发的路径使用了,解决的办法有两个:
- 转发:
@RequestMapping("testVoid")
public void testVoid(HttpServletRequest request, HttpServletResponse response) throws Exception {
System.out.println("testVoid......");
request.getRequestDispatcher("/WEB-INF/pages/success.jsp").forward(request,response);
}
回顾请求转发的特点:
1. 请求只发送一次
2. 浏览器地址栏路径不会改变
3. 只能转发服务内部的资源,这里是webapp下的资源,必须由 / 开头
- 重定向:
@RequestMapping("testVoid")
public void testVoid(HttpServletRequest request, HttpServletResponse response) throws Exception {
System.out.println("testVoid......");
response.sendRedirect(request.getContextPath()+"/index.jsp");
}
回顾重定向特点:
1.浏览器地址栏会发送变化,如上图
2.可以重定向到服务器外部资源
3.重定向会发送两个请求,成功的话对应的状态码分别是302,200
3. 响应ModelAndView
<a href="user/testModelAndView">测试返回ModelAndView</a>
@RequestMapping("testModelAndView")
public ModelAndView testModelAndView(){
System.out.println("testModelAndView......");
ModelAndView mv = new ModelAndView();
User user = new User();
user.setUname("liuzeyu");
user.setAge(100);
mv.addObject("user",user);
mv.setViewName("success"); //设置转发视图
return mv;
}
4. 使用forward和redirect进行页面响应跳转
<a href="user/testForwardAndRedirect">测试返回testForwardAndRedirect</a>
@RequestMapping("testForwardAndRedirect")
public String testForwardAndRedirect(){
//return "forward:/WEB-INF/pages/success.jsp";
return "redirect:/index.jsp";
//return "redirect:/WEB-INF/pages/success.jsp"; //不能访问WEB-INF下的web文件
}
5. 为什么redirect重定向不能访问WEB-INF目录下的内容?
答案还得从WEB-INF这个目录的作用说起
WEB-INF目录是Java的Web应用安全目录,客户端是无法访问的,只有服务器能访问。将一些页面放在这个目录下可以限制外部访问,提高安全性,如一些jsp,html页面。
原因:
既然是安全目录,客户端无法访问,那重定向当然是无法访问到的,因为重定向的特点是客户端没有直接请求服务器资源,而是二次请求服务器资源,因此是属于直接访问内部资源的,这是不被允许的。
然而请求转发是请求服务器去转发访问资源,因此服务器是可以访问资源的。
6. 响应json数据
- 加载jQuery文件(也不明白为什么本地会加载失败)
<script src="http://libs.baidu.com/jquery/1.9.1/jquery.js"></script>
- 添加按钮和点击事件
<script>
$(function () {
$("#bt").click(function () {
alert("按钮被点击了....")
$.ajax({
url:"user/testAjax",
contentType:"application/json;charset=UTF-8",
type:"post",
data:'{"uname":"liuzeyu","age":"66"}',
success:function (data) {
//接收服务器响应的数据
}
});
});
});
</script>
<button id="bt"> 发送异步请求</button>
- controller层接收
@RequestMapping("testAjax")
public void testAjax(@RequestBody String body){
System.out.println(body);
}
- controller层响应
@RequestMapping("testAjax")
public @ResponseBody User testAjax(@RequestBody User user){
user.setAge(9999); //修改年龄为9999
System.out.println(user);
return user;
}
- 前端渲染
success:function (data) {
//接收服务器响应的数据
alert(data);
alert(data.uname); //liuzeyu
alert(data.age); //999
}
2. 文件上传
1. 传统的文件上传方式
1. 文件上传的必要前提
A form 表单的 enctype 取值必须是:multipart/form-data
(默认值是:application/x-www-form-urlencoded)
enctype:是表单请求正文的类型
B method 属性取值必须是 Post(get的参数浏览器栏装不下)
C 提供一个文件选择域<input type=”file” />
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--
3. 借助第三方组件实现文件上传
使用 Commons-fileupload 组件实现文件上传,需要导入该组件相应的支撑 jar 包:Commons-fileupload 和
commons-io。commons-io 不属于文件上传组件的开发 jar 文件,但Commons-fileupload 组件从 1.1 版本开始,它
工作时需要 commons-io 包的支持。
导入坐标
<!--导入传统形式上传文件依赖的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>
4. 实现步骤
- 编写JSP页面
<h2>文件上传</h2>
<form action="user/testUpload1" method="post" enctype="multipart/form-data">
选择文件:<input type="file" value="文件" name="upload">
<input type="submit" value="上传">
</form>
- controller层
@RequestMapping("/user")
@Controller
public class UserController {
@RequestMapping("testUpload1")
public String testUpload(HttpServletRequest request) throws Exception {
System.out.println("testUpload1...");
//使用fileupload组件完成文件上传
//上传位置
String realPath = request.getSession().getServletContext().getRealPath("/uploads/");
//判断该路径是否存在
File file = new File(realPath);
if(!file.exists())
{
//创建文件夹
file.mkdirs();
}
//解析request对象,获取上传文件选中
DiskFileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
//解析request
List<FileItem> fileItems = upload.parseRequest(request);
//遍历
for (FileItem item : fileItems) {
//进行判断,当前的item是否是上传文件项
if(item.isFormField()){
//表示普通的表单项
}else{
//上传文件项
//获取上传文件名称
String name = item.getName();
//把名字id设置成唯一值
String uuid = UUID.randomUUID().toString().replace("-", "");
name = uuid+ "_"+name;
//完成文件上传
item.write(new File(realPath,name));
//删除临时文件
item.delete();
}
}
return "success";
}
}
上传成功!
2. 使用springMVC文件上传方式
-
分析
-
搭建环境
<h2>文件上传2</h2>
<form action="user/testUpload2" method="post" enctype="multipart/form-data">
选择文件:<input type="file" value="文件" name="upload">
<input type="submit" value="上传">
</form>
@RequestMapping("testUpload2")
public String testUpload2(HttpServletRequest request, MultipartFile upload) {
return null;
}
- 配置文件解析器
<!--配置文件上传解析器-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!--设置上传文件的最小尺寸为5m-->
<property name="maxUploadSize" value="5242880"></property>
</bean>
- controller层代码
@RequestMapping("testUpload2")
public String testUpload2(HttpServletRequest request, MultipartFile upload) throws IOException {
System.out.println("testUpload1...");
//使用fileupload组件完成文件上传
//上传位置
String realPath = request.getSession().getServletContext().getRealPath("/uploads/");
//判断该路径是否存在
File file = new File(realPath);
if(!file.exists())
{
//创建文件夹
file.mkdirs();
}
//上传文件项
//获取上传文件名称
String name = upload.getOriginalFilename();
//把名字id设置成唯一值
String uuid = UUID.randomUUID().toString().replace("-", "");
name = uuid+ "_"+name;
//完成文件上传
upload.transferTo(new File(realPath,name));
return "success";
}
其中很重要的一步:解析request对象获取文件上传项,交给了springMVC来做。
3. springMVC跨服务器文件上传
-
搭建omcat图片服务器,要修改HTTP端口号
-
实现springMVC跨服务器文件上传
/** * 跨服务器器文件上传 * @param * @param upload * @return * @throws IOException */ @RequestMapping("testUpload3") public String testUpload3(MultipartFile upload) throws IOException { System.out.println("testUpload3 跨服务器上传"); String path = "http://localhost:9090/uploads/"; //图片服务器路径,需要手动创建一个/uplodas目录 //上传文件项 //获取上传文件名称 String name = upload.getOriginalFilename(); //把名字id设置成唯一值 String uuid = UUID.randomUUID().toString().replace("-", ""); name = uuid+ "_"+name; //创建客户端对象 Client client = Client.create(); //和图片服务器进行连接 WebResource resource = client.resource(path + name); //上传文件 resource.put(upload.getBytes()); return "success"; }
3. springMVC处理异常
存在的异常问题:
如果在dao层出现了异常,会抛向service层,再抛向…前端控制器。如果前端控制器再往浏览器抛,则用户一般看不懂,这显然对用于不友好,于是我们应该在前端控制处对异常进行处理,这个任务交给异常处理器,最后将处理好的异常发送给前端浏览器。
- jsp页面
<a href="user/testException">测试异常</a>
- controller层代码
@RequestMapping("/user")
@Controller
public class UserController {
@RequestMapping("testException")
public String testException() throws SysException {
System.out.println("发生了异常....");
try {
int i = 1/0;
} catch (Exception e) {
e.printStackTrace();
//抛出自定义的异常
throw new SysException("查询所有用户失败...");
}
return "success";
}
}
- spring配置文件
<!--配置创建springIOC容器时要扫描的包-->
<context:component-scan base-package="com.liuzeyu"></context:component-scan>
<!--配置视图解析器-->
<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>
<!--配置spring开启注解mvc的支持-->
<mvc:annotation-driven></mvc:annotation-driven>
<!--配置spring开启异常处理器的支持-->
<bean id="sysException" class="com.liuzeyu.exception.SysExceptionResolver"></bean>
- web.xml
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<!--配置servlet-->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--servlet启动后需要加载的spring配置文件-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!--处理post请求的中文乱码问题-->
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern> </filter-mapping> </web-app>
- 自定义异常类
/** * Created by liuzeyu on 2020/4/29. * 自定义异常类 */
public class SysException extends Exception {
private String message;
@Override
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public SysException(String message) {
this.message = message;
}
}
- 异常处理器
/** * Created by liuzeyu on 2020/4/29. * 异常类处理器 */
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{
sysException = new SysException("系统正在维护");
}
//创建ModleAndView对象
ModelAndView mv = new ModelAndView();
mv.addObject("errorMsg",sysException.getMessage());
mv.setViewName("error");
return mv;
}
}
4. springMVC拦截功能
1. 拦截器的作用
springMVC的拦截器类似于servlet的Filter,用于对处理器进行预处理和后处理,用户可以自定义拦截器来实现特定的功能。
谈到拦截器,还要向大家提一个词——拦截器链(Interceptor Chain)。拦截器链就是将拦截器按一定的顺序联结成一条链。在访问被拦截的方法或字段时,拦截器链中的拦截器就会按其之前定义的顺序被调用。
<mark>拦截器和过滤器的区别:</mark>
- 过滤器是servlet的规范中的一部分,任何web工程都可以使用,包括springMVC工程
拦截器
是springMVC框架自身的,只有springMVC框架能使用- 过滤器在配置了 /* 后,可以对访问的资源进行拦截
拦截器
只会拦截controller里面的方法,如果要访问某某jsp,html,或者js资源,拦截器是不会进行拦截的,它也是AOP思想的应用。
如果需要自定义拦截器,需要实现接口HandlerInterceptor
接口
2. 拦截器的入门使用
- 自定义拦截器
public class MyInterceptor implements HandlerInterceptor{
/**预处理方法: preHandle:在controller方法执行执行的拦截器方法 返回值: true:表示可以放行以下要执行的controller方法 false:表示不能执行以行的controller方法 */
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("MyInterceptor....");
return true;
}
}
- 配置拦截器
<mvc:interceptors>
<mvc:interceptor>
<!--拦截路径-->
<mvc:mapping path="/user/*"/>
<!--不拦截路径-->
<!--<mvc:exclude-mapping path=""/>-->
<!--配置拦截器对象-->
<bean class="com.liuzeyu.interceptor.MyInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
- controller方法
@RequestMapping("testInterceptor")
public String testInterceptor(){
System.out.println("测试拦截器...");
return "success";
}
测试结果:
3. 拦截器的其它两个方法
- postHandle
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("MyInterceptor....preHandle");
return true;
}
/** * 方法执行时机:controller执行之后,success.jsp执行之前 * @param request * @param response * @param handler * @param modelAndView * @throws Exception */
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("MyInterceptor....postHandle");
request.getRequestDispatcher("/WEB-INF/pages/error.jsp").forward(request,response);
}
结果:
error.jsp
注:拦截器处理转发后不会再去跳转controller里面的任何页面跳转。
- afterCompletion
/** * Created by liuzeyu on 2020/4/29. */
public class MyInterceptor implements HandlerInterceptor{
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("MyInterceptor....preHandle");
return true;
}
/** * controller执行之后,success.jsp执行之前 * @param request * @param response * @param handler * @param modelAndView * @throws Exception */
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("MyInterceptor....postHandle");
request.getRequestDispatcher("/WEB-INF/pages/error.jsp").forward(request,response);
}
/** * 最后执行方法:通常用于是否资源,在success.jsp执行后才执行 * @param request * @param response * @param handler * @param ex * @throws Exception */
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("MyInterceptor....afterCompletion");
}
}
测试结果:
4. 拦截链执行顺序
- 配置拦截器2
/** * Created by liuzeyu on 2020/4/29. */
public class MyInterceptor2 implements HandlerInterceptor{
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("MyInterceptor22222....preHandle");
return true;
}
/** * controller执行之后,success.jsp执行之前 * @param request * @param response * @param handler * @param modelAndView * @throws Exception */
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("MyInterceptor2222....postHandle");
//request.getRequestDispatcher("/WEB-INF/pages/error.jsp").forward(request,response);
}
/** * 最后执行方法:通常用于是否资源,在success.jsp执行后才执行 * @param request * @param response * @param handler * @param ex * @throws Exception */
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("MyInterceptor2222....afterCompletion");
}
}
- 配置文件
<mvc:interceptors>
<mvc:interceptor>
<!--拦截路径-->
<mvc:mapping path="/user/*"/>
<!--不拦截路径-->
<!--<mvc:exclude-mapping path=""/>-->
<!--配置拦截器对象-->
<bean class="com.liuzeyu.interceptor.MyInterceptor"/>
</mvc:interceptor>
<!--再配置一个拦截器-->
<mvc:interceptor>
<!--拦截路径-->
<mvc:mapping path="/**/"/>
<!--不拦截路径-->
<!--<mvc:exclude-mapping path=""/>-->
<!--配置拦截器对象-->
<bean class="com.liuzeyu.interceptor.MyInterceptor2"/>
</mvc:interceptor>
</mvc:interceptors>
- 执行顺序分析
结果:
可见执行顺序如上,符合拦截链的执行流程。