SpringMVC第二天

该文档是:SpringMVC框架学习...

博客连接:https://www.loveuluo.cn

日期:2021-01-08

1. 响应数据和结果视图

先搭建环境,创建maven项目引入依赖,配置web.xml文件等等...
image-20210108102618556

1.1 返回类型为字符串

controller方法返回字符串可以指定逻辑视图名,通过视图解析器解析为物理视图地址。

(1)jsp页面:

response.jsp:

<body>
    <a href="user/save">发送请求</a>
</body>

success.jsp:

<body>
    <h3>跳转成功</h3>
    ${requestScope.user.username}
    ${requestScope.user.password}
</body>

(2)UserController类:

@Controller
@RequestMapping("/user")
public class UserController {
    @RequestMapping("/save")
    public String save(Model model){
        //模拟数据库查出数据
        User user=new User();
        user.setUsername("Luo");
        user.setPassword("123456");
        //把user对象放入request域中
        model.addAttribute(user);
        //return字符串相当于请求转发
        //指定逻辑视图名,经过视图解析器解析为jsp物理路径:/WEB-INF/pages/success.jsp
        return "success";
    }
}

执行结果:

image-20210108112022218

1.2 返回类型为void

  1. 如果控制器的方法返回值编写成void,执行程序报404的异常,默认查找JSP页面没有找到。
  2. 默认会跳转到@RequestMapping(value="/testVoid") testVoid的页面。
  3. 可以使用请求转发或者重定向跳转到指定的页面

(1)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");

        //设置中文乱码
        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;c");
        //直接进行相应
        response.getWriter().write("hello");
        //可以直接retrun不让代码执行下去
        return;
    }

1.3 返回类型为ModelAndView

ModelAndView是SpringMVC为我们提供的一个对象,该对象也可以用作控制器方法的返回值。(返回为Spring底层其实也是调用的这个)

(1)该对象中有两个方法:

image-20210108132227453

image-20210108132231303

(2)UserController类:

@RequestMapping("/testModelAndView")
public ModelAndView testModelAndView(){
    //创建ModelAndView对象
    ModelAndView mv=new ModelAndView();
    //模拟数据库查出数据
    User user=new User();
    user.setUsername("Luo");
    user.setPassword("123456");
    //把user对象存入mv对象中,也会把user对象存入到requset对象中。
    mv.addObject(user);
    //要跳转到哪个页面
    mv.setViewName("success");
    //返回这个mv对象,他会使用视图解析器然后转发到setViewName设置的success.jsp页面
    return mv;
}

1.4 SpringMVC框架提供的转发和重定向(不常用)

(1)forward转发

controller方法在提供了String类型的返回值之后,默认就是请求转发。我们也可以写成:

/**
 * 使用forward关键字进行请求转发
 * "forward:转发的JSP路径",不走视图解析器了,所以需要编写完整的路径
 * @return
 * @throws Exception
 */
@RequestMapping("/delete")
public String delete() throws Exception {
    System.out.println("delete方法执行了...");
//  return "forward:/WEB-INF/pages/success.jsp";
    return "forward:/user/findAll";
}

需要注意的是,如果用了formward:则路径必须写成实际视图url,不能写逻辑视图。 它相当于request.getRequestDispatcher("url").forward(request,response)。使用请求转发,既可以转发到jsp,也可以转发到其他的控制器方法。

(2)redirect重定向

contrller方法提供了一个String类型返回值之后,它需要在返回值里使用:redirect:

/**
 * 重定向
 * @return
 * @throws Exception
 */
@RequestMapping("/count")
public String count() throws Exception {
    System.out.println("count方法执行了...");
    //不需要写工程路径,底层已经封装好了
    return "redirect:/index.jsp";
}

它相当于response.sendRedirect(url)。需要注意的是,如果是重定向到jsp页面,则jsp页面不能写在WEB-INF目录中,否则无法找到。

1.5 @ResponseBody响应json数据

(1)DispatcherServlet会拦截到所有的资源,导致一个问题就是静态资源(img、css、js)也会被拦截到,从而不能被使用。解决问题就是需要配置静态资源不进行拦截,在springmvc.xml配置文件添加如下配置:

image-20210108141036030

引入js:

image-20210108152355501

设置不拦截静态资源,修改spring.xml进行配置:

image-20210108141303229

<!-- 设置静态资源不过滤 -->
<!--顺序一定不要弄错,mapping在前,location在后-->
<!--1. mapping元素表示以/static开头的所有请求路径,如/static/a 或者/static/a/b-->
<!--2. location元素表示webapp目录下的包下的所有文件-->
<mvc:resources mapping="/css/**" location="/css/" /> <!-- 样式 -->
<mvc:resources mapping="/images/**" location="/images/"/> <!-- 图片 -->
<mvc:resources mapping="/js/**" location="/js/"/> <!-- javascript -->

(2)使用@RequestBody获取请求体数据:

jsp页面:

<head>
    <title>Title</title>
    <%--引入jquery--%>
    <script type="text/javascript" src="js/jquery-1.7.2.js"></script>
    <script type="text/javascript">
        $(function () { //入口函数
            $("#test").click(function () {
                //发送ajax请求
                $.ajax({
                    //编写json格式,设置属性和值
                    url:"user/testAjax",
                    contentType:"application/json;charset=UTF-8",
                    //data是发送给服务器的请求体
                    data:'{"username":"hehe","password":"123","age":"13"}',
                    dataType:"json",
                    type:"post",
                    //data就是服务器响应回来的数据
                    success:function (data) {

                    }
                })
            })
        })
    </script>
</head>
<body>
    <button id="test">发送ajax请求</button>
</body>

Controller类:

@RequestMapping("/testAjax")
//@RequestBody注解可以获得整个请求体的内容
public void testAjax(@RequestBody String body){
    System.out.println(body);
}

输出结果:

image-20210108155127764

(3)使用@RequestBody把json数据封装到实体类中再用@ResponseBody注解实现将controller方法返回对象转换为json响应给客户端:

json字符串和JavaBean对象互相转换的过程中,需要使用jackson的jar包:

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

jsp页面:

<head>
    <title>Title</title>
    <%--引入jquery--%>
    <script type="text/javascript" src="js/jquery-1.7.2.js"></script>
    <script type="text/javascript">
        $(function () { //入口函数
            $("#test").click(function () {
                //发送ajax请求
                $.ajax({
                    //编写json格式,设置属性和值
                    url:"user/testAjax",
                    contentType:"application/json;charset=UTF-8",
                    //data是发送给服务器的请求体
                    data:'{"username":"hehe","password":"123"}',
                    //返回的数据类型是json
                    dataType:"json",
                    type:"post",
                    //data就是服务器响应回来的数据
                    success:function (data) {
                        alert(data+"用户名"+data.username+"密码"+data.password+"年龄"+data.age)
                    }
                })
            })
        })
    </script>
</head>
<body>
    <button id="test">发送ajax请求</button>
</body>

Controller类:

@RequestMapping("/testAjax")
//@RequestBody:因为导入了jackson依赖,此时会自动把json字符串封装到user对象中,需要json的键和实体类属性名对上
//@ResponseBody:因为ajax请求中要求返回的数据格式是json,会自动把user对象变成json格式响应回去
public @ResponseBody User testAjax(@RequestBody User user){
    //假设从数据库 根据传过来的username和passoword查到用户年龄
    user.setAge(20);
    return user;
}

网页输出结果:

image-20210108161540400

2. SpringMVC实现文件上传

2.1 文件上传的回顾

文件上传的必要前提:

image-20210108164941454

文件上传的原理分析:

image-20210108165115990

image-20210108165153183

借助第三方组件实现文件上传(用这些jar包才能解析上边图片中的http协议内容):

image-20210108165226860

编写文件上传的Controller控制器(不使用SpringMVC实现文件上传的情况):

    /**
     * 文件上传
     * @throws Exception
     */
    @RequestMapping(value="/fileupload")
    public String fileupload(HttpServletRequest request) throws Exception {
        // 先获取到要上传的文件目录
        String path = request.getSession().getServletContext().getRealPath("/uploads");
        // 创建File对象,一会向该路径下上传文件
        File file = new File(path);
        // 判断路径是否存在,如果不存在,创建该路径
        if(!file.exists()) {
            file.mkdirs();
        }
        // 创建磁盘文件项工厂
        DiskFileItemFactory factory = new DiskFileItemFactory();
        ServletFileUpload fileUpload = new ServletFileUpload(factory);
        // 解析request对象
        List<FileItem> list = fileUpload.parseRequest(request);
        // 遍历
        for (FileItem fileItem : list) {
        // 判断文件项是普通字段,还是上传的文件
            if(fileItem.isFormField()) {
            }else {
            // 上传文件项
                // 获取到上传文件的名称
                String filename = fileItem.getName();
                // 上传文件
                fileItem.write(new File(file, filename));
                // 删除临时文件
                fileItem.delete();
            }
        }
        return "success";
    }

2.2 SpringMVC传统方式的文件上传

(1)分析:

image-20210108170513128

(2)同样需要引入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>

(3)先在springmvc.xml中配置文件解析器:

<!--配置文件解析器,这里的id必须为multipartResolver-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <!--里边可以配置属性,maxUploadSize代表最大允许上传文件大小-->
    <property name="maxUploadSize" value="10485760"></property>
</bean>

(4)jsp页面:

<form action="file/fileupload1" method="post" enctype="multipart/form-data">
    文件上传:<input type="file" value="选择文件" name="upload">
    <input type="submit" value="提交">
</form>

(5)编写Controller类:

SpringMVC框架提供了MultipartFile对象,该对象表示上传的文件,要求变量名称必须和表单file标签的name属性名称相同。

@Controller
@RequestMapping("/file")
public class FileController {
    @RequestMapping("fileupload1")
    // 这里的MultipartFile 参数的名称必须和type="file"的input框的name一致
    public String fileupload1(HttpServletRequest request, MultipartFile upload) throws IOException {
        // 先获取到要上传的文件目录
        String path = request.getSession().getServletContext().getRealPath("/uploads");
        System.out.println(path);

        // 创建File对象,一会向该路径下 上传文件
        File file = new File(path);
        // 判断路径是否存在,如果不存在,创建该路径(文件夹)
        if(!file.exists()) {
            file.mkdirs();
        }
        // 获取到上传的文件的名称
        String filename = upload.getOriginalFilename();
        // 把文件的名称唯一化(防止不同的人上传相同的文件名然后覆盖掉了)
        String uuid = UUID.randomUUID().toString().replaceAll("-", "").toUpperCase();
        filename = uuid+"_"+filename;
        // 上传文件(地址为file,文件名为filename)
        upload.transferTo(new File(file,filename));
        return "success";
    }
}

结果:

image-20210108183759594

image-20210108183820221

2.3 SpringMVC跨服务器方式文件上传

(1)好处:

image-20210108184650011

image-20210108184700017

(2)分析:

image-20210108184629224

(3)搭建环节,需要再来一个tomcat充当储存图片的服务器:

用模板创建maven项目,什么都不需要改,只是存放图片用的服务器:

image-20210108185335700

创建用来存放图片的文件夹:

image-20210108191805935

给这个服务器配置一个tomcat:

image-20210108185541138

需要修改tomcat的web.xml配置:

image-20210108192501477

image-20210108192544951

加入此行的含义是:接收文件的目标服务器可以支持写入操作。

(4)导入开发需要的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>
<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>

(5)jsp页面:

<form action="file/fileupload2" method="post" enctype="multipart/form-data">
    文件上传:<input type="file" value="选择文件" name="upload">
    <input type="submit" value="提交">
</form>

(6)编写Controller类:

@RequestMapping("/fileupload2")
// 这里的MultipartFile 参数的名称必须和type="file"的input框的name一致
public String fileupload2(MultipartFile upload) throws IOException {
    // 定义图片服务器的请求路径(你要传的服务器的地址和具体位置),这里注意不能漏了http://
    String path="http://localhost:9090/fileuploadserver/uploads/";

    // 获取到上传的文件的名称
    String filename = upload.getOriginalFilename();
    // 把文件的名称唯一化(防止不同的人上传相同的文件名然后覆盖掉了)
    String uuid = UUID.randomUUID().toString().replaceAll("-", "").toUpperCase();
    filename = uuid+"_"+filename;

    // 创建客户端对象
    Client client = Client.create();
    // 连接图片服务器(要放哪+文件名)
    WebResource resource = client.resource(path + "/" + filename);
    // 上传文件(需要文件的byte字节)
    resource.put(upload.getBytes());
    return "success";
}

测试结果:

image-20210108193831287

image-20210108193859823

image-20210108194034907

3. SpringMVC中的异常处理

3.1 为什么要异常处理和过程

直接让用户看到错误提示肯定不太友好,处理一下让用户看到友好的错误页面

系统中异常包括两类:预期异常和运行时异常RuntimeException,前者通过捕获异常从而获取异常信息,后者主要通过规范代码开发、测试通过手段减少运行时异常的发生。

系统的dao、service、controller出现都通过throws Exception向上抛出,最后由springmvc前端控制器交由异常处理器进行异常处理,如下图:

image-20210108200122931

3.2 实现步骤

(1)jsp页面:

<body>
    <a href="wrong/testException">异常处理</a>
</body>

(2)自定义异常类:

public class SysException extends Exception{

    //存储提示信息的
    private String message;

(3)Controller类模拟异常:

@Controller
@RequestMapping("/wrong")
public class WrongController {
    @RequestMapping("testException")
    public String testException() throws SysException {
        System.out.println("testException执行了...");

        try {
            //模拟异常
            int a = 10/0;
        } catch (Exception e) {
            //打印异常信息
            e.printStackTrace();
            //抛出自定义异常信息
            throw new SysException("查询所有用户出现错误了...");
        }
        return "success";
    }
}

(4)编写异常处理器类,并且在配置文件中配置它:

SysExceptionResolver异常处理器类:

/**
 * 异常处理器
 */
public class SysExceptionResolver implements HandlerExceptionResolver {

    /**
     * 当Controller真的把异常抛过来,而且配置了这个处理器的话,它就会调用这个处理器并且会执行这里边的方法
     * @param request
     * @param response
     * @param handler
     * @param ex
     * @return
     */
    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        //Exception ex这个参数代表的就是Controller抛过来的异常
        //获取到异常对象
        SysException e=null;
        //如果ex底层指向的是SysException的话
        if (ex instanceof SysException){
            //强转成SysException
            e = (SysException) ex;
        } else {
            //如果不是就new一个
            e=new SysException("系统正在维护...");
        }
        ModelAndView mv=new ModelAndView();
        //把错误信息存入request域中
        mv.addObject("erroMsg",e.getMessage());
        //设置要往哪个友好的错误提示页面跳转
        mv.setViewName("error");
        //返回mv,等于说一有异常那么就会带着错误信息往 error.jsp这个页面跳转
        return mv;
    }
}

在springmvc.xml中进行配置:

<!--配置异常处理器-->
<bean id="sysExceptionResolver" class="com.Luo.exception.SysExceptionResolver"></bean>

(5)编写error.jsp错误页面:

<body>
    <h3>${requestScope.erroMsg}</h3>
</body>

执行结果:

image-20210109094552210

image-20210109094612819

4. SpringMVC中的拦截器

4.1 拦截器的概述

image-20210109105357140

4.2 自定义拦截器步骤

jsp页面:

<a href="lanjie/testInterceptor">测试拦截器</a>

Controller类:

public class MyInterceptor1 implements HandlerInterceptor {
    /**
     * 预处理,controller方法执行前,进行拦截的方法
     * @return true放行
     * @return false拦截
     * 可以使用转发或者重定向直接跳转到指定的页面。
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("拦截器预处理方法执行了...");
        return true;
    }
}

在springmvc.xml中配置拦截器:

<!--配置多个拦截器-->
<mvc:interceptors>
    <!--配置拦截器-->
    <mvc:interceptor>
        <!--要拦截的具体方法,表示拦截请求为/lanjie下的所有方法-->
        <mvc:mapping path="/lanjie/*"/>
        <!--这个则表示不拦截的方法,请求为/lanqiu下的方法都不拦截
        <mvc:exclude-mapping path="/lanjie/*"/>         -->

        <!--配置自己写的拦截器对象-->
        <bean class="com.Luo.interceptor.MyInterceptor1"></bean>
    </mvc:interceptor>
</mvc:interceptors>

4.3 HandlerInterceptor接口中的方法

image-20210109124608265

4.4 配置多个拦截器

执行顺序:

image-20210109125141879

在springmvc.xml中配置拦截器:

<!--配置多个拦截器-->
<mvc:interceptors>
    <!--配置第一个拦截器-->
    <mvc:interceptor>
        <!--要拦截的具体方法,表示拦截请求为/lanjie下的所有方法-->
        <mvc:mapping path="/lanjie/*"/>
        <!--这个则表示不拦截的方法,请求为/lanqiu下的方法都不拦截
        <mvc:exclude-mapping path="/lanjie/*"/>         -->

        <!--配置自己写的拦截器对象-->
        <bean class="com.Luo.interceptor.MyInterceptor1"></bean>
    </mvc:interceptor>

    <!--配置第二个拦截器-->
    <mvc:interceptor>
        <mvc:mapping path="/lanjie/*"/>
        <bean class="com.Luo.interceptor.MyInterceptor2"></bean>
    </mvc:interceptor>
</mvc:interceptors>

4.5 拦截器的简单案例(验证用户是否登录)

(1)实现思路:

image-20210109125644516

(2)控制器代码:

//登陆页面
@RequestMapping("/login")
public String login(Model model)throws Exception{
    return "login";
}
//登陆提交
// userid:用户账号,pwd:密码
@RequestMapping("/loginsubmit")
public String loginsubmit(HttpSession session, String userid, String pwd)throws Exception{
    //向session记录用户身份信息
    session.setAttribute("activeUser", userid);
    return "redirect:/main.jsp";
}
//退出
@RequestMapping("/logout")
public String logout(HttpSession session)throws Exception{
    //session过期
    session.invalidate();
    return "redirect:index.jsp";
}

(3)拦截器代码:

image-20210109130412633

最后修改:2021 年 01 月 21 日 04 : 36 PM
如果觉得我的文章对你有用,请随意赞赏