Spring MVC 框架 (持续完善中)







Spring MVC框架

Spring Framework 包含了 IOC, AOP, DT, Spring Test, Spring MVC ··· ···

  • M ------ 指的是 Model, 模型,数据的处理层,包含 Entity/pojo/bean, dao, servcie, …
  • V ------ 指的是 View,视图, 做数据呈现,包含:HTML/CSS/JS, AJAX, JSP, Freemaker, Velocity, Thymeleaf, …
  • C ------ 指的是 Controller, 控制器,做请求和响应的处理,包含: Servlet, Controller, Action, …


Spring MVC框架的功能

  1. 基于Spring Framework 的生态,完美与Spring IOC, AOP , DT ,Spring-test 集成
  2. 提供了非侵入式的控制器,它通过配置来完成
  3. 提供了视图解析器,使得视图技术与解析技术分离,这样,Spring MVC可以支持多种不同的视图技术,如:JSP, FreeMarker, Velocity, thymeleaf ··· ···
  4. 提供了ModelAndView, 可以把视图与模型整合在一起,交给核心控制器进行解析。
  5. 提供了异常处理机制。
  6. 提供了表单数据的后端验证
  7. 支持国际化[i18n]


Spring MVC项目的开发步骤

  1. 准备maven项目

  1. 在pom.xml 中导入依赖 spring-webmvc
 <!-- springmvc 依赖-->
         <dependency>
             <groupId>org.springframework</groupId>
             <artifactId>spring-webmvc</artifactId>
             <version>${spring.version}</version>
         </dependency>

  1. 在web.xml中要配置Spring mvc框架的入口【要启动Spring mvc】,也就是配置前置控制器 DispatherServlet
<!-- spring mvc 主控制器 -->
    <servlet>
        <servlet-name>DispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

        <init-param>
            <param-name>contextClass</param-name>
            <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
        </init-param>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>com.hc.config.WebMvcConfig</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>

  1. 编写基于注解的Web配置类,名叫:WebMvcConfig.java , 在此类中 启动MVC模式,扫描 controller 包等
package com.hc.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/*************
 * 代表的 ACWAC,也就是WEB应用相关的上下文
 */
@Configuration   //表示这是一个Spring的注解配置类
@ComponentScan({"com.hc.controller"})  //扫描控制层的包
@EnableWebMvc   //启动 WebMvc
//@Import(value = {AppConfig.class})  //加载IOC容器应用上下文
//这就表明,此应用将有两个ApplicationContext
public class WebMvcConfig implements WebMvcConfigurer {

    //nothing!
}


  1. 开发你自己的控制器【想象成 Servlet 】
  1. 做测试



Spring MVC 工作流程示意图:

故屿




Spring MVC框架下控制器的开发

框架本身提供了一个前置控制器,也就是 DispatcherServlet, 我们也把它叫 大C, 而我们自己开发的控制器叫 小C, 大C 接收客户端的请求后,负责把这个请求分发给目标 小C, 它是如何确定分发给哪一个 小C 呢?



“小C” 的开发

在Spring MVC框架下,我们开发小C,不需要实现任何的接口或继承任何抽象类,就是一个普通的JavaBean,所以说,Spring MVC框架是非侵入式的。

package com.hc.controller;

import com.hc.entity.Student;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Controller
@RequestMapping("/stu")
public class StudnetController {

    @RequestMapping("/toadd")
    public String toAddStudent(HttpServletRequest request, HttpServletResponse response) {

        return "addStudent";
    }


    @RequestMapping(value = "/add",produces = "application/json")
    @ResponseBody
    public Student addStudent(Student student) {
        System.out.println(student);
        student.setId(100);
        return student;
    }
}

请求参数的处理

处理响应

跳转的处理







Spring MVC的核心功能

Spring MVC的操作

  1. 搭建项目环境
  1. 开发小C
  • ①方法的返回值
    * String —> 表示逻辑视图名【注: 如果不想经过视图解析器话,在String前面加 forward:或redirect: 前缀。
    * JAVA数据类型 + @ResponseBody --> 表示直接以返回值做为响应的内容,建议内容是 application/json 格式 【用在异步请求中】
    * void -> 不建议使用
    * ModelAndView -> 把模型和视图封装在一起返回给大C, 由大C进一步解析。【这种做法是老版本的做法】
  • ②方法的参数
    * Spring MVC内置的参数
    * request, response, httpSession, … 【在Servlet时代中存在的】
    * Model, MultipartFile, …
    * 由客户端传递给服务端的请求参数
    * 可以是非表单中的请求参数【以queryString的方法传递过来】
    * 可以是表单中的请求参数【一般以实体类做为参数,可以有多个】
  • ③有关 @RequestMapping 注解
    * value 属性,指定此方法接收的请求URI【Uniform Resource Identifier, 统一资源定位符】,
    * produces 属性,会配合@ResponseBody使用, 指定此方法的返回内容以什么格式写到客户端,大多数时候,是写 application/json
    * method属性,指定此方法只能接收哪种类型【GET, POST, DELETE, PUT, PATCH】的请求。
  1. 单元测试


Spring MVC中常用的功能

一、文件上传和下载

  1. WebMvcConfig.java中进行配置对 Multipart 文件的配置, 如下:
@Bean
    public CommonsMultipartResolver multipartResolver() {
        CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
        //设置 一些属性
        multipartResolver.setDefaultEncoding("UTF-8");
        multipartResolver.setMaxUploadSizePerFile(2*1024*1024); //每个文件大小
        multipartResolver.setMaxUploadSize(20*1024*1024); //总大小
        //
        return multipartResolver;
    }

表明你的大C有能力处理 MultipartFile 类型的数据,有能力处理文件上传

  1. 在前端页面中的表单中,要指定两个东西:
  • method=“post”
  • enctype=“multipart/form-data”
<form name="xxxForm" action="/upload" method="POST" enctype="multipart/form-data">
    ....
    <input type="file" name="uploadFile">
</form>
  1. 在小C中,就可以注入 MultipartFile 对象,这个对象就封装了文件上传的所有操作, 如下:
public class FileController {

    @RequestMapping(value = "/upload")
    public String upload(MultipartFile uploadFile) {
        //....
        return "";
    }
}
  • 下载时的注意事项:
  1. 要告诉浏览器,响应客户内容的类型,也就是要调用:
response.setContentType("application/x-download;charset=utf-8")
  1. 其次,在响应头中,告诉浏览器该如何处理这个文件? 是直接打开还是弹出对话框让用户保存或打开。
response.setHeader("Content-Disposition", "attachment;filename=xxxx.png")


二、Bean Validation

由于前端的验证很容易就被“跳过”, 也就是说数据传递到后端还存在很大的不安全性,为了保证这些数据在传递到数据库之前是合法的,我们在后端需要对前端传递过来的数据做进一步的验证。这个就是后端的Bean Validation, 也叫 JSR303 规范



后端的数据验证方式
  1. 手动编写一个工具类,利用正则表达式就可以来验证各种数据,比如:Email, phone, birth, age, …
  2. 通过配置方式来完成验证,无需程序员编写验证逻辑代码【我们要这种】
  • 注:
    Spring MVC框架完全支持JSR303规范,所以,我们在使用spring mvc框架就可以使用 bean validation 技术。
  • 注:
    JSR303规范,有一个很好的实现者就是 Hibernate-validator, 【不是Hibernate框架】,而Spring MVC本着不重复发明车轮的理念,并没有重新去实现 JSR303规范,而是直接使用 hibernate-validator 框架。


如何使用 Bean Validation?
  1. 在 pom.xml中,导入 hibernate-validator 的组件
 <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-validator</artifactId>
            <version>5.4.1.Final</version>
        </dependency>


  1. 配置 Spring MVC中内置的 BeanValidator 【验证器】,在 WebMvcConfig.java中, 重写 getValidator 方法。
/****************************
     * 重写父类的方法,不再返回NULL,而是要返回一个有效的BeanValidator
     * @return
     */
    @Override
    public Validator getValidator() {
        LocalValidatorFactoryBean validatorFactoryBean = new LocalValidatorFactoryBean();
        //设置属性 【可选】
        validatorFactoryBean.setValidationMessageSource(messageSource());
        //
        return validatorFactoryBean;
    }

    /***
     * 利用消息文件,可以做国际化[i18n]  [可选]
     * @return
     */
    private MessageSource messageSource() {
        ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
        //设置属性
        messageSource.setDefaultEncoding("UTF-8");
        messageSource.setBasename("validateMessage");
        //
        return messageSource;
    }


  1. 在你的需要被验证的实体类中,使用JSR303的注解来修饰,如下:
import javax.validation.constraints.*;

public class Student {

    private Integer id;

    @NotNull(message = "{error.name.notnull}")
    @Size(min = 6, max=12, message = "{error.name.size}")
    private String name;

    @NotNull(message = "{error.stuno.notnull}")
    @Pattern(regexp = "[A-Z][0-9]+",message = "{error.stuno.pattern}")
    private String stuNo;

    private Gender gender;

    @DateTimeFormat(pattern = "yyyy-MM-dd")
    @Past(message = "{error.birth.future}")
    private Date birth;
    
    //....
}


  1. 在小C的方法中,利用 @Valid 注解和 BindingResult 类来处理验证失败后的提示信息
    @RequestMapping(value = "/add",method = RequestMethod.POST)
    public String addStudent(@Valid Student student, BindingResult result, Model model) {
        //要验证
        if(result.hasErrors()) {
            // 说明验证失败, 取出每一个属性对应的message
            List<FieldError> fieldErrors = result.getFieldErrors();
            /*for(FieldError fe : fieldErrors) {
                System.out.printf("%s -> %s\n", fe.getField(), fe.getDefaultMessage());
                //绑定到model中去,以便在目标资源可以显示出来
                //model.addAttribute(fe.getField(), fe.getDefaultMessage());
            }*/
            //转换成map,绑定到前台
            model.addAttribute("ERROR_MAP",convertMap(fieldErrors));
            return "student/add";
        } else {
            //调用业方法
            studentService.add(student);
            model.addAttribute("STUDENT_KEY",student);
            //跳转|重定向 到 /list 请求中
            return "redirect:/stu/list"; //如果带上 forward或redirect 关键后,就表示不再经过视图解析器
    }
}
    //把List转换成map
    private Object convertMap(List<FieldError> fieldErrors) {
        Map<String, String> map = new HashMap<>();
        for(FieldError fieldError : fieldErrors) {
            map.put(fieldError.getField(), fieldError.getDefaultMessage());
        }
        return map;
    }

注:
如果要做国际化的话,则应该把错误提示信息,单独存储在一个 xxxx.properties 文件中,这个文件可以做成国际化文件,后面带语言代号和国家代码,如: xxx_zh_cn.properties

如:
error.name.notnull=学号不能为
error.name.size=学员名长度要在6至12个字符之间
error.stuno.notnull=学号不能为
error.stuno.pattern=学号必需以大写字母开头
error.birth.future=生日时间不能大于当前时

  • 注:上面的中文一定要转成 unicode 码,网上有很多的在线工具可以使用帮助。


三、拦截器[Interceptor]

在 Servlet学习中,我们已经了解过一个请求拦截的组件叫 Filter, 它是在 WEB Container层面进行请求的拦截,面我们在这里学习的 Interceptor ,是针对 控制器请求的拦截。

开发拦截器
  1. 写一个类实现 HandlerInterceptor 接口, 并重写你感兴趣的方法,一般是 preHandle方法
  1. 配置这个拦截器, 支持两种配置方式
    1. 通过xml配置
   <mvc:interceptors>
       <mvc:interceptor>
           <mvc:mapping path="/stu/*"/>
           <bean class="com.hc.interceptors.HelloInterceptor"/>
       </mvc:interceptor>
   </mvc:interceptors>
   ```



然后,要把这个xml让我们的注解配置去读取,如下 :

   @Configuration   //表示这是一个Spring的注解配置类
   @ComponentScan({"com.hc.controller"})  //扫描控制层的包
   @EnableWebMvc   //启动 WebMvc
   @Import(value = {AppConfig.class})  //加载IOC容器应用上下文
   //读到xml文档
   @ImportResource(value = "classpath:applicationContext.xml")   //读取
   public class WebMvcConfig implements WebMvcConfigurer {
       //....
   }
   
    1. 通过代码配置,直接在WebMvcConfig.java类中,重写父类的 addInterceptors方法即可,如下:
  /**
   * 通过代码的方式来添加拦截器
   * @param registry
   */
  @Override
  public void addInterceptors(InterceptorRegistry registry) {
      System.out.println("--- 代码注册拦截器....");
      HandlerInterceptor helloInterceptor = new HelloInterceptor();
      //
      registry.addInterceptor(helloInterceptor).addPathPatterns("/stu/*");
  }


四、控制器的测试





Spring MVC中的异常处理

支持两种,一种是局部针对某一个控制器中的异常处理,另一种是全局的异常处理



单个控制器的异常处理【基于注解】

  • @ExceptionHandler 注解

在需要做异常处理的控制器中,写一个异常处理方法,在此方法上面打上 @ExceptionHandler注解,如下 :

@ExceptionHandler(value = {RuntimeException.class,SQLException.class})
    public String handlerException(Throwable t, Model model) {
        //
        System.out.println("出现异常了:");
        //日志记录:
        log.debug(t);
        if(t instanceof RuntimeException) {
            System.out.println("运行时异常");
            //跳到某个页面

        } else if(t instanceof SQLException) {
            System.out.println("SQL异常");
            //跳到某个页面
        }
        //
        model.addAttribute("EXCEPTION",t);
        //
        return "500";
    }

注:这种方式只能处理当前的控制器出现的异常。



全局异常处理器【基于注解】

    1. 写一个类实现 HandlerExceptionResolver 接口,并重写它的方法
    1. 配置这个全局异常处理类型,有两种方式
  • 基于注解的配置

    1. 在此类上面使用 @Component 注解

    2. 在 WebMvcConfig.java中,在@ComponentScan注解中,把此异常处理类所在的包添加进去,如下 :

      
      @Configuration   //表示这是一个Spring的注解配置类
      @ComponentScan({"com.hc.controller","com.hc.exhandler"})  //扫描控制层及异常处理层的包
      @EnableWebMvc   //启动 WebMvc
      @Import(value = {AppConfig.class})  //加载IOC容器应用上下文
      //读到xml文档
      //@ImportResource(value = "classpath:applicationContext.xml")
      //这就表明,此应用将有两个ApplicationContext
      public class WebMvcConfig implements WebMvcConfigurer {
          //...
      }
      
      
  • 基于代码的配置

    • 直接在 WebMvcConfig中,重写父类的一个方法: configureHandlerExceptionResolvers ,如下:

      
      @Override
          public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
              System.out.println("-- 代码注册全局异常处理器...");
              System.out.printf("-- 已经有的异常处理器个数:%d\n",resolvers.size());
              HandlerExceptionResolver her = new GlobalExceptionHandler();
              resolvers.add(her);
          }
          
      








Note:
欢迎点赞,留言,转载请在文章页面明显位置给出原文链接
知者,感谢您在茫茫人海中阅读了我的文章
没有个性 哪来的签名!
详情请关注点我
持续更新中

© 2020 09 - Guyu.com | 【版权所有 侵权必究】
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值