zoukankan      html  css  js  c++  java
  • SpringMVC学习

    SpringMVC

    官方文档:https://docs.spring.io/spring/docs/5.2.0.RELEASE/spring-framework-reference/web.html#spring-web

    1. 介绍

    MVC

    Model(模型):数据模型,提供要展示的数据,因此包含数据和行为,可以认为是领域模型或JavaBean组件(包含数据和行为),不过现在一般都分离开来:Value Object(数据Dao) 和 服务层(行为Service)。也就是模型提供了模型数据查询和模型数据的状态更新等功能,包括数据和业务。如JavaBean。

    • 取得表单数据

    • 调用业务逻辑

    • 转向指定的页面

    View(视图):负责进行模型的展示,一般就是我们见到的用户界面,客户想看到的东西。如JSP。

    • 页面显示

    Controller(控制器):接收用户请求,委托给模型进行处理(状态改变),处理完毕后把返回的模型数据返回给视图,由视图负责展示。也就是说控制器做了个调度员的工作。如Servlet。

    • 业务逻辑

    • 保存数据的状态

    SpringMVC

    Spring MVC是Spring Framework的一部分,是基于Java实现MVC的轻量级Web框架。Spring的web框架围绕DispatcherServlet [ 调度Servlet ] 设计。DispatcherServlet的作用是将请求分发到不同的处理器。

    Spring MVC的特点:①轻量级,简单易学;②高效,基于请求响应的MVC框架;③与Spring兼容性好,无缝结合;④约定优于配置;⑤功能强大:RESTful、数据验证、格式化、本地化、主题等;⑥简洁灵活。

    Spring MVC框架像许多其他MVC框架一样, 以请求为驱动,围绕一个中心Servlet分派请求及提供其他功能,DispatcherServlet实际上是一个的Servlet (它继承自HttpServlet 基类)

    2. HelloSpringMVC

    2.1 实现方式

    方式一:配置版

    1. 新建maven项目,导入公共依赖,之后只需在该项目建立子项目即可;

      <dependencies>
          <dependency>
              <groupId>junit</groupId>
              <artifactId>junit</artifactId>
              <version>4.13</version>
          </dependency>
          <dependency>
              <groupId>org.springframework</groupId>
              <artifactId>spring-webmvc</artifactId>
              <version>5.3.4</version>
          </dependency>
          <dependency>
              <groupId>javax.servlet</groupId>
              <artifactId>servlet-api</artifactId>
              <version>2.5</version>
          </dependency>
          <dependency>
              <groupId>javax.servlet.jsp</groupId>
              <artifactId>javax.servlet.jsp-api</artifactId>
              <version>2.3.3</version>
          </dependency>
          <dependency>
              <groupId>javax.servlet</groupId>
              <artifactId>jstl</artifactId>
              <version>1.2</version>
          </dependency>
      </dependencies>
    2. 新建子项目springmvc-01-hello,添加web框架支持;

    3. 配置web.xml文件,注册DispatcherServlet;

      <?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"><!-- 注册DispatcherServlet(核心,请求分发器,前端控制器) -->
          <servlet>
              <servlet-name>springmvc</servlet-name>
              <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
              <!-- 关联springmvc的配置文件(*-servlet.xml) -->
              <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>
          <servlet-mapping>
              <servlet-name>springmvc</servlet-name>
              <url-pattern>/</url-pattern>
          </servlet-mapping>
      </web-app>
    4. 配置web.xml中关联的springmvc配置文件(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
              https://www.springframework.org/schema/beans/spring-beans.xsd"><!-- 1. 处理器映射器 -->
          <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
          <!-- 2. 处理器适配器 -->
          <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
          <!-- 3. 视图解析器 -->
          <bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
              <property name="prefix" value="/WEB-INF/jsp/"/>
              <property name="suffix" value=".jsp"/>
          </bean><bean id="/hello" class="com.wang.controller.HelloController"/>
      </beans>
    5. 编写要操作业务的Controller类,要么实现Controller接口,要么增加注解;返回一个ModelAndView,用于装数据、封视图;

      public class HelloController implements Controller {
          public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
              ModelAndView mv = new ModelAndView();
              // 业务代码
              String res = "HelloSpringMVC";
              mv.addObject("msg", res);
              // 视图跳转
              mv.setViewName("hello");
              return mv;
          }
      }
    6. 将Controller类交给SpringIOC容器,在springmvc配置文件中注册bean;

      <bean id="/hello" class="com.wang.controller.HelloController"/>
    7. 编写要跳转的jsp页面,显示ModelAndView存放的数据;

      <%@ page contentType="text/html;charset=UTF-8" language="java" %>
      <html>
      <head>
          <title>hello</title>
      </head>
      <body>
      ${msg}
      </body>
      </html>
    8. 配置Tomcat,测试。

    访问出现404,排查步骤:

    1. 查看控制台输出,看一下是不是缺少了什么jar包。

    2. 如果jar包存在,显示无法输出,就在IDEA的项目发布中,添加lib依赖!

    3. 重启Tomcat 即可解决!

    方式二:注解版

    1. 新建子项目springmvc-02-annotation,添加web框架支持,已在父项目中添加Spring框架核心库、Spring MVC、servlet , JSTL等依赖;

    2. Maven可能存在资源过滤的问题,完善配置;

      <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>
    3. 配置web.xml文件,注册DispatcherServlet,同上;

    4. 配置web.xml中关联的springmvc配置文件(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"
             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">
          <!-- 1. 自动扫描包,让指定包下的注解生效,由IOC容器统一管理 -->
          <context:component-scan base-package="com.wang.controller"/>
          <!-- 2. 让Spring MVC不处理静态资源 -->
          <mvc:default-servlet-handler/>
          <!-- 3. 支持mvc注解驱动
             在spring中一般采用@RequestMapping注解来完成映射关系,要想使@RequestMapping注解生效,
             必须向上下文中注册DefaultAnnotationHandlerMapping和一个AnnotationMethodHandlerAdapter实例
             这两个实例分别在类级别和方法级别处理。而annotation-driven配置帮助我们自动完成上述两个实例的注入。
          -->
          <mvc:annotation-driven/>
          <!-- 4. 视图解析器 -->
          <bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
              <property name="prefix" value="/WEB-INF/jsp/"/>
              <property name="suffix" value=".jsp"/>
          </bean>
      </beans>
    5. 创建Controller类;

      @Controller
      public class HelloController {
          // 真实访问地址:http://localhost:8080/hello
          @RequestMapping("/hello")
          public String helloSpringMVC(Model model){
              // 封装数据。向模型中添加属性,可以在JSP页面中取出并渲染
              model.addAttribute("msg", "HelloSpringMVC");
              // 被视图解析器处理,拼接后为/WEB-INF/jsp/hello.jsp
              return "hello";
          }
      }
    6. 创建视图层,同上;

    7. 配置Tomcat,测试。

    使用springMVC必须配置的三大件:处理器映射器、处理器适配器、视图解析器

    通常,我们只需要手动配置视图解析器,而处理器映射器处理器适配器只需要开启注解驱动即可。

    2.2 Controller

    控制器提供访问应用程序的行为,通常通过接口定义注解定义两种方法实现。控制器负责解析用户的请求并将其转换为一个模型。在Spring MVC中一个控制器类可以包含多个方法。

    • 接口定义:一个控制器中只有一个方法,如果要多个方法则需要定义多个Controller,定义的方式比较麻烦,如配置版的HelloSpringMVC;

    • 注解定义:@Controller注解类型用于声明Spring类的实例是一个控制器,如注解版的HelloSpringMVC。

    2.3 RequestMapping

    @RequestMapping注解用于映射url到控制器类或一个特定的处理程序方法。可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。

    • 只作用于方法:路径为http://localhost:8080/hello

      @Controller
      public class HelloController {
          @RequestMapping("/hello")
          public String helloSpringMVC(Model model){
              model.addAttribute("msg", "HelloSpringMVC");
              return "hello";
          }
      }
    • 同作用于类和方法:路径为http://localhost:8080/project/hello

      @Controller
      @RequestMapping("/project")
      public class HelloController {
          @RequestMapping("/hello")
          public String helloSpringMVC(Model model){
              model.addAttribute("msg", "HelloSpringMVC");
              return "hello";
          }
      }

    3. Spring MVC原理

    当发起请求时被前置的控制器拦截到请求,根据请求参数生成代理请求,找到请求对应的实际控制器,控制器处理请求,创建数据模型,访问数据库,将模型响应给中心控制器,控制器使用模型与视图渲染视图结果,将结果返回给中心控制器,再将结果返回给请求者。

    图为SpringMVC的一个较完整的流程图,实线表示SpringMVC框架提供的技术,不需要开发者实现,虚线表示需要开发者实现。

    简要分析执行流程

    1. DispatcherServlet表示前置控制器,是整个SpringMVC的控制中心。用户发出请求,DispatcherServlet接收请求并拦截请求。

      我们假设请求的url为 : http://localhost:8080/SpringMVC/hello

      如上url拆分成三部分:http://localhost:8080服务器域名、SpringMVC部署在服务器上的web站点、hello表示控制器

      通过分析,如上url表示为:请求位于服务器localhost:8080上的SpringMVC站点的hello控制器。

    2. HandlerMapping为处理器映射。DispatcherServlet调用HandlerMapping,HandlerMapping根据请求url查找Handler。

    3. HandlerExecution表示具体的Handler,其主要作用是根据url查找控制器,如上url被查找控制器为:hello。

    4. HandlerExecution将解析后的信息传递给DispatcherServlet,如解析控制器映射等。

    5. HandlerAdapter表示处理器适配器,其按照特定的规则去执行Handler。

    6. Handler让具体的Controller执行。

    7. Controller将具体的执行信息返回给HandlerAdapter,如ModelAndView。

    8. HandlerAdapter将视图逻辑名或模型传递给DispatcherServlet。

    9. DispatcherServlet调用视图解析器(ViewResolver)来解析HandlerAdapter传递的逻辑视图名。

    10. 视图解析器将解析的逻辑视图名传给DispatcherServlet。

    11. DispatcherServlet根据视图解析器解析的视图结果,调用具体的视图。

    12. 最终视图呈现给用户。

    4. RESTful风格

    Restful就是一个资源定位及资源操作的风格,不是标准也不是协议。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。

    传统方式操作资源:通过不同的参数来实现不同的效果!方法单一,post 和 get

    http://127.0.0.1/item/queryItem.action?id=1 查询GET

    http://127.0.0.1/item/saveItem.action 新增POST

    http://127.0.0.1/item/updateItem.action 更新POST

    http://127.0.0.1/item/deleteItem.action?id=1 删除GET/POST

    1. 编写Controller类;

      @Controller
      public class TranditionalController {
          @RequestMapping("/add")
          public String test(int a, int b, Model model){
              int res = a + b;
              model.addAttribute("msg", "result: " + res);
              return "test";
          }
      }
    2. 测试。

    使用RESTful操作资源:可以通过不同的请求方式来实现不同的效果!请求地址一样,但是功能可以不同!

    http://127.0.0.1/item/1 查询GET

    http://127.0.0.1/item 新增POST

    http://127.0.0.1/item 更新PUT

    http://127.0.0.1/item/1 删除DELETE

    1. 编写Controller类;

      @Controller
      public class RESTfulController {
          @RequestMapping("add/{p1}/{p2}")
          public String test(@PathVariable int p1, @PathVariable int p2, Model model){
              int res = p1 + p2;
              model.addAttribute("msg", "result: " + res);
              return "test";
          }
      }
    2. 测试。

    使用method属性指定请求类型:用于约束请求的类型,可以收窄请求范围。指定请求谓词的类型如GET、 POST、HEAD、OPTIONS、PUT、PATCH、DELETE、TRACE等。

    如:

    @RequestMapping(value = "/hello",method = {RequestMethod.POST})

    此时,若使用浏览器地址栏请求,或报错405,因为指定的请求类型为 POST ,而所有的浏览器地址栏请求默认都会是 HTTP GET 类型的。将 RequestMethod.POST 改为 RequestMethod.GET 即可请求成功。

    方法级别的注解变体

    @GetMapping
    @PostMapping
    @PutMapping
    @DeleteMapping
    @PatchMapping

    其中,@GetMapping 是一个组合注解,相当于@RequestMapping(method =RequestMethod.GET) 的一个快捷方式。

    5. 结果跳转方式

    方式一:ModelAndView

    设置ModelAndView对象 ,根据view的名称和视图解析器跳到指定的页面。

    视图解析器:

    <bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

    Controller类:

    public class HelloController implements Controller {
        public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
            ModelAndView mv = new ModelAndView();
            mv.addObject("msg", "HelloSpringMVC");
            // 视图跳转(页面:{视图解析器前缀} + viewName + {视图解析器后缀})
            mv.setViewName("hello");
            return mv;
        }
    }

    方式二:ServletAPI

    不需要视图解析器。

    @Controller
    public class ResultGo {
        // 通过HttpServletResponse进行输出
        @RequestMapping("/result/t1")
        public void test1(HttpServletRequest req, HttpServletResponse rsp) throws IOException {
            rsp.getWriter().println("Hello,Spring BY servlet API");
        }
         // 通过HttpServletResponse实现重定向
        @RequestMapping("/result/t2")
        public void test2(HttpServletRequest req, HttpServletResponse rsp) throws IOException {
            rsp.sendRedirect("/index.jsp");
        }
         // 通过HttpServletResponse实现转发
        @RequestMapping("/result/t3")
        public void test3(HttpServletRequest req, HttpServletResponse rsp) throws Exception {
            req.setAttribute("msg","/result/t3");
            req.getRequestDispatcher("/WEB-INF/jsp/test.jsp").forward(req,rsp);
        }
    }

    方式三:SpringMVC

    • 无需视图解析器实现转发和重定向

    在web目录下创建redirectAndForward.jsp文件测试。

    @Controller
    public class RedirectAndForward01 {
        @RequestMapping("/raf/t1")
        public String test01(){
            // 转发方式一
            return "/redirectAndForward.jsp";
        }
    
        @RequestMapping("/raf/t2")
        public String test02(){
            // 转发方式二
            return "forward:/redirectAndForward.jsp";
        }
    
        @RequestMapping("/raf/t3")
        public String test03(){
            // 重定向
            return "redirect:/redirectAndForward.jsp";
        }
    }
    • 需要视图解析器实现转发和重定向

    @Controller
    public class RedirectAndForward02 {
        @RequestMapping("/raf/t1")
        public String test01(){
            // 转发
            return "redirectAndForward";
        }
    
        @RequestMapping("/raf/t3")
        public String test03(){
            // 重定向
            return "redirect:/redirectAndForward.jsp";
        }
    }

    视图解析器:

    <bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

    注:重定向是客户端的,而转发是服务端内部的。重定向是让客户端去访问重定向的地址,而WEB-INF下的文件是不能通过外部访问的!

    6. 数据处理

    6.1 处理提交数据

    @Controller
    public class DataHandling {
        // 1. 提交的域名称和处理方法的参数名一致
        // http://localhost:8080/dh/t1?name=111
        @RequestMapping("/dh/t1")
        public String test01(String name){
            System.out.println(name);
            return "hello";
        }
    
        // 2. 提交的域名称和处理方法的参数名不一致
        // http://localhost:8080/dh/t2?username=111
        @RequestMapping("/dh/t2")
        public String test02(@RequestParam("username") String name){
            System.out.println(name);
            return "hello";
        }
    
        // 3. 提交一个对象
        // http://localhost:8080/dh/t3?id=1&name=111&age=18
        @RequestMapping("/dh/t3")
        public String test03(User user){
            System.out.println(user);
            return "hello";
        }
    }
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class User {
        private int id;
        private String name;
        private int age;
    }

    6.2 数据显示到前端

    • ModelAndView

      案例参考HelloSpringMVC配置版的Controller类。

    • ModelMap

      @Controller
      public class DataPresentation {
          @RequestMapping("/dp/t1")
          public String test01(ModelMap model){
              model.addAttribute("msg", "DataPresentation01");
              return "hello";
          }
      
          @RequestMapping("/dp/t2")
          public String test01(Model model){
              model.addAttribute("msg", "DataPresentation02");
              return "hello";
          }
      }

    • Model

    Model 只有寥寥几个方法只适合用于储存数据,简化了新手对于Model对象的操作和理解;

    ModelMap 继承了 LinkedMap,除了实现了自身的一些方法,同样的继承 LinkedMap 的方法和特性;

    ModelAndView 可以在储存数据的同时,可以进行设置返回的逻辑视图,进行控制展示层的跳转。

    6.3 中文乱码问题

    SpringMVC过滤器

    <filter>
        <filter-name>encoding</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>utf-8</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>encoding</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    自定义过滤器

    1. 修改tomcat配置文件(server.xml):设置编码

      <Connector URIEncoding="utf-8" port="8080" protocol="HTTP/1.1"
                 connectionTimeout="20000"
                 redirectPort="8443" />
    2. 自定义过滤器

      package com.kuang.filter;
       
      import javax.servlet.*;
      import javax.servlet.http.HttpServletRequest;
      import javax.servlet.http.HttpServletRequestWrapper;
      import javax.servlet.http.HttpServletResponse;
      import java.io.IOException;
      import java.io.UnsupportedEncodingException;
      import java.util.Map;
       
      /**
       * 解决get和post请求 全部乱码的过滤器
       */
      public class GenericEncodingFilter implements Filter {
       
          @Override
          public void destroy() {
          }
       
          @Override
          public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
              //处理response的字符编码
              HttpServletResponse myResponse=(HttpServletResponse) response;
              myResponse.setContentType("text/html;charset=UTF-8");
       
              // 转型为与协议相关对象
              HttpServletRequest httpServletRequest = (HttpServletRequest) request;
              // 对request包装增强
              HttpServletRequest myrequest = new MyRequest(httpServletRequest);
              chain.doFilter(myrequest, response);
          }
       
          @Override
          public void init(FilterConfig filterConfig) throws ServletException {
          }
       
      }
      //自定义request对象,HttpServletRequest的包装类
      class MyRequest extends HttpServletRequestWrapper {
       
          private HttpServletRequest request;
          //是否编码的标记
          private boolean hasEncode;
          //定义一个可以传入HttpServletRequest对象的构造函数,以便对其进行装饰
          public MyRequest(HttpServletRequest request) {
              super(request);// super必须写
              this.request = request;
          }
       
          // 对需要增强方法 进行覆盖
          @Override
          public Map getParameterMap() {
              // 先获得请求方式
              String method = request.getMethod();
              if (method.equalsIgnoreCase("post")) {
                  // post请求
                  try {
                      // 处理post乱码
                      request.setCharacterEncoding("utf-8");
                      return request.getParameterMap();
                  } catch (UnsupportedEncodingException e) {
                      e.printStackTrace();
                  }
              } else if (method.equalsIgnoreCase("get")) {
                  // get请求
                  Map<String, String[]> parameterMap = request.getParameterMap();
                  if (!hasEncode) { // 确保get手动编码逻辑只运行一次
                      for (String parameterName : parameterMap.keySet()) {
                          String[] values = parameterMap.get(parameterName);
                          if (values != null) {
                              for (int i = 0; i < values.length; i++) {
                                  try {
                                      // 处理get乱码
                                      values[i] = new String(values[i]
                                              .getBytes("ISO-8859-1"), "utf-8");
                                  } catch (UnsupportedEncodingException e) {
                                      e.printStackTrace();
                                  }
                              }
                          }
                      }
                      hasEncode = true;
                  }
                  return parameterMap;
              }
              return super.getParameterMap();
          }
       
          //取一个值
          @Override
          public String getParameter(String name) {
              Map<String, String[]> parameterMap = getParameterMap();
              String[] values = parameterMap.get(name);
              if (values == null) {
                  return null;
              }
              return values[0]; // 取回参数的第一个值
          }
       
          //取所有值
          @Override
          public String[] getParameterValues(String name) {
              Map<String, String[]> parameterMap = getParameterMap();
              String[] values = parameterMap.get(name);
              return values;
          }
      }
    3. 在web.xml中注册过滤器。 

    附:狂神b站视频链接

  • 相关阅读:
    2020寒假简记
    感知神经网络模型与学习算法
    信息检索模型与评估
    Diffie-Hellman密钥交换
    RSA密码体制
    MySQL基准测试(benchmark)
    MySQL数据引擎
    MySQL 多版本并发控制(MVCC)
    MySQL事务管理
    利用dotnet restore 导入本地 .nupkg 包
  • 原文地址:https://www.cnblogs.com/java-learning-xx/p/14613687.html
Copyright © 2011-2022 走看看