SpringMVC-功能

SpringMVC的功能

  • 参数绑定:
    • 通常我们在从页面拿form表单参数的时候是通过这种方式去拿的:

      @RequestMapping(value = "login",method = RequestMethod.POST)
          public String login(String userName,String password){
      //       对参数进行处理
              return "tohello";
          }
      

      这种方式在当我们的参数有几十个的时候,那么将会在括号里面写一长串的参数列表了,噢,这也太不美观了,要是有一个参数写错了,那么还得挨着找,所以,SpringMVC给我们提供了很好的便利,将所有的值都封装到同一个对象里面了

    • 首先我们写一个form表单:

      <form action="/login" method="post">
          <input type="text" placeholder="用户名" name="userName">
          <input type="password" placeholder="密码" name="password">
          <button type="submit"/>提交
      </form>
      

      对应的controller:

      @RequestMapping(value = "login",method = RequestMethod.POST)
          public String login(LoginForm login){
              System.out.println(userName);
              System.out.println(login.getPassword());
              System.out.println(login.getAge());
              return "tohello";
          }
      

      写一个用来封装表单元素的类:

      /**
       * springMVC会将表单元素的值封装到这个类的对象里面,
       * 所以字段名要与表单元素的name一致,否则拿不到值.
       * 类名可以随便写,只要在对应的controller方法中引用这个类就行了
       * 当请求带着参数进来时,springMVC会将对应的参数绑定到这个类的对象上面
       */
      @Data
      public class LoginForm  {
      
          private String userName;
          private String password;
          private String age;
      }
      

      然后我们运行,在表单中输入账号 li 密码:99999 ,提交,在控制台我们可以看到:

      li
      99999
      null
      

      只要表单中的name的值与表单数据处理类的字段名一样,那么springMVC就会将数据进行数据绑定,在取的时候使用对象取就行了.

    • 并且springmvc还会将封装到对象里面的数据进行透传,什么意思呢,就是我们可以直接在页面上进行取值,就不需要再使用request.setAttribute()这种方法来传回数据了

      在页面上我们直接取值:这里的规则是将类型名的首字母小写转换为key,我们开始定义的类叫LoginForm,所以我们取值如下:
      ${loginForm.userName}
      ${loginForm.password}
      
  • 数据校验:
    • 需要导入依赖:

      <!--    参数校验,hibernate-validator-->
          <!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-validator -->
          <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-validator</artifactId>
            <version>6.1.0.Final</version>
          </dependency>
      
    • 加注解,来限定参数的类型:需要其他限制请去查看API

      @Data
      public class LoginForm  {
      
          @NotNull(message = "账号不能为空")   //不能为空,message的值可以自定义,提示语句
          @NotBlank  //非空并且不能为空白字符串
          @Length(min = 2,max = 5)  //限定参数的长度,最小为2,最大为5位
          @Email   //判断是否为email格式
          @Pattern(regexp ="可以在这里面写正则表达式")
          private String userName;
          
          private String password;
          
          private String age;
      }
      

      开始校验:在要校验的参数对象前加上@Valid注解,校验结果会放在BindResult里面

      @RequestMapping(value = "login",method = RequestMethod.POST)
          public String login(@Valid LoginForm login, BindingResult bindingResult, ModelMap modelMap){
              System.out.println(bindingResult);
              StringBuilder stringBuilder=new StringBuilder();
      //        转换校验结果
              if (bindingResult.hasErrors()){
      //            取出所有的错误的集合
                  List<FieldError> fieldErrors = bindingResult.getFieldErrors();
                  for (FieldError error:fieldErrors) {
      //                得到有错误的参数名
                      stringBuilder.append(error.getField()+",");
      //                得到此错误的提示
                      stringBuilder.append(error.getDefaultMessage()+",");
                  }
              }
      
      //        ModelMap对象主要用于传递控制方法处理数据到结果页面,
      //        也就是说我们把结果页面上需要的数据放到ModelMap对象中即可,跟使用request传值效果一样
              modelMap.put("errorMessage",stringBuilder.toString());
              return "tohello";
          }
      }
      

      在页面上,我们使用jstl直接取值:

      ${errorMessage},
      最后就会将错误信息打印出来:
      password,长度需要在1和3之间,userName,不是一个合法的电子邮件地址,userName,长度需要在2和5之间,
      
  • 类型转换
    • SpringMVC默认是不支持日期的类型自动转换的

    • 使用注解,进行日期转换:@InitBinder

       //日期转换,SpringMVC的自动转换不支持日期类
          @InitBinder
          public void init(WebDataBinder binder){
              binder.addCustomFormatter(new DateFormatter("yyyy-MM-dd HH:mm:ss"));
          }
      
      

      注意,这个的作用范围只在当前controller有效,所以,我们可以将其抽象成一个基类,往后所有的controller都来继承这个基类,就自然有他的效果了,我们的参数校验这个方法也需要在很多地方用到,所有都抽象出来:

      /**
       * 将所有controller类有的功能抽象出来
       * 基类,封装了日期转换和校验的方法
       */
      public abstract class ControllerBase {
      
          //日期转换,SpringMVC的自动转换不支持日期类
          @InitBinder
          public void init(WebDataBinder binder){
              binder.addCustomFormatter(new DateFormatter("yyyy-MM-dd HH:mm:ss"));
          }
      
          public String extractError(BindingResult result){
              //转换校验结果
              StringBuilder errorString=new StringBuilder();
              if(result.hasErrors()){
                  //将错误信息放在集合里面
                  List<FieldError> fieldErrors = result.getFieldErrors();
                  for(FieldError fieldError:fieldErrors){
                      errorString.append(fieldError.getField()+";");
                      errorString.append(fieldError.getDefaultMessage()+";");
                  }
                  return errorString.toString();
              }
              return null;
          }
      }
      
  • JSON转换:
    • 导入依赖Jackson,spring默认的json转换工具

      <properties>
          <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
          <maven.compiler.source>1.7</maven.compiler.source>
          <maven.compiler.target>1.7</maven.compiler.target>
      <!--    对jackson的依赖进行一个版本管理-->
          <jackson.version>2.10.0</jackson.version>
        </properties>
      <!--    jackson,转换json的支持-->
          <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>${jackson.version}</version>
          </dependency>
          <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>${jackson.version}</version>
          </dependency>
          <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
            <version>${jackson.version}</version>
          </dependency>
      
    • 创建一个实体类:

      @Data
      public class User {
          private String userName;
          private String password;
          private String sex;
          private int age;
      
          public User(String userName, String password, String sex, int age) {
              this.userName = userName;
              this.password = password;
              this.sex = sex;
              this.age = age;
          }
      }
      
    • 使用:在需要转换json的controller上加@ResponseBody注解

      /**
       * json转换
       */
      @Controller
      //@RestController   //如果是加RestController注解,那么这个类下面所有的方法都会被转换为json格式,并且方法上就不用再加@ResponseBody注解了
      public class JSONController extends ControllerBase {
      
          @RequestMapping("getData")
          @ResponseBody
          public User getData(){
      
              return new User("li","99999","男",22);
          }
      }
      
    • 结果:当我们访问getData时,就会得到一个json格式的对象了

      {"userName":"li","password":"99999","sex":"男","age":22}
      
  • 静态资源的处理:
    • 在使用springMVC的时候,你可能会遇到访问页面的时候,发现所有的静态资源都被拦截了,那是因为在默认情况下,所有的静态资源(css,js,html,图片,音频)都会被拦截,所以在配置的时候需要我们手动的把这些静态资源放出来.

    • 在子容器配置中添加:

      <!--    mapping:访问路径 ,location:本地路径-->
      <!--    这里的style可以自己定义,一般还是跟目录结构匹配比较方便,因为这样写的话有提示,可以避免错误-->
          <mvc:resources mapping="/style/**" location="/css/"/>
          <mvc:resources mapping="/js/**" location="/images/"/>
          <mvc:resources mapping="/images/**" location="/js/"/>
          <mvc:resources mapping="/fonts/**" location="/fonts/"/>
      
    • 使用:在要使用静态资源的地方根据配置直接使用就好了:

      <html>
      <head>
        <--  比如我要在这里导入一个css样式,因为我上面配的是/style/**,所以我这里的路径写/style/login.css,springMVC就会帮我们匹配到WEB-INF/css路径下去寻找叫login.css文件  -->
          <link rel="stylesheet" href="/style/login.css">
      </head>
      
  • 全局异常的处理:
    • 异常处理通常有两种方式:try-catch,或者是throws,当我们可以处理的异常可以使用try-catch进行处理,但是,当遇到我们不能处理的异常怎么办?比如说sql异常,有这样一个场景,用户在进行登录的时候,传的参数有问题,dao层在查询数据库的时候出现了sql异常,如果这个时候你将这个异常try-catch处理掉,当做什么都没发生,那么用户会发现自己在点了登录之后页面没有动!又或者说出现数据库抖动,这些异常我们都处理不了.

    • 对于这些不好处理的异常最好的办法就是向上抛,如果出现这些异常我们就给用户返回一个错误页面,比如说服务器正在维护呀,您访问的页面走丢了呀这样的页面.

    • 全局异常处理就相当于一个接盘的,我们肯定不能将错误一直往上抛,最后丢给tomcat处理,结果就是最后用户会看见一个tomcat的404页面.所以我们当然不能这么干,全局异常处理器就是你们都不处理,最后我来处理的意思.

    • 使用:@ControllerAdvice注解拦截所有的controller,@ExceptionHandler注解拦截所有的异常:

      /**
       * 全局异常处理器
       * 将异常都收集到一起
       */
      @ControllerAdvice  //全局的通知,会拦截所有的controller给他加入一些功能
      public class GlobalExceptionHandler {
          //异常处理器
          @ExceptionHandler
          public String handleException(Exception e, Model model){
      
              //打印堆栈信息
              e.printStackTrace();
              System.out.println("出错了"+e.getMessage());
              model.addAttribute("msg","您要访问的页面走丢啦~~~");
              //这里我们就可以使用一个漂亮的404页面返回给用户
              return "404";
          }
      }
      
  • 文件上传:
    • 依赖:commons-fileupload

      <!--    文件上传的支持-->
          <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.4</version>
          </dependency>
      
    • 配置:因为文件上传是web相关的,所以我们将其配置在子容器中:

    <!--    上传文件配置-->
        <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
            <!--最大上传大小-->
            <property name="maxUploadSize" value="10485760"/>
            <!--        最大上传文件单个大小-->
            <property name="maxInMemorySize" value="1048576"/>
        </bean>
    
    • 接收文件上传的controller,使用MultipartFile类来接收上传的文件

      @Controller
      public class UploadController {
      
          @RequestMapping("upload")
          public String uploadFile(MultipartFile myfile){
      //        得到上传的文件的名字,注意,此时的参数一定要与上传文件的name相对应,这样springMVC才会进行参数绑定,才可以拿到文件对象
              String filename = myfile.getOriginalFilename();
              System.out.println(filename);
              try {
      //            将上传的文件保存在本地
                  myfile.transferTo(new File("d://Temp//"+filename));
      
              } catch (IOException e) {
                  e.printStackTrace();
              }
              return "tohello";
          }
      }
      
    • 页面:注意要给form表单里面添加属性enctype=“multipart/form-data”;

      <%@ page contentType="text/html;charset=UTF-8" language="java" %>
      <html>
      <head>
          <title>Title</title>
      </head>
      <body>
      <%--注意:method一定要为post,否则会报错
      然后要写enctype="multipart/form-data",不然文件传不进去--%>
      <form action="/upload" method="post" enctype="multipart/form-data">
          <input type="file" name="myfile">
          <input type="submit" value="上传">
      </form>
      </body>
      </html>
      
    • 结果:

      我们上传了一个不超过配置中指定大小的文件,最后会在d:/Temp目录下看见,文件上传成功!
      
  • 文件下载:
    • 直接上代码就可以了,写一个文件下载的controller,注意,这里是从本地下载:
       文件下载
        @RequestMapping("download")
        public void downLoad(String filename, HttpServletResponse response){
            BufferedInputStream bis=null;
            BufferedOutputStream bos=null;
            try {
    //            从d:/Temp下下载文件
                bis=new BufferedInputStream(new FileInputStream("d://Temp//"+filename));
    
    //            设置响应头,告诉浏览器是文件下载
                response.setHeader("content-disposition","attachment;filename="+ URLEncoder.encode(filename,"UTF-8"));
    //            读取要下载的文件,保存到文件的输入流
                bos=new BufferedOutputStream(response.getOutputStream());
    //            创建缓冲区
                byte[] bytes=new byte[1024];
                int len=0;
    //            循环将输入流中的内容读取到缓冲区中
                while ((len=bis.read(bytes))!=-1){
    //                输出缓冲区的内容到浏览器,实现文件下载
                    bos.write(bytes,0,len);
                }
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }finally {
    //            关流
                try {
                    bis.close();
                    bos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    
  • 拦截器:
    • 拦截器与过滤器的区别:

      • 拦截器Interceptor,过滤器是Filter
      • 拦截器是springMVC的组件,过滤器是javaEE中的组件
      • 拦截器是配置在Spring的配置文件中的,过滤器是配置在web.xml中的
      • 拦截器是运行在DispatcherServlet之后、Controller之前的,且在Controller执行完后还会调用2个方法,而过滤器是运行在所有的Servlet之前的;
      • 拦截器的配置非常灵活,可以配置多项黑名单,也可以配置多项白名单,过滤器的配置非常单一,只能配置1项过滤路径;
      • 拦截器与过滤器也有很多相似之处,例如:都可拒绝掉某些访问,也可以选择放行;都可以形成链
      • 相比之下,在一个使用SpringMVC框架的项目中,拦截器会比过滤器要好用一些,但是,由于执行时间节点的原因,它并不能完全取代过滤器!
    • 上一张图吧:在这里插入图片描述

    • 配置:拦截器是springMVC的组件,所以配置在子容器中:

      <!--    配置拦截器-->
          <mvc:interceptors>
              <mvc:interceptor>
      <!--            要拦截的路径-->
                  <mvc:mapping path="/**"/>
      <!--            要放过的路径-->
                  <mvc:exclude-mapping path="/css/**"/>
                  <bean class="com.li.springmvc.controller.component.LoginIntercepter"/>
              </mvc:interceptor>
          </mvc:interceptors>
      
    • 写一个拦截器:这里就只展示在Controller处理之前的了,如果有其他需要再使用其他两个方法,主要是需要实现HandlerInterceptor接口

      /**
       * 登录拦截器
       */
      public class LoginIntercepter implements HandlerInterceptor {
      
          /**
           * Controller处理之前
           * @param request
           * @param response
           * @param o
           * @return  false就拦截,true就放过
           * @throws Exception
           */
          @Override
          public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) throws Exception {
      
              Object userName = request.getSession().getAttribute("userName");
      
              if (userName==null){
                  System.out.println("没有登录");
                  request.setAttribute("msg","您尚未登录,请登录");
                  request.getRequestDispatcher("/index.jsp").forward(request,response);
                  return false;
              }
              return true;
          }
      
          /**
           * controller处理之后
           * @param httpServletRequest
           * @param httpServletResponse
           * @param o
           * @param modelAndView
           * @throws Exception
           */
          @Override
          public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
      
          }
      
          /**
           * 在请求处理完成之后,视图渲染结束后
           * @param httpServletRequest
           * @param httpServletResponse
           * @param o
           * @param e
           * @throws Exception
           */
          @Override
          public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
      
          }
      }
      
    • 前端:

      <%@ page contentType="text/html;charset=UTF-8" language="java" %>
      <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
      <html>
      <head>
          <meta charset="UTF-8">
          <meta name="viewport"
                content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
          <meta http-equiv="X-UA-Compatible" content="ie=edge">
          <title>Document</title>
      <%--    --%>
          <link rel="stylesheet" href="/style/login.css">
      </head>
      <body>
      ${msg}
      <form action="/login" method="post">
          <input type="text" placeholder="用户名" name="userName">
          <input type="password" placeholder="密码" name="password">
          <button type="submit"/>登录
      </form>
      </body>
      </html>
      
    • 结果:注意,springMVCInterceptor不会对webapp下面的页面,想要拦截webapp下的页面解决办法有两个,将页面放在WEB-INF里面去,或者使用Filter进行拦截

      当我们在浏览器输入:http://localhost:8080/hello?name=li
      会发现被拦截了,并在登录页面打印出您尚未登录,请登录.
      
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值