SpringMVC-Day2
第一章:响应数据和结果视图
新建模块springmvc_day02_01_response
1. 返回值分类
1. 返回字符串
1). Controller中的方法返回字符串可以指定逻辑视图的名称,根据视图解析器为物理视图的地址
2). 具体的应用场景
response.jsp中点击对应方法mapping的超链接:
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <a href="user/testString">testString</a> </body> </html>
ControllerUser.java
@Controller @RequestMapping("/user") public class UserController { @RequestMapping("/testString") public String testString(Model model){ System.out.println("testString方法执行..."); // 模拟从数据库中查询出User对象 User user = new User(); user.setUsername("张三"); user.setPassword("123"); user.setAge(30); //model对象 // 底层会存储到!request域!对象中,域中会显示有这么一个键值对 model.addAttribute("user",user); return "success"; } }
点击超链接,服务器执行方法后根据视图解析器跳转到返回值success.jsp 设置允许el表达式
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %> <html> <head> <title>成功</title> </head> <body> <h3>执行成功</h3> <p>从request域中拿值:<br> 用户名: ${user.username}<br> 密码:${user.password} </p> </body> </html>
2. 返回值是void(没有视图解析器的事儿了。。手动写转发。。
1). 如果控制器的方法返回值编写成void,执行程序报404的异常,默认查找JSP页面没有找到。
(可以用原生Servlet的API,就是传入HttpServletRequest req和res为参数,然后用req.getRequestDispatcher("/WEB-INF/pages/success.jsp").forword(req,res); 转发到对应路径。
这个路径作为转发的话就不用写项目名称啥的,是在服务器端的转发,直接写从当前找***.jsp的路径。如果是res的重定向就是二次请求,是浏览器端的,需要写全路径获取虚拟目录,一般用req.getContextPath()+"***.jsp"。)
1. 默认会跳转到@RequestMapping(value="/initUpdate") initUpdate的页面。
2. 可以使用请求转发或者重定向跳转到指定的页面
@RequestMapping("/testVoid") public void testVoid(HttpServletRequest request, HttpServletResponse response) throws Exception { System.out.println("testVoid方法执行了..."); //1. 编写请求转发的程序 request.getRequestDispatcher("/WEB-INF/pages/success.jsp").forward(request,response); // 2.或者重定向 response.sendRedirect(request.getContextPath()+"/index.jsp"); // 设置中文乱码 response.setCharacterEncoding("UTF-8"); response.setContentType("text/html;charset=UTF-8"); // 3.或者不用跳转直接会进行响应 response.getWriter().print("你好"); return; }
运行结果:在response.jsp里面点链接会跳转
3. 返回值是ModelAndView对象(也是使用视图解析器跳转,跟返回字符串差不多的写法,返回字符串那个底层也会用到ModelAndView对象)
1). ModelAndView对象是Spring提供的一个对象,可以用来调整具体的JSP视图
2). 具体的代码如下
/** * 返回ModelAndView * @return */ @RequestMapping("/testModelAndView") public ModelAndView testModelAndView(){ // 创建ModelAndView对象 ModelAndView mv = new ModelAndView(); System.out.println("testModelAndView方法执行了..."); // 模拟从数据库中查询出User对象 User user = new User(); user.setUsername("李四"); user.setPassword("456"); user.setAge(30); // 把user对象存储到mv对象中,也会把user对象存入到request对象 mv.addObject("user",user); // 跳转到哪个页面 mv.setViewName("success"); return mv; }
运行结果:点击链接后跳转到success.jsp
2. SpringMVC框架提供的转发和重定向
1. forward请求转发
1). controller方法返回String类型,想进行请求转发也可以编写成
2. redirect重定向
1). controller方法返回String类型,想进行重定向也可以编写成
/** * 使用关键字的方式进行转发或者重定向 * @return */ @RequestMapping("/testForwardOrRedirect") public String testForwardOrRedirect(){ System.out.println("testForwardOrRedirect方法执行了..."); // 请求的转发 forward是关键字,此时也不能使用视图解析器了 // return "forward:/WEB-INF/pages/success.jsp"; // 重定向 redirect是关键字 这里的重定向底层已经把项目名加上了 // (但是重定向无法获取里面的jsp?只能在webapp包下的?? return "redirect:/index.jsp"; }
3. ResponseBody响应json数据
首先搭建一个ajax异步的环境。(ajax就是按住大部分网页不动也不用等待服务器啥的,只对小部分进行与服务器的交互比如接收服务器的数据,大部分不用等小部分。。。
webapp下面新建js包,把jquery.min.js拷贝到js文件夹下面。在response.jsp里面引入,然后写点击按钮发送异步请求。
response.jsp:
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> <script src="js/jquery.min.js"></script> <script> // 页面加载,绑定单击事件 $(function(){ $("#btn").click(function(){ // alert("hello btn"); // 发送ajax请求 $.ajax({ // 编写json格式的字符串,设置属性和值 url:"user/testAjax",//后台这里对应路径的方法就可以执行了!!! contentType:"application/json;charset=UTF-8", data:'{"username":"zhangsan","password":"123","age":30}', dataType:"json", type:"post", success:function(data){//回调函数!!success后执行 // data服务器端响应的json的数据,进行解析 alert(data); alert(data.username); alert(data.password); alert(data.age); } }); }); }); </script> </head> <body> <a href="user/testString">testString</a> <br> <a href="user/testVoid">testViod</a> <br> <a href="user/testModelAndView">testModelAndView</a> <br> <a href="user/testForwardOrRedirect">testForwardOrRedirect</a> <br> <%--button的mapping写在scrpit的ajax绑的那里--%> <button id="btn">发送ajax的请求</button> </body> </html>
1. DispatcherServlet会拦截到所有的资源,导致一个问题就是静态资源(img、css、js)也会被拦截到,从而不能被使用。解决问题就是需要配置静态资源不进行拦截,在springmvc.xml配置文件添加如下配置
1). mvc:resources标签配置不过滤
1. location元素表示webapp目录下的包下的所有文件
2. mapping元素表示以/static开头的所有请求路径,如/static/a 或者/static/a/b
<!--告诉前端控制器,哪些静态资源不要拦截 js下面的--> <!-- 设置静态资源不过滤 --> <mvc:resources location="/css/" mapping="/css/**"/> <!-- 样式 --> <mvc:resources location="/images/" mapping="/images/**"/> <!-- 图片 --> <mvc:resources location="/js/" mapping="/js/**"/> <!-- javascript -->
2. 使用@RequestBody获取请求体数据
@RequestMapping("/testAjax") public void testAjax(@RequestBody String body){ System.out.println("testAjax方法执行了..."); System.out.println(body);}
运行结果:就跟之前day1的一样。。。这里忘复制了。。。
3. 使用@RequestBody注解把json的字符串转换成JavaBean的对象(需要在jsp中json类型的data的key值与属性名一致,需要依赖一个jar包jackson,就是json与javabean相互转换的那个,在pom中引入坐标,3个!!!)
<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>
controller中改String为User对象,由于前端传的json的key与属性是对应的,所以可直接获取和打印了
@RequestMapping("/testAjax") public void testAjax(@RequestBody User user){ System.out.println("testAjax方法执行了..."); // 客户端发送ajax的请求,传的是json字符串,后端把json字符串封装到user对象中 System.out.println(user);}
4. 使用@ResponseBody注解!!!把JavaBean对象转换成json字符串,直接响应
1). 要求方法需要返回JavaBean的对象
写有返回值的可以响应给前端,把从前端异步接收的json转成对象拿到后,经过服务器修改,又响应返回给前端以json显示。
@RequestMapping("/testAjax") public @ResponseBody User testAjax(@RequestBody User user){//请求体,响应体 两个注解!!!!3个jar包!完成互相转换!!! System.out.println("testAjax方法执行了..."); // 客户端发送ajax的请求,传的是json字符串,后端把json字符串封装到user对象中 System.out.println(user); // 做响应,模拟查询数据库 user.setUsername("lisi"); user.setAge(40); // 做响应 return user; }
完成json字符串和JavaBean对象互相转换的过程中,需要使用jackson的jar包
运行结果:
前端response.jsp 在异步请求里发的json数据是zhangsan,123,30,
然后到达服务器方法里成功通过@RequestBody和jackson拿到了封装好前端传来数据的User
服务器中改了这个User的部分数据,然后通过@ResponseBody注解和3个jar包又响应给前端
前端回调函数果然显示的是已经被服务器改掉了的json数据,没改的密码就还没变,名字年龄变化了。
这个其实课题也有用,因为服务器给客户端传最终的定位坐标数据应该也是异步的,不影响其他。(比如这里是点击按钮接受一个异步的数据由弹窗获得,而我的课题是走一步触发新的定位数据到方法里,然后方法调业务层方法计算出数据返回给客户端!就看要不要封装成实体类了,应该是要)
第二章:SpringMVC实现文件上传
1. 文件上传的回顾
(?web学过吗?只记得文件下载webday15学过,文件上传没有丝毫印象。。。
新建模块springmvc_day02_02_fileupload
普通超链接a和herf指向controller的方法执行(啊啊啊也可以带参数比如day01里面很多超链接传参注解,比如
<a href="anno/testRequestParam?myname=哈哈">RequestParam</a>
)。。。表单是带着请求参数指向方法
“/”开头代表根目录 一般herf里面不是,但reqmapping是带/的
1. 编写文件上传的JSP页面
A :form 表单的 enctype 取值必须是:multipart/form-data (可以切割请求体)
默认值是:application/x-www-form-urlencoded) enctype:是表单请求正文的类型
B :method 属性取值必须是 Post
C :提供一个文件选择域<input type=”file” />
index.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="上传"> <%--现在点文件 点上传 就会跳到controller里面的上传方法--%> </form> </body> </html>
这个编完点选择文件后面的input框是真的可以上传文件!神奇!(有点熟悉了。。好像web里是经历过这个,学前端的时候好像。。。
点上传的话就会执行controller里面的方法的业务!所以接下来是在controller里面写在服务器端接收文件的业务逻辑!
2. 导入文件上传的jar包(commons-fileupload)
<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>
3. 编写文件上传的Controller控制器
表单都封装在request里面,拿的话用request对象
/** * 文件上传 * @return */ @RequestMapping("/fileupload1") public String fileUpload1(HttpServletRequest request) throws Exception { System.out.println("文件上传。。。"); //使用fileuploa组件完成文件上传 //上传的位置:uploads的根目录 String path = request.getSession().getServletContext().getRealPath("/uploads/");// request.getServletContext();竟然报红。。。非得加个getSession才行,与web学的不一样。。。 System.out.println(path); //判断该路径是否存在 File file = new File(path); if(!file.exists()){ //创建该文件夹 file.mkdirs(); } //解析request对象,获取上传的文件项 DiskFileItemFactory factory = new DiskFileItemFactory();//组件里的磁盘文件项工厂 ServletFileUpload upload = new ServletFileUpload(factory);//组件里的上传 //解析request List<FileItem> fileItems = upload.parseRequest(request); //遍历快捷键:iter for (FileItem fileItem : fileItems) { //进行判断当前fileitem对象是否是上传文件项 if(fileItem.isFormField()){ //说明是普通表单项 }else {//说明是上传文件项 //获取到上传文件的名称 String fileItemName = fileItem.getName(); // 把文件的名称设置唯一值,uuid String uuid = UUID.randomUUID().toString().replace("-", ""); fileItemName = uuid+"_"+fileItemName; //完成文件上传 fileItem.write(new File(path,fileItemName)); //删除临时文件 fileItem.delete(); } } return "success"; }
500的错误。。。
Type Exception Report
Message Request processing failed; nested exception is org.apache.commons.fileupload.FileUploadBase$IOFileUploadException: Processing of multipart/form-data request failed. D:\LenovoQMDownload\tomcat\apache-tomcat-8.5.31\temp\upload_940f5a15_ae3c_41f5_81e6_46473baa7c39_00000007.tmp (拒绝访问。)
Description The server encountered an unexpected condition that prevented it from fulfilling the request.
---------------------------------------------------------------------------------
经过我的不懈试验发现,这个是10kb以下大小可以成功保存,超过10kb就报该错误。不知该怎么办。。。。狗生艰难。。。
(没事,后面的可以10M都ok!!!不要用这个传统方法传文件了,用MVC框架的方法吧!!
卧槽卧槽卧槽我又试了一下传统方法莫名其妙又可以了。。。迷惑服务器行为。。。。。。。。。但是根本找不到文件。。。耍我。。。ok放弃这个吧)
2. SpringMVC传统方式文件上传
文件解析器替代了前边使用jar包得到List然后遍历判断等等的工作。
1. SpringMVC框架提供了MultipartFile对象,该对象表示上传的文件,要求变量名称必须和表单file标签的 name属性名称相同。
2. 代码如下
/** * springMVC文件上传 * @return */ @RequestMapping("/fileupload2") public String fileUpload2(HttpServletRequest request, MultipartFile upload) throws Exception { System.out.println("文件上传2。。。"); //使用fileuploa组件完成文件上传 //上传的位置:uploads的根目录 String path = request.getSession().getServletContext().getRealPath("/uploads/");
// request.getServletContext();竟然报红。。。非得加个getSession才行,与web学的不一样。。。 System.out.println(path); //判断该路径是否存在 File file = new File(path); if(!file.exists()){ //创建该文件夹 file.mkdirs(); } //说明是上传文件项 //获取到上传文件的名称 String fileItemName = upload.getOriginalFilename(); // 把文件的名称设置唯一值,uuid String uuid = UUID.randomUUID().toString().replace("-", ""); fileItemName = uuid+"_"+fileItemName; //完成文件上传 upload.transferTo(new File(path,fileItemName)); return "success"; }
3. 配置文件解析器对象
<!-- 配置文件解析器对象,要求id名称必须是multipartResolver --> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <property name="maxUploadSize" value="10485760"/> </bean>
成功上传!!!!
3. SpringMVC跨服务器方式文件上传
实际开发中有很多负责不同模块的服务器们
1. 搭建图片服务器
1). 根据文档配置tomcat9的服务器,现在是2个服务器,springmvc和fileupload
2). 导入资料中day02_springmvc5_02image项目,作为图片服务器使用
2. 实现SpringMVC跨服务器方式文件上传
1). 导入开发需要的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>
2). 编写文件上传的JSP页面
和之前一样的一组表单
3. 编写控制器
/** * 跨服务器文件上传 往别的服务器传 * @return */ @RequestMapping("/fileupload3") public String fileUpload3(HttpServletRequest request, MultipartFile upload) throws Exception { System.out.println("文件上传3。。。"); //定义上传服务器的路径 String path = "http://localhost:8090/uploads/"; //说明是上传文件项 //获取到上传文件的名称 String fileItemName = upload.getOriginalFilename(); // 把文件的名称设置唯一值,uuid String uuid = UUID.randomUUID().toString().replace("-", ""); fileItemName = uuid+"_"+fileItemName; //实现往别的服务器传 需要导入jersey的两个jar包 //创建客户端对象 Client client = Client.create(); //和图片服务器进行连接(我用的01response模块) WebResource webResource = client.resource(path + fileItemName); //上传文件 webResource.put(upload.getBytes()); return "success"; }
失败了呵呵呵呵呵呵呵呵呵呵。。。
HTTP Status 500 – Internal Server Error
Type Exception Report
Message Request processing failed; nested exception is com.sun.jersey.api.client.UniformInterfaceException: PUT http://localhost:8090/uploads/7b154a5ab31b49db953c8173f690c43b_lessthan10kb.txt returned a response status of 404 Not Found
Description The server encountered an unexpected condition that prevented it from fulfilling the request.
有缘再解决吧
第三章:SpringMVC的异常处理
系统中异常包括两类:预期异常和运行时异常RuntimeException,前者通过捕获异常从而获取异常信息,后者主要通过规范代码开发、测试通过手段减少运行时异常的发生。
1. 异常处理思路
1. Controller调用service,service调用dao,异常都是向上抛出的,最终有DispatcherServlet找异常处理器进行异常的处理。
新建模块springmvc_day02_03_exception
如果不写异常处理器则直接500,404之类的,很不友好
2. SpringMVC的异常处理
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) {//构造方法 则new异常类时必须传提示信息 this.message = message; } }
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());//异常信息键值对存到req域里 mv.setViewName("error");//跳到error.jsp 可以用el显示上面存的 return mv; } }
3. 配置异常处理器
<!--配置异常处理器--> <bean id="sysExceptionResolver" class="cn.itcast.exception.SysExceptionResolver"/>
实现思路:
1.自定义一个异常处理器,实现HandlerExceptionResolver接口,跳转至指定的error,jsp
2.写一个error.jsp,打印异常处理器给出的异常原因
2.写一个异常类,继承Exception类,给一个message属性以及它的getters&setters,构造方法。
3.在Usercontroller类中模拟异常,在catch中throw new 异常处理器对象
4.在springmvc.xml中写一个bean,配置异常处理器。
第四章:SpringMVC框架中的拦截器
1. 拦截器的概述
1. SpringMVC框架中的拦截器用于对处理器Controller进行预处理和后处理的技术。山贼(类似过滤器,过滤器拦截后台的资源)
2. 可以定义拦截器链,连接器链就是将拦截器按着一定的顺序结成一条链,在访问被拦截的方法时,拦截器链中的拦截器会按着定义的顺序执行。
3. 拦截器和过滤器的功能比较类似,有区别
1). 过滤器是Servlet规范的一部分,任何框架和java web工程都可以使用过滤器技术。(sun公司的)
2). 拦截器是SpringMVC框架独有的。
3). 过滤器配置了/*,可以拦截任何资源。
4). 拦截器只会对控制器中的方法进行拦截。(不拦资源)
4. 拦截器也是AOP思想的一种实现方式
5. 想要自定义拦截器,需要实现HandlerInterceptor接口。
2. 自定义拦截器步骤
新建springmvc_day02_04_interceptor模块
1. 创建类,实现HandlerInterceptor接口,重写需要的方法
/** * 自定义拦截类 */ public class Myinterceptor1 implements HandlerInterceptor { /** * 预处理,controller方法执行前,进行拦截的方法 * return true 放行,执行下一个拦截器,如果没有,执行controller中的方法 * return false不放行 * @param request * @param response * @param handler * @return * @throws Exception * 可以使用转发或者重定向直接跳转到指定的页面。 */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("MyInterceptor1执行了。。。"); return true; } }
2. 在springmvc.xml中配置拦截器类
<!--配置拦截器--> <mvc:interceptors> <mvc:interceptor> <!--你要拦截的具体方法--> <mvc:mapping path="/user/*"/> <!--你不要拦截的方法--> <!--<mvc:exclude-mapping path=""--> <!--配置拦截器对象--> <bean class = "com.xxw.interceptor.Myinterceptor1"/> </mvc:interceptor> </mvc:interceptors>
3. HandlerInterceptor接口中的方法
1. preHandle方法是controller方法执行前拦截的方法(预处理)
1). 可以使用request或者response跳转到指定的页面
2). return true放行,执行下一个拦截器,如果没有拦截器,执行controller中的方法。
3). return false不放行,不会执行controller中的方法。(写个跳转直接跳页面了,就req.getDispater.forward转发走了
2. postHandle是controller方法执行后执行的方法,在JSP视图执行前。(后处理)
1. 可以使用request或者response跳转到指定的页面
2. 如果指定了跳转的页面,那么controller方法跳转的页面将不会显示。
3. postHandle方法是在JSP执行后执行
1. request或者response不能再跳转页面了
4.afterCompletion方法 jsp页面执行后,该方法最终执行
/** * 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("MyInterceptor1执行了...最后1111"); }
4. 配置多个拦截器
1. 再编写一个拦截器的类
Myinterceptor2.java
2. 配置2个拦截器
再写一个 <mvc:interceptor> 标签内配置