0
点赞
收藏
分享

微信扫一扫

SpringMvc高级应用----将使用自己手写的SpringMvc改造为使用真正的SpringMvc

源码(码云):https://gitee.com/yin_zhipeng/implement-_springmvc_of_myself.git

文章目录

一、手写SpringMVC

篇幅原因,我将其放在这篇文章https://blog.csdn.net/grd_java/article/details/123000127

二、SpringMVC高级应用

Spring MVC
与原生Servlet的区别

1. 环境搭建(很重要,必须知道为什么这么用)

  <packaging>war</packaging>

  <name>advanced_application_of_springmvc Maven Webapp</name>
  <!-- FIXME change it to the project's website -->
  <url>http://www.example.com</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>8</maven.compiler.source>
    <maven.compiler.target>8</maven.compiler.target>
  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
    </dependency>
    <!--spring MVC-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.1.12.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>3.1.0</version>
      <scope>provided</scope><!--tomcat如果有,不使用这个使用自己的,避免冲突-->
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <!--指定编译级别-->
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId><!--加个插件-->
        <version>3.1</version>
        <configuration>
          <source>8</source><!--JDK-->
          <target>8</target><!--编译-->
          <encoding>utf-8</encoding><!--编码-->
<!--          <compilerArgs>-->
<!--            <arg>-parameters</arg>&lt;!&ndash;告诉编译器,编译时,记录形参的真实名称&ndash;&gt;-->
<!--          </compilerArgs>-->
        </configuration>
      </plugin>
      <!--tomcat 7插件-->
      <plugin>
        <groupId>org.apache.tomcat.maven</groupId>
        <artifactId>tomcat7-maven-plugin</artifactId>
        <version>2.2</version>
        <configuration>
          <port>8080</port>
          <path>/</path>
          <uriEncoding>UTF-8</uriEncoding>
        </configuration>
      </plugin>
    </plugins>
  </build>
  1. 编写spring配置文件,配置springmvc,我们需要知道springmvc扫描路径,因为我们通过注解标识Controller类,我们得告诉springmvc去哪找这些类。还得配置视图解析器,解耦合
  1. 选择性配置,处理器映射器和处理器适配器。这两个都有默认实现,如果不配置就用默认的,我们可以自己配置,选择最适合我们的。我们可以添加一个注解驱动
    在这里插入图片描述
<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
">

    <!--让spring扫描注解-->
    <context:component-scan base-package="com.yzpnb.controller"></context:component-scan>
    <!--spring mvc视图解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/"></property>
        <property name="suffix" value=".jsp"></property>
    </bean>
    <!--处理器映射器,处理器适配器
        -   自动注册最合适的
    -->
    <mvc:annotation-driven></mvc:annotation-driven>
</beans>
<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <display-name>Archetype Created Web Application</display-name>
  <!--配置SpringMVC唯一的Servlet,DispatchServlet-->
  <servlet>
    <servlet-name>springmvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param><!--此配置可以初始化Servlet时获取-->
      <param-name>contextConfigLocation</param-name><!--springMvc指定的参数名,就是配置文件的路径-->
      <param-value>classpath:springmvc.xml</param-value>
    </init-param>
  </servlet>
  <servlet-mapping>
    <servlet-name>springmvc</servlet-name>
    <!--
      拦截请求
      方式一:*.action   *.do .....带后缀,可以准确拦截带后缀的请求
      方式二:/    拦截所有URL请求
      方式三:/*   除了拦截URL,还会拦截例如.jsp文件这种,jsp文件这种东西一般自己处理,而不需要springmvc处理
    -->
    <url-pattern>/</url-pattern>
  </servlet-mapping>
</web-app>
启动服务器,测试

2. SpringMVC请求处理流程和九大组件

请求处理流程
9大组件(🏆需要掌握/🍅了解即可)
  1. 🏆HandlerAdapter处理器适配器
  1. 🏆HandlerExceptionResolver处理器异常解析器
  1. 🏆ViewResolver视图解析器
  1. 🍅RequestToViewNameTranslator请求到视图名翻译(转换)器
  1. 🍅LocalResolver地址解析器
  1. 🍅ThemeResolver主题处理器
  1. 🏆MultipartResolver复合解析器
  1. 🍅FlashMapManager:FlashMap管理者

3. url-pattern配置和原理

前面我们web.xml配置了DispatcherServlet拦截路径为< url-pattern>/< /url-pattern>,可以拦截所有url请求。接下来详细介绍这个配置,以及剖析其原理
那么既然配置"/"会拦截静态资源,我们应该如何解决问题呢?方案一:配置文件中配置< mvc:default-servlet-handler/>,表示遇到静态资源请求url,就交给defaultServlet处理(Tomcat)
上面的方法,有很大局限,静态文件只能放在webapp文件夹中(建立目录也可以),不能放入WEB-INF目录下,因此还有方案二:SpringMVC自己处理静态资源< mvc:resources mapping="/resources/**" location=“classpath:/”/>

    <!--静态资源配置方案-->
    <mvc:default-servlet-handler></mvc:default-servlet-handler>
    <!--
        如果请求url是/resources/******   就去classpath/下面去找,就是resources
        如果有多个,可以用逗号分隔
    -->
    <mvc:resources mapping="/static/**,/resources/**" location="classpath:/"/>

4. 数据输出Model、Map、ModelMap,请求参数绑定

一般我们使用springboot时,很少直接使用ModelAndView来进行Model数据处理,而他也是使用BindingAwareModelMap处理数据,反射通过BindingAwareModelMap做了很多事
  1. 另外还可以使用Model
    在这里插入图片描述
  2. 甚至使用Map
    在这里插入图片描述
而它们几个其实都是BindingAwareModelMap实例,只要是给BindingAwareModelMap保存的内容,都会保存到请求域中
接下来看请求参数的绑定,我们先前手写SpringMVC框架时,绑定请求参数时,通过位置和参数名,绑定到相应方法参数(Handler中的参数映射容器中),对于Servlet API(request、response)直接进行传递(DispatcherServlet的req和resp)
  1. 支持实体类参数(Pojo,java类)
  2. 支持SpringMVC封装的一些类,比如接收用户上传文件的,比如ModelMap
  3. 原理就是DispatcherServlet接收到请求后,从request中获取传输的参数(例如request.getParameter(“xxxx”)),获取获取json数据。然后根据Handler中保存的参数信息,进行合理封装
那么SpringMVC不支持的参数绑定,就会无法自动转换,比如前端给一个日期类型字符串,我们无法将其解析成日期类型。这时候就需要SpringMVC类型转换器,我们需要继承接口并实现,然后配置文件中注册它
import org.springframework.core.convert.converter.Converter;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * Converter<源类型, 目标类型>
 */
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;
    }
}
    <!--转换器-->
    <bean id="conversionServiceBean" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
        <property name="converters">
            <set>
                <bean class="com.yzpnb.converter.DateConverter"></bean>
            </set>
        </property>
    </bean>
    <mvc:annotation-driven conversion-service="conversionServiceBean"></mvc:annotation-driven>

5. Filter处理Post请求乱码,以及请求方式转换

太基础的对于怎么搞Post就不说了,这里主要讲如何用Filter过滤器处理Post乱码
get请求乱码需要修改tomcat下server.xml文件的配置
<Connector URIEncoding="utf-8" connectionTimeout="2000" port="8080" protocol="HTTP/1.1" redirectPort="8443"/>
一般我们通过指定请求_method参数来表示当前请求类型,比如put,delete。这也需要过滤器来解析
  <!--springMVC请求方式转换过滤器,检查请求参数是否有_method参数,有就按照指定请求方式转换-->
  <filter>
    <filter-name>hiddenHttpMethodFilter</filter-name>
    <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>hiddenHttpMethodFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

6. 监听器、Spring MVC拦截器、过滤器

三者区别
  1. 拦截器Interceptor:SpringMVC、Struts等表现层框架特有,不拦截jsp/html/css/image等资源访问,只会拦截访问的控制器方法(Handler)
  1. Servlet、filter、listener配置在web.xml,interceptor配置在表现层框架配置文件中

6.1 单个拦截器执行流程

执行流程如下,建议先看下面知道如何实现单拦截器,然后回来看流程
单个拦截器
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class MyInterceptorOne implements HandlerInterceptor {
    /**
     * handler执行前拦截,常用于权限校验
     * @return true表示放行,false表示不放行
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("MyInterceptorOne====>>>>preHandle");
        return true;
    }
    /**下面两个方法都不常用**/
    //handler执行完,还没跳转页面前拦截
    //modelAndView可以让我们,还没跳转时,对数据进行修改,一般不会有这种需求
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("MyInterceptorOne====>>>>postHandle");
    }
    //handler执行完,跳转页面后拦截
    //ex 可以让我们进行异常捕获,但我们有异常处理器,压根用不到
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("MyInterceptorOne====>>>>afterCompletion");
    }
}
    <!--拦截器-->
    <mvc:interceptors>
        <!--拦截所有-->
<!--        <bean class="com.yzpnb.interceptor.MyInterceptorOne"></bean>-->
        <!--拦截指定请求-->
        <mvc:interceptor>
            <mvc:mapping path="/dome/**"/>
            <!--排除特定或某些不拦截-->
<!--            <mvc:exclude-mapping path="/dome/methosd0222"/>-->
            <!--拦截器-->
            <bean class="com.yzpnb.interceptor.MyInterceptorOne"></bean>
        </mvc:interceptor>
    </mvc:interceptors>

6.2 多个拦截器执行流程

如果有多个拦截器,如何执行呢,代码都一样,就不赘述了

7. 处理文件上传Multipart形式数据

传统SSM项目需要jar包依赖,spirngBoot不需要
    <!--文件上传-->
    <dependency>
      <groupId>commons-fileupload</groupId>
      <artifactId>commons-fileupload</artifactId>
      <version>1.3.1</version>
    </dependency>
第二步需要配置文件上传解析器的Bean实例,id就是固定的multipartResolver,不要随意起名
    <!--文件上传解析器,多元素解析器-->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <!--设置上传最大限制,单位字节。-1表示没有限制-->
        <property name="maxUploadSize" value="1000000000"></property>
    </bean>
第三步,参数列表中指定MultipartFile即可,和ModelMap一样,springMVC会在反射时进行参数的封装,而像Request,MultipartFile这样的都会特殊处理。注意,参数名,需要和前端对应,否则需要注解指定和前端参数名对应,例如@RequestParam(“fileUpload”) MultipartFile file

8. 异常处理机制

(不推荐的方法)我们可以在controller中通过注解@ExceptionHandler(异常类型.class)来捕获相应异常
使用注解@ControllerAdvice声明一个类
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

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

@ControllerAdvice
public class ExceptionAdvice {
    /**
     * @ExceptionHandler(要捕获的异常类型.class)
     * @param exception 捕获到的异常,这里一定要比ExceptionHandler注解指定的类型大(或等于)。否则无法强制转换
     */
    @ExceptionHandler(ArithmeticException.class)
    public void handleException(ArithmeticException exception, HttpServletResponse response){
        try{
            response.getWriter().write("ExceptionAdvice====>>>>handleException"+exception.getMessage());
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}

9. springMVC重定向参数传递flash属性

虽然用的不多,但是也需要了解。重定向url会改变,参数会丢失,两个请求。转发,url不会变,参数不丢失,一个请求
    @RequestMapping("/A")
    public String A(String name, RedirectAttributes redirectAttributes){
        //方式一,直接字符串拼接
//        return "redirect:B?name="+name;

        //方式二,flash属性,暂存到session中,跳转页面之后,属性销毁
        redirectAttributes.addFlashAttribute("name",name);
        return "redirect:B";

    }
    @RequestMapping("/B")
    public void B(@ModelAttribute("name") String name){
        System.out.println(name);
    }

三、SpringMVC源码

篇幅原因,我将其放在这篇文章https://blog.csdn.net/grd_java/article/details/123000880
举报

相关推荐

0 条评论