SpringMVC

SpringMVC

1 Overview

学习方法 课程说明 秦老师课前说的:

SE 入门, 能看懂代码

Web 框架在精简它

框架 按正常, 这里是要从官网文档自学的, 不能再被老师带. 锻炼自学能力, 锻炼笔记能力, 锻炼项目能力

SSM整合, 相当于在Web阶段的项目

Spring IoC和AOP特别重要, 要再复习

SpringMVC中也有个很重要的东西, 执行流程

博客很重要, 工作时如果没有对应文档, 可能会崩溃

版本: 老师用5.1.9, 我自己选用了5.2.0

1.1 MVC

模型(DAO service) | 视图(jsp) | 控制器(Servlet)

后端很多特别牛批的控制器, 很多底层也都是Servlet

职责

Controller 1. 取得表单数据 2. 调用业务逻辑 3. 转向指定页面

Model 1. 业务逻辑 2. 保存数据状态

View 显示页面

pojo:

  • vo: 视图层的实体类对象, 更灵活
  • dto: 数据传输时的对象
  • ...

面试: 你的项目的架构是设计好的还是演进的??

坑 一般都是演进的. 才合理. 阿里巴巴, MySQL --> AliSQL --> AliRedis

所有项目一般都是All in one --> 微服务

1.2 SpringMVC

为什么要用它呢?

  • 轻量, 易学
  • 高效地, 基于请求响应的框架. (不是以事件为驱动)
  • 可与SpringFramework无缝集成
  • 约定优于配置
  • 功能强大, 支持: RESTful | 数据合法验证 | 异常处理 | 拦截器 ...
  • 市占率高

RESTful, 不需要问号拼接参数

1.3 基本原理 执行流程

A - DispatcherServlet

Spring的web框架围绕 DispatcherServlet 设计

作用: 转发调度 ,将请求分发到不同"处理器"

本质: 还是Servlet, 如下图

![image-20200422105032987](D:微云同步助手364291826同步文件夹知识库10 - md文库 秦疆老师框架SpringMVC.assetsimage-20200422105032987.png)

![image-20200421214015127](D:微云同步助手364291826同步文件夹知识库10 - md文库 秦疆老师框架SpringMVC.assetsimage-20200421214015127.png)

中文版

image-20200422111900346

秦老师的图

image-20200422114808087

基本原理 - 简述: (based on 老师公众号)

  1. DispatcherServlet, 代表FrontController, 是整个SpirngMVC的"控制中心". 用户发出request, DS接收请求. 解析URL:
    • 例如: http://localhost:8080/SpringMVC/hello
    • http://localhost:8080 - 服务器域名端口
    • /SpringMVC - 部署在服务器上的具体web站点
    • /hello - 控制器
    • 综述: 请求位于服务器上"SpringMVC站点"的"hello"控制器
  2. HandlerMapping根据url中信息, 中众多注册过的Handler们中找具体的
  3. HandlerExecution (理解不太深刻), 主要作用是根据url查找控制器hello
  4. HandlerAdapter按特定规则执行Handler, Handler再让具体的Controller执行
  5. Controller调用执行我们写的业务代码, 得到数据, 存入ModelAndView对象, 并把该对象返回给Adapter, 再给DS
  6. DS调用ViewResolver解析mv对象中的"逻辑视图名", 同时将数据渲染给这个具体的View
  7. DS通过逻辑视图名, 找到渲染好的View, 呈现

摘自老师: 没有笨人只有懒人

基本原理 - 个人理解:

HandlerMapping 是众多Handler们的映射器 注册中心, 通过它找到具体的Handler的信息, 把信息告诉给DS, DS再通过Adapter调用具体的handler, 也就是我们写的Controller

ModelAndView 是Controller方法的返回值, 理解可以认为"给前端带个需要的值, 并且指定哪个页面文件"

ViewResolver 作用

  1. 提取mv中model中的数据
  2. 解析mv中指定的view名字信息
  3. 拼接前缀路径+视图名字+后缀, 找到对应view
  4. 将数据渲染到view中
  5. 个人理解: VR是从MandV中提取出数据和指定view信息, 再把两者结合(渲染), 丢给DS, DS展现..ok

B - 补充: Maven资源过滤问题

Maven存在资源过滤问题, 经常出现xml文件编辑后不能正常out到target中, 可以在parent module的pom.xml中添加以下代码来解决:

<build>
   <resources>
       <resource>
           <directory>src/main/java</directory>
           <includes>
               <include>**/*.properties</include>
               <include>**/*.xml</include>
           </includes>
           <filtering>false</filtering>
       </resource>
       <resource>
           <directory>src/main/resources</directory>
           <includes>
               <include>**/*.properties</include>
               <include>**/*.xml</include>
           </includes>
           <filtering>false</filtering>
       </resource>
   </resources>
</build>

1.4 RESTful

(已经比较熟悉了, 这里只补充一些)

作用:

  • 简洁, 显而易见
  • 高效 (支持缓存)
  • 可能有一定安全性, 因为url上可以避免暴露太多后台代码信息 (秦老师看法)

注意点:

  • requestMapping有相同url, 但接收请求的method不同的 (一个get 一个post), 则会根据实际请求方式不同自动匹配
  • 别忘了@PathVariable

1.5 小黄鸭调试法

向自己, 如同向他人一样, 解释一下代码的作用 流程. 说到一半可能就明白了

2 Hello SpringMVC

2.1 通过代码理解: XML

手动配置各个组件, 用Spring + SpringMVC实现一次HelloWorld!

  1. 确保Maven中webmvc的依赖正确导入, 确保projectStructure > artifact > 中依赖都正确

  2. 准备好一个SpringFramework的核心配置文件: springmvc-servlet.xml, in resources目录(classpath:)

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd">
    
    </beans>
    
  3. 配置DispatcherServlet: in web.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
             version="4.0">
    
        <!--配置DS-->
        <servlet>
            <servlet-name>springmvc</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <!--DS需要绑定Spring的核心配置文件-->
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>classpath:springmvc-servlet.xml</param-value>
            </init-param>
            <!--启动级别: 1-->
            <load-on-startup>1</load-on-startup>
        </servlet>
        <!--斜杠和斜杠* 有区别: 斜杠只匹配请求,斜杠*匹配请求+也包括jsp文件-->
        <servlet-mapping>
            <servlet-name>springmvc</servlet-name>
            <url-pattern>/</url-pattern>
        </servlet-mapping>
    </web-app>
    

    注意:

    <url-pattern>

    • / - 只匹配请求, 不匹配.jsp文件
    • /* - 不光匹配请求, 也匹配文件
  4. 配置HandlerMapping, HandlerAdapter and ViewResolver, 交给Spring管理, in springmvc-servlet.xml

     <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <!--HandlerMapping-->
        <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
    
        </bean>
    
        <!--HandlerAdapter-->
        <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter">
    
        </bean>
        <!--ViewResolver -->
        <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
              id="internalResourceViewResolver">
            <!--前缀-->
            <property name="prefix" value="/WEB-INF/jsp"></property>
            <!--后缀-->
            <property name="suffix" value=".jsp"></property>
        </bean>
    
    </beans>
    
  5. Controller: HelloController.java

    package com.kuang.controller;
    
    import org.springframework.web.servlet.ModelAndView;
    import org.springframework.web.servlet.mvc.Controller;
    
    public class HelloController implements Controller {
        public ModelAndView handleRequest(javax.servlet.http.HttpServletRequest httpServletRequest, javax.servlet.http.HttpServletResponse httpServletResponse) throws Exception {
            ModelAndView mv = new ModelAndView();
    
            /*这里写业务代码*/
            String result = "HelloSpringMVC";
            mv.addObject("msg",result);
    
            /*视图跳转*/
            mv.setViewName("test"); // 在视图解析器中拼接
    
            return mv;
        }
    }
    
    
  6. 配置Tomcat部署/s1并启动测试http://localhost:8080/s1/hello, 通过.

2.2 通过代码理解: 注解

  1. 确保依赖都正常, 必须手动在projectStructure>artifact中添加lib目录, 并且把依赖也能输出进去!否则会404!

    image-20200422160930128

  2. 准备Spring的核心配置文件springmvc-servlet.xml. 这里和XML方式稍微有区别

    别忘了准备页面文件 web > jsp > hello.jsp

    <?xml version="1.0" encoding="UTF-8"?>
    <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
           http://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.kuang.controller"></context:component-scan>
    
        <!--SpringMVC固定要写的 #######################开始-->
    
        <!--过滤静态资源的处理, 使S MVC不处理静态资源-->
        <mvc:default-servlet-handler></mvc:default-servlet-handler>
        <!--注解引擎-->
        <mvc:annotation-driven></mvc:annotation-driven>
        <!--ViewResolver-->
        <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
              id="internalResourceViewResolver">
            <property name="prefix" value="/WEB-INF/jsp/"></property>
            <property name="suffix" value=".jsp"></property>
        </bean>
    
        <!--SpringMVC固定要写的 #######################结束-->
    
    
    </beans>
    
  3. web.xml中配置DServlet

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
             version="4.0">
    
        <!--配置DServlet-->
        <servlet>
            <servlet-name>springmvc</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <!--DS需要绑定Spring的核心配置文件-->
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>classpath:springmvc-servlet.xml</param-value>
            </init-param>
            <!--启动级别: 1-->
            <load-on-startup>1</load-on-startup>
        </servlet>
        <!--斜杠和斜杠* 有区别: 斜杠只匹配请求,斜杠*匹配请求+也包括jsp文件-->
        <servlet-mapping>
            <servlet-name>springmvc</servlet-name>
            <url-pattern>/</url-pattern>
        </servlet-mapping>
    </web-app>
    
  4. Controller.java

    package com.kuang.controller;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    @Controller // 被包扫描配置扫描到, 被Spring接管
    public class HelloSpring {
    
        @RequestMapping("/hello")
        public String hello(Model model){
            // 处理业务, 封装数据
            model.addAttribute("msg","Hello, SrpingMVC-annotaion!");
            return "hello"; // 会自动被视图解析器解析
        }
    
    }
    
    

2.3 结果跳转

A - ModelAndView方式

  1. 配置视图解析器

    页面 = [视图解析器前缀 + ] viewName [ + 视图解析器后缀]
    
  2. 对应Controller

    • 具体的controller是实现接口的. 返回的结果需要用MV对象封装
    // 伪代码
    public class HelloController implements Controller{
        public ModelAndView handleRequest(req, resp){
            mv.addObject(...);
            return mv;
        }
    }
    
    • controller如果是注解, 则要用Model.addAttribute()

      还有个ModelMap| ModelAndView, Model与之很像, 但更精简! 更常用Model

    @Controller // 被包扫描配置扫描到
    public class HelloController {
    
        @RequestMapping("/hello")
        public String hello(Model model){
            // 处理业务, 封装数据
            model.addAttribute("msg","Hello, SrpingMVC-annotaion!");
            return "hello"; // 会自动被视图解析器解析
        }
    
    }
    

B - HttpServletResponse方式

使用Servlet的API, 比较原始

如果不用视图解析器, resp和req可以直接声明入方法形参, 直接可以用

  • 输出

    resp.getWriter().print("Hello world!");
    
  • 重定向

    resp.sendRedirect("/index.jsp");
    
  • 转发

    resp.getRequestDispatcher("/WEB-INF/jsp/test.jsp").forward(req, resp);
    

C 重定向 转发

  • 无视图解析器

    return "redirect:/index.jsp";	// 重定向
    return "forward:/index.jsp";	// 转发
    
  • 有视图解析器

    return "redirect:/index.jsp";	// 与重定向, 与没有视图解析器相同
    return "index";	//转发
    

2.4 请求数据处理

  • 如果前端name = 后端参数名name
    • 直接就可以了
  • 如果前端username != 后端参数名name
    • 借助@RequestParam("username") String name
    • 老师建议都加这个注解, 可以有效区分前端参数和普通参数
  • 如果前端正好符合后端的一个对象
    1. 后端可以直接用对象当参数
    2. 前提: 参数名要一致

2.5 POST中文乱码

手写Filter

可解决, 注意过滤器的配置<url-pattern>要配置/*

过滤器拦截路径配置 (重要, 易忘)

  • / - 所有请求, 不包含.jsp页面
  • /* - 所有请求, 包含.jsp页面

Spring的Filter

(与上类似)

3 JSON

JSON <--> JS 互转

    <script type="text/javascript">

        var v1 = {
            id:1001,
            name:"张三",
            age:18
        };
        var j = JSON.stringify(v1);
        console.log(j)
        var obj = JSON.parse(j);
        console.log(obj)

    </script>

3.1 Java中生成JSON: Jackson

可以借助Json解析工具, 如: Jackson | fastjson

如何使用Jackson?

  1. 依赖jar

    <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.10.0</version>
    </dependency>
    
    
  2. Controller层

    package com.kuang.controller;
    
    import com.fasterxml.jackson.core.JsonProcessingException;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.kuang.pojo.User;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    @Controller
    public class JsonController {
    
        @RequestMapping(value="/json",produces = "application/json;charset=utf8")   //解决乱码问题
        @ResponseBody   // 不经过视图解析器解析 或者用类上@RestController
        public String testJson() throws JsonProcessingException {
            User user = new User(1001, "张三", 18);
    
            ObjectMapper mapper = new ObjectMapper();
            String stringJson = mapper.writeValueAsString(user);
            return stringJson;
        }
    }
    

3.2 List to JSON

// 输出结果是
[{name:"dfkjdk",id:213},{},{}]

3.3 Date to JSON

  • 传统手艺

    String dateSDF = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
    return mapper.writeValueAsString(dateSDF);
    
    // 结果 样式
    "2011-11-11 11:11:11"
    
  • Jackson的API

    1. 停用默认时间戳
    2. sdf格式传入
    objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
    mapper.setDateFormat(sdf);
    objectMapper.writeValueAsString(new Date());
    

3.4 乱码问题

  • 可以如上述3.1中一个一个解决

  • 也可以统一解决

    统一解决, 在Spring的核心配置文件中配置

3.5 FastJson

from Alibaba

String str = JSON.toJSONString(userList  user);	
User user = JSON.parseObject(str, User.class);	

JSONObject jsonObject = (JSONObject) JSON.toJSON(user);
jsonObject.getString("name");
User user = JSON.toJavaObject(jsonObject, User.class);

4 AJAX

4.1 试试

jQuery.ajax(...)

部分参数

  • url: 请求地址

  • type: 请求方式 GET POST 1.9.0之后用method

  • headers: 请求头

  • data: 要发送的数据

  • contenType: 即将发送给服务器的内容的编码类型(默认: application/x-www-form-urlencode)

  • async: 是否异步

  • timeout: 设置请求超时时间

  • beforeSend: 发送请求前执行的函数 全局

  • complete: 完成之后的回调函数

  • success: 成功之后执行的回调函数

  • error: 失败之后执行的回调函数

  • accepts: 通过请求头发送给服务器, 告诉服务器当前客户端可接受的数据类型

  • dataType: 将服务器返回的数据转换成指定类型

    • "xml": 将服务器返回的内容换成xml格式
    • "text": 普通文本
    • "html"
    • "script"
    • "json"
    • "jsonp"

代码示例

test.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
    <script src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-3.4.1.js"></script>
    <script type="text/javascript">
        function a(){
            $.post({
                url:"${pageContext.request.contextPath}/a1",
                data:{"name":$("#username").val()},
                success:function(data){
                    alert(data);
                }
            });
        };


    </script>
</head>
<body>
<%--失去焦点时发起一个请求--%>
用户名:<input type="text" id="username" onblur="a()">

</body>
</html>

package com.kuang.controller;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.kuang.pojo.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Controller
public class JsonController {

    @RequestMapping(value = "/a1", produces="application/json;charset=utf8")
    @ResponseBody
    public void a1(String name, HttpServletResponse resp) throws IOException {    // 故意写成name
        System.out.println("a1:param-->" + name);
        if ("kuangshen".equals(name)){
            resp.getWriter().print("yes");
        } else {
            resp.getWriter().print("no");
        }
    }

    @RequestMapping(value = "/index2")
    public String index(){
        return "test2";
    }

}

4.2 结合JSON

X BUG

Artifact中一定要确保所有依赖都手动添加过! 别忘了中途添加的依赖!

原文地址:https://www.cnblogs.com/clzhang/p/12761148.html