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>
View Code

  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";
    }
View Code

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 omcatapache-tomcat-8.5.31 empupload_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>
pom.xml

    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";
    }
View Code

失败了呵呵呵呵呵呵呵呵呵呵。。。

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;
    }

}
View Code

  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. postHandlecontroller方法执行后执行的方法,在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> 标签内配置

原文地址:https://www.cnblogs.com/gezi1007/p/12892499.html