Spring MVC 框架基础

Spring MVC 框架基础

Spring MVC 基本应用

MVC体系结构

三层架构

  • 表现层

    web层。负责接收客户端请求,向客户端返回相应结果。

  • 业务层

    service层。负责处理业务逻辑。表现层依赖业务层,但业务层不依赖表现层。

  • 持久层

    dao层。负责数据持久化。和数据库进行交互。

MVC设计模式

​ MVC:Model View Controller。是一种用于设计创建 Web 应用程序的表现层模式。

  • Model:模型。包括业务模型数据模型。业务模型用于封装业务数据,数据模型用于处理业务数据。

  • View:视图。用于展示数据。一般依赖于模型数据创建。

  • Controller:控制器。用于处理程序逻辑。

    每一层都编写自己的东西,不编写任何其他代码。分层是为了解耦,解耦是为了维护方便和分工协作。

Spring MVC 是什么

​ Spring MVC:Spring Web MVC。是基于 Java 实现 MVC 设计模式的轻量级 web 框架。是 Spring FrameWork 的后续产品。本质可以认为是对 Servlet 的封装,简化了 Servlet 的开发。

spring MVC 的工作流程

请求处理流程

  1. 用户发送请求到前端控制器 DispatcherServlet
  2. DispatchServlet 收到请求后调用 HandlerMapping(处理器映射器)。
  3. HandlerMapping(处理器映射器)根据请求的 URL 找到具体的 Handler(后端控制器),返回处理器执行链(处理器拦截器 + 处理器对象)给 DispatcherServlet
  4. DispatcherServlet 调用 HandlerAdapter(处理器适配器)去调用 Handler(后端控制器)。
  5. HandlerAdapter(处理器适配器)执行 Handler(后端控制器)。
  6. Handler(后端控制器)执行完成后,返回 **ModelAndView ** 对象给 HandlerAdapter(处理器适配器)。
  7. HandlerAdapter(处理器适配器)将 **ModelAndView ** 对象返回给 DispatcherServlet
  8. DispatcherServlet 请求 ViewResolver(视图解析器),根据逻辑视图名来解析真正的视图。
  9. ViewResolver(视图解析器)返回 View 对象给 DispatcherServlet
  10. DispatcherServlet 进行视图渲染,将 ModelAndView 中的数据模型填充到 request 域中。
  11. DispatcherServlet 向用户响应结果。

九大组件

组件名 描述
HandlerMapping 处理器映射器 在请求到达后,查找请求相对应的 Handler(处理器)+ Interceptor(拦截器)。
标注了 @RequestMapping 的每个⽅法都可以看成是⼀个 Handler。
HandlerAdapter 处理器适配器 让固定的 Servlet 方法调用 Handler 来进行处理。
Servlet 的方法结构都是 doService(HttpServletRequest req,HttpServletResponse resp) 形式的。
Spirng MVC 的 Handler 可以是任意形式的,只要能处理请求即可。
HandlerExceptionResolver 处理程序异常解析器 ⽤于处理 Handler 产⽣的异常情况。
根据异常设置 ModelAndView,之后交给渲染方法进行渲染。
ViewResolver 视图解析器 Controller 层返回的 String 类型视图名 viewName 会被解析成 View 对象。找到渲染所用的模板 和 所用的技术 并填入参数。spring MVC 会默认配置一个 InternalResourceViewResolver 用于解析 JSP 视图。
RequestToViewNameTranslator 请求视图名称转换器 由于有的 Handler 没有设置 ViewName,ViewResolver 根据 ViewName 查询不到 View 。从请求中获取 viewName。
LocalResolver 区域设置解析器
ThemeResolver 主题解析器
MultipartResolver 将普通请求包装成 MultipartHttpServletRequest 。使其拥有上传文件的功能。
FlashMapManager 用于重定向时的参数传递。

请求参数绑定

http 协议:超文本传输协议。

原生 Servlet 接受一个整型的参数:

String ageStr = request.getParameter("age");

Integer age = Integer.parseInt(ageStr);

Spring MVC 对 Servlet 进行了封装,简化了 Servlet 的许多操作。

Spring MVC 环境搭建

pom.xml

<dependency>
    <groupId>org.springframeworkgroupId>
    <artifactId>spring-webmvcartifactId>
    <version>5.1.12.RELEASEversion>
dependency>

<dependency>
    <groupId>javax.servletgroupId>
    <artifactId>javax.servlet-apiartifactId>
    <version>3.1.0version>
    <scope>providedscope>
dependency>

web.xml

<servlet>
    <servlet-name>springMvcservlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
    <init-param>
        <param-name>contextConfigLocationparam-name>
        <param-value>classpath:applicationContext.xmlparam-value>
    init-param>
servlet>
<servlet-mapping>
    <servlet-name>springMvcservlet-name>
    <url-pattern>/url-pattern>
servlet-mapping>

applicationContext.xml


<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        https://www.springframework.org/schema/mvc/spring-mvc.xsd
">
    <context:component-scan base-package="com.demo"/>
    
    <mvc:annotation-driven/>
    
    
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        
        <property name="prefix" value="/WEB-INF/jsp/"/>
        
        <property name="suffix" value=".jsp"/>
    bean>
    
beans>
  • 返回值 ModelAndView
@RequestMapping("/handle01")
// 返回 ModelAndView;Model 里面数数据模型;View 是需要跳转的页面地址
public ModelAndView handle01() {
    ModelAndView modelAndView = new ModelAndView();
    modelAndView.addObject("date", new Date());
    modelAndView.setViewName("success");
    return modelAndView;
}
// ModelMap;Model;Map;最终都会转换为==>BindingAwareModelMap

/**
* 继承体系如下:
* Model ——> ExtendedModelMap ——> BindingAwareModelMap
* Map ——> ModelMap ——> ExtendedModelMap ——> BindingAwareModelMap
*/
@RequestMapping("/handle11")
public String handle11(ModelMap modelMap) {
    modelMap.addAttribute("date", new Date());
    return "success";
}

@RequestMapping("/handle12")
public String handle12(Model model) {
    model.addAttribute("date", new Date());
    return "success";
}

@RequestMapping("/handle13")
public String handle13(Map<String, Object> map) {
    map.put("date", new Date());
    return "success";
}

@RequestMapping("/handle14")
public String handle14(ExtendedModelMap extendedModelMap) {
    extendedModelMap.addAttribute("date", new Date());
    return "success";
}

@RequestMapping("/handle15")
public String handle15(BindingAwareModelMap bindingAwareModelMap) {
    bindingAwareModelMap.addAttribute("date", new Date());
    return "success";
}
  • 绑定简单数据类型
// 对八种基本数据类型的支持
// 除了 boolean 型的推荐用包装类 可以为 null
// 对于 boolean,Boolean 0 —— false;1 —— true;
// 对于 String 直接 传就行了
// 127.0.0.1:8080/demo/handle03?hello=127&s=60&id=1&uid=2&money=3&age=4&letter=0&flag=1
@RequestMapping("/handle03")
public String handle03(@RequestParam(value = "hello", required = false) Byte b, Short s, Integer id, Long uid, Double money, Float age, Character letter, Boolean flag) {
    System.out.println(b);
    System.out.println(s);
    System.out.println(id);
    System.out.println(uid);
    System.out.println(money);
    System.out.println(age);
    System.out.println(letter);
    System.out.println(flag);
    return "success";
}
  • 对原生 Servlet 参数的支持
@RequestMapping("/handle02")
// 对原生 servlet 的支持
public String handle02(HttpServletRequest request, HttpServletResponse response, HttpSession session) {
    request.setAttribute("date", new Date());
    return "success";
}
  • 绑定 POJO 类型参数
// 对 pojo 类型的支持
// 如果传两个对象 属性名相同的会同时赋值 属性名不同的会单独赋值
// 127.0.0.1:8080/demo/handle04?user.id=1&username=myName&otherAttribute=1233
@RequestMapping("/handle04")
public String handle04(User user, Substance substance) {
    System.out.println(user.getId());
    System.out.println(user.getUsername());

    System.out.println(substance.getId());
    System.out.println(substance.getUsername());
    System.out.println(substance.getOtherAttribute());
    return "success";
}
  • 绑定 POJO 包装对象参数
// 对 pojo 里面包含 pojo 的支持
// 127.0.0.1:8080/demo/handle05?id=1&username=myName&otherAttribute=1233&user.id=2&user.username=username&user.subUser.age=1001
@RequestMapping("/handle05")
public String handle05(Substance substance) {
    System.out.println(substance);
    return "success";
}
  • 绑定日期类型参数
<mvc:annotation-driven conversion-service="conversionServiceFactoryBean"/>


<bean id="conversionServiceFactoryBean" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
    <property name="converters">
        <set>
            <bean class="com.demo.converter.DateConverter"/>
        set>
    property>
bean>
public class DateConverter implements Converter<String, Date> {
    @Override
    public Date convert(String source) {
        // 完成字符串向日期的转换
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        try {
            Date parse = simpleDateFormat.parse(source);
            return parse;
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return null;
    }
}
@RequestMapping("/handle06")
public String handle06(Date birthday) {
    System.out.println(birthday);
    return "success";
}

Restful 风格请求支持

// 对 rest 风格接口的支持
@RequestMapping(value = "/handle/{id}", method = RequestMethod.GET)
public String handleGet(@PathVariable Integer id) {
    System.out.println("GET:" + id);
    return "success";
}

@RequestMapping(value = "/handle/{id}", method = RequestMethod.POST)
public String handlePost(@PathVariable Integer id) {
    System.out.println("POST:" + id);
    return "success";
}

@RequestMapping(value = "/handle/{id}", method = RequestMethod.DELETE)
public String handleDelete(@PathVariable Integer id) {
    System.out.println("DELETE:" + id);
    return "success";
}

@RequestMapping(value = "/handle/{id}", method = RequestMethod.PUT)
public String handlePut(@PathVariable String id) {
    System.out.println("PUT:" + id);
    return "success";
}

JSON 交互

pom.xml

<dependency>
    <groupId>com.fasterxml.jackson.coregroupId>
    <artifactId>jackson-coreartifactId>
    <version>2.9.0version>
dependency>
<dependency>
    <groupId>com.fasterxml.jackson.coregroupId>
    <artifactId>jackson-databindartifactId>
    <version>2.9.0version>
dependency>
<dependency>
    <groupId>com.fasterxml.jackson.coregroupId>
    <artifactId>jackson-annotationsartifactId>
    <version>2.9.0version>
dependency>
// 对 JSON 数据的支持
// 需要导入 jackson 的 pom
@RequestMapping("/handle07")
public @ResponseBody User handle07(@RequestBody User user) {
    user.setUsername("张三丰");
    return user;
}

Spring MVC 高级应用

拦截器的使用

监听器、过滤器、拦截器

  • Servlet:处理 request 请求 和 response 响应。
  • Filter:过滤器。对 request 请求起到过滤作用,作用在 Servlet 之前。配置为 /* 可以对所有的资源(servlet、js/css静态资源等)访问进行过滤。
  • Listener:监听器。实现了javax.servlet.ServletContextListener 接口的服务器端组件,它随Web应用的启动而启动,只初始化⼀次,然后会⼀直运行监视,随Web应用的停止而销毁。
  • Interceptor:拦截器。是 Spring MVC 等表现层自己的,不会拦截 JSP/HTML/CSS/IMAGE 等的访问。只会拦截访问的控制方法(Handler)。

从配置的角度来看,Servlet、Filter、Listener 是配置在 web.xml 中的。而 Interceptor 是配置在表现层自己的配置文件中。

拦截器的执行流程

  1. 在 handler 方法业务逻辑执行之前执行
  2. 在 handler 业务逻辑执行之后 尚未跳转页面时执行
  3. 页面已经跳转渲染完毕之后执行
public class MyInterceptor01 implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 在 handler 方法业务逻辑执行之前执行
        // 返回值 返回值 boolean 代表是否放行,true 代表放行,false 代表中止
        System.out.println("MyInterceptor01 preHandle......");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        // 在 handler 业务逻辑执行之后 尚未跳转页面时执行
        System.out.println("MyInterceptor01 postHandle......");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        // 页面已经跳转渲染完毕之后执行
        System.out.println("MyInterceptor01 afterCompletion......");
    }
}
<mvc:interceptors>
    
    
    
    
    <bean class="com.demo.interceptor.MyInterceptor01"/>
    
mvc:interceptors>

多个拦截器的执行顺序

MyInterceptor01 preHandle......
MyInterceptor02 preHandle......
POST:1
MyInterceptor02 postHandle......
MyInterceptor01 postHandle......
MyInterceptor02 afterCompletion......
MyInterceptor01 afterCompletion......

处理 multipart 形式的数据

pom.xml

<dependency>
    <groupId>commons-fileuploadgroupId>
    <artifactId>commons-fileuploadartifactId>
    <version>1.3.1version>
dependency>

applicationContext.xml


<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">bean>
@RequestMapping("/upload")
public String upload(MultipartFile uploadFile) {
    String originalFilename = uploadFile.getOriginalFilename();
    try {
        uploadFile.transferTo(new File("D:\\learning\\spring-mvc", originalFilename));
    } catch (IOException e) {
        e.printStackTrace();
    }
    return "success";
}

在控制器中处理异常

@ControllerAdvice
public class GlobalExceptionResolver {

    @ExceptionHandler(ArithmeticException.class)
    public ModelAndView handleException(ArithmeticException exception) {
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("msg", exception.getMessage());
        modelAndView.setViewName("error");
        return modelAndView;
    }
}

基于 Flash 的跨重定向数据传递

转发:url 不会变,参数也不会丢,一个请求。

重定向:url 会变,参数会丢失,需要重新携带参数。两个请求。

@RequestMapping("/handle99")
public String handle99(String name) {
    // 利用 flash 的时候此处拿不到name;因为在页面跳转后值已经销毁了
    System.out.println(name);
    return "success";
}

@RequestMapping("/handleRedirect")
public String handleRedirect(String name, RedirectAttributes redirectAttributes) {
    // return "redirect:handle99?name=" + name + "";
    redirectAttributes.addFlashAttribute("name", name);
    return "redirect:handle99";
}

利用 RedirectAttributes 设置一个 flash 类型的属性,该属性会暂时被存在 session 中,在页面跳转后进行销毁。

参考git:https://gitee.com/zhangyizhou/learning-spring-mvc-demo.git

1.自定义 mvc 项目:custom-mvc

1.spring mvc demo 项目:spring-mvc-demo

你可能感兴趣的:(mvc,spring,java)