Spring MVC源码分析

瘦欲@ 提交于 2019-11-26 21:42:27

 

从以下三个方面进行介绍:

  1.  Servlet与Spring MVC之间的关系
  2.  Servlet框架线程是否安全
  3. Spring MVC是如何完全无web.xml启动的 

 

   Spring MVC是基于Servlet实现的封装。

    首先回顾下Servlet:

    Servlet是sun公司提供的一门用于开发动态web资源的技术。

  Sun公司在其API中提供了一个servlet接口,用户若想用发一个动态web资源(即开发一个Java程序向浏览器输出数据),需要完成以下2个步骤:

  1、编写一个Java类,实现servlet接口。

  2、把开发好的Java类部署到web服务器中。

  按照一种约定俗成的称呼习惯,通常我们也把实现了servlet接口的java程序,称之为Servlet

 

创建工程:

IDEA添加如下参数可以防止长时间Build

 

 需要Servlet环境,则进入Servlet的Jar包,两种方式:

 1.Tomcat自带的

  2.mavne 引入的

 

在JavaEE项目必须有web.xml,那么为啥在SpringBoot不需要web.xml?

1.xml版本:

public class MyServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.getWriter().write("hello world");
    }
}

xml配置:

<web-app version="2.4"
         xmlns="http://java.sun.com/xml/ns/j2ee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<servlet>
  <servlet-name>MyServlet</servlet-name>
  <servlet-class>com.toov5.servlet01.MyServlet</servlet-class>
</servlet>
  
<servlet-mapping>
  <servlet-name>MyServlet</servlet-name>
  <url-pattern>/hello</url-pattern>
</servlet-mapping>  
 
</web-app>
  

启动并且访问:

 

 

注: Servlet线程不安全,单例才会产生共享。使用适合加锁哦

 

关于过滤器和拦截器:

拦截器与过滤器区别
  • 拦截器和过滤器都是基于Aop实现,能够对请求执行之前和之后实现拦截。
  • 过滤器是基于Servlet容器实现,对Web请求之前和之后实现拦截
  • 拦截器不需要依赖于Servlet、不仅可以实现Web请求还有其他方法拦截等。

 

SpringMVC拦截器的使用
  1. 自定义拦截拦截请求Token

 

     preHandle在业务处理器处理请求之前被调用;

     postHandle在业务处理器处理请求执行完成后,生成视图之前执行;

     afterCompletion在DispatcherServlet完全处理完请求后被调用,可用于清理资源等 。afterCompletion()执行完成后开始渲染页面

 

拦截器:

/**
 * 拦截传递参数中是否有token
 */
public class TokenInterceptor implements HandlerInterceptor {

    /**
     * 请求方法前置拦截,如果返回True表示会执行目标方法(请求方法) 如果返回false的情况下,则不会执行目标方法
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println(">>>preHandle<<<");
        //回去token
        String token = request.getParameter("token");
        if (StringUtils.isEmpty(token)) {
            //响应下
            response.getWriter().print("not find token");
            return false;
        }
        return true;
    }

    /**
     *
     * @param request
     * @param response
     * @param handler
     * @param modelAndView
     * @throws Exception
     */
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println(">>>>>postHandle<<<<<<<<<");
    }

    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println(">>>afterCompletion<<<");
    }
}

配置:

@Configuration
@ComponentScan( basePackages = {"com.toov5.controller","com.toov5.service"})
@EnableWebMvc //等于开启Spring MVC注解方式
@EnableAsync
public class SpringMVCConfig implements WebMvcConfigurer {

    /**
     * 视图解析器
     * @return
     */
    @Bean
    public InternalResourceViewResolver internalResourceViewResolver(){
        InternalResourceViewResolver internalResourceViewResolver = new InternalResourceViewResolver();
        internalResourceViewResolver.setPrefix("/WEB-INF/view/");
        internalResourceViewResolver.setSuffix(".jsp");
        return internalResourceViewResolver;
    }

    //1. 手动注入拦截器到Spring中
    @Bean
   public TokenInterceptor tokenInterceptor(){
        return new TokenInterceptor();
   }
   //2. 添加拦截器
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(tokenInterceptor()).addPathPatterns("/**"); //  /**表示拦截所有请求 也可以排除某个请求
    }
    DispatcherServlet
}

注意:使用拦截器一定要关闭EnableWebMvc 否则拦截器不会生效。

 

 

ServletContainerInitializer

关于Servlet的接口:ServletContainerInitializer.  涉及到Spring Boot  Spring MVC如何实现没有web.xml启动的。

在web容器启动时为提供给第三方组件机会做一些初始化的工作,例如注册servlet或者filtes等,servlet规范中通过ServletContainerInitializer实现此功能。

每个框架要使用ServletContainerInitializer就必须在对应的jar包的META-INF/services 目录创建一个名为javax.servlet.ServletContainerInitializer的文件,文件内容指定具体的ServletContainerInitializer实现类。

Servlet容器在初始化时候可以加载一些第三方相关依赖初始化工作,比如

 

实现接口:

@HandlesTypes(value = MyHandlesType.class)
public class MyServletContainerInitializer implements ServletContainerInitializer {

    /**
     * 作用是servlet容器初始化加载一些操作 比如第三方依赖信息 手动加载Servlet、监听器、过滤器
     * @param set  获取继承该类MyHandersType类所有子类class信息 (官方称之为感兴趣的类)
     * @param servletContext
     * @throws ServletException
     */
    public void onStartup(Set<Class<?>> set, ServletContext servletContext) throws ServletException {
        // 1.打印所有感兴趣的类型
        for (Class<?> c : set) {
            System.out.println(c);
        }
        // 2.servletContext 手动注册过滤器、servlet、监听器
        ServletRegistration.Dynamic payServlet = servletContext.addServlet("payServlet", new PayServlet());
        payServlet.addMapping("/pay");
    }
}

 

定义类:

public class MyHandlesType {
}

 

继承类:

public class BookServlet extends MyHandlesType {
}

 

定义servlet:

public class PayServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.getWriter().print("this is pay");
    }
}

 

启动并访问

注: tomcat回去META-INF下面读取配置:

tomcat启动时候:

 

 

 

 

下面搭建Spring mvc的环境:

 

可以看到引入包

 <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.2.1.RELEASE</version>
    </dependency>

 

 

 

DispatcherServlet是Spring MVC的核心类,客户端所有的请求都转发到DispatcherServlet,最终执行请求定义的方法。其实就是一个Servlet类,无非就是包装来根据URL能够映射找到我们Spring mvc中定义的方法。

源代码分析:

 

 看继承图:

 

 

继承FrameworkServlet 继承 HttpServlet

面向对象思想,重写。执行先父类再之类。

请求过来先走Service方法,判断类型 如果是get,执行doGet方法。

 

 

流程:HttpServlet Service 判断请求如果Get请求

  1. getHandler(),通过url路径地址查询到具体的控制请求的方法,如果没有找到情况下直接返回404。

           Handler 对应的其实就是我们的控制层类方法

          SpringMVC容器 存放当前jvm中所有url映射的路径对应的请求的方法存放到Map集合

   Key:url value:请求方法

 SpringMVC映射路径的容器在什么时候被创建呢?Servlet在调用我们的init的方法被创建的。

 Handler请求控制层 HandlerMethod 指的请求具体方法。

 

https://www.cnblogs.com/yanghongfei/p/8507632.html

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!