SpringMVC源码解析之SpringServletContainerInitializer原理分析

SpringMVC

对SpringMVC或者其它比较成熟的MVC框架而言,解决的问题无外乎以下几点:

  1. 将web页面的请求传给服务器
  2. 根据不同的请求处理不同的逻辑单元
  3. 返回处理结果数据并跳转至响应的页面

Servlet与SpringMVC之间的关系

Spring的MVC是基于Servlet功能实现的,通过实现Servlet接口的DispatcherServlet来封装其核心功能实现。

快速搭建Servlet环境

<dependencies>
  <!-- 添加Servlet支持 -->
  <dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.1.0</version>
  </dependency>
  <dependency>
    <groupId>javax.servlet.jsp</groupId>
    <artifactId>javax.servlet.jsp-api</artifactId>
    <version>2.3.1</version>
  </dependency>
</dependencies>

servlet线程是否安全?

不安全,构造函数只执行一次

ServletContainerInitializer接口

在web容器启动时会做一些初始化的工作,例如注册servlet或者filtes等,servlet规范中通过ServletContainerInitializer实现此功能。

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

1、Servlet容器启动会扫描,当前应用里面每一个jar包的ServletContainerInitializer的实现
2、提供ServletContainerInitializer的实现类;必须绑定在META-INF/services/javax.servlet.ServletContainerInitializer,文件的内容就是ServletContainerInitializer实现类的全类名;

META-INF:tomcat默认就会去读取的

案例演示:

@HandlesTypes(value = MyHandlesType.class)//该注解声明的类,会被注入到set中,如果没有合适的类,set为null
public class MyServletContainerInitializer implements ServletContainerInitializer {

    /**
     * @param set 感兴趣类型 也就是MyHandlesType 所有子类型
     * @param servletContext
     * @throws ServletException
     */
    public void onStartup(Set<Class<?>> set, ServletContext servletContext) throws ServletException {
        // 1.打印所有感兴趣的类型,会输出所有继续了MyHandlesType的子类
        for (Class<?> c : set) {
            System.out.println(c);
        }
        // 2.servletContext 手动注册过滤器、servlet、监听器
        ServletRegistration.Dynamic payServlet = servletContext.addServlet("payServlet", new PayServlet());
        payServlet.addMapping("/pay");
    }
}

基于注解方式构建SpringMVC框架

<dependencies>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.0.5.RELEASE</version>
  </dependency>
  <!-- 添加Servlet支持 -->
  <dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.1.0</version>
  </dependency>
  <dependency>
    <groupId>javax.servlet.jsp</groupId>
    <artifactId>javax.servlet.jsp-api</artifactId>
    <version>2.3.1</version>
  </dependency>
</dependencies>

经经

springmvc是如何实现无web.xml配置,靠的就是ServletContainerInitializer

在这里插入图片描述
在这里插入图片描述

SpringServletContainerInitializer源码分析

SpringServletContainerInitializer的作用:加载一些第三方的依赖信息

@HandlesTypes({WebApplicationInitializer.class})
public class SpringServletContainerInitializer implements ServletContainerInitializer {
    public SpringServletContainerInitializer() {
    }
    public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext) throws ServletException {}
}

SpringServletContainerInitializer 为spring中实现ServletContainerInitializer接口的唯一类,该类主要是从容器获取实现WebApplicationInitializer的类,并且按照次序(javax.annotation.Priority)调用其onStartup方法

我们定义的WebInitializer实现了WebApplicationInitializer接口

public class WebInitializer implements WebApplicationInitializer {
    public void onStartup(javax.servlet.ServletContext servletContext) throws ServletException {
  ....
    }
}

原理:实现了WebApplicationInitializer 接口的类会被Spring初始化;所以如果需要动态添加servlet、filter和listener,就可以实现该接口,交由spring初始化。

案例演示:

@RestController
public class IndexController {

    @RequestMapping(value = "/",produces="text/html;charset=UTF-8")
    public String index() {
        return "success...";
    }
    /**
     * springmvc环境的时候需要配置那些东西?
     * SpringMVC启动的时候如何实现没有web.xml
     */
}
@Configuration
@ComponentScan("com.mayikt.controller")
@EnableWebMvc
public class SpringMvcConfig {
    //@EnableWebMvc 等于开启SpringMVC注解方式
    //@Configuration xml
    // @ComponentScan("com.mayikt.controller") mvc 扫包范围
    //DispatcherServlet
}
public class WebInitializer implements WebApplicationInitializer {
    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        // 1.启动SpringMVC 容器 类注入到Spring中
        AnnotationConfigWebApplicationContext app = new AnnotationConfigWebApplicationContext();// 启动SpringMVC Web
        // 2.注入我们的springmvc 的配置文件
        app.register(SpringMvcConfig.class);
        // 3. 将我们的DispatcherServlet 注入到 serlvet容器中
        ServletRegistration.Dynamic dynamic = servletContext.addServlet("dispatcher", new DispatcherServlet(app));
        // 4.填写url路径映射
        dynamic.addMapping("/");
        dynamic.setLoadOnStartup(1);// 优先级最高表示 最早被加载
    }
    // 基本配置已经ok呢? web.xml ? 使用WebApplicationInitializer 替代web.xml
    //为什么这个类WebInitializer 不需要注解呢? 能够自动的找到该类呢?
}

springmvc是如何替代web.xml
使用:SpringServletContainerInitializer 提供给SpringMVC实现初始化

debug:可以看到WebApplicationInitializer子类有我们自定义的WebInitializer
在这里插入图片描述
在这里插入图片描述

总结

web项目可以脱离繁琐的web.xml配置文件,就是靠的SpringServletContainerInitializer这个类。首先,SpringServletContainerInitializer作为ServletContainerInitializer的实现类,通过SPI机制,在web容器加载的时候会自动的被调用。这个类上还有一个注解@HandlesTypes,它的作用是将感兴趣的一些类注入到ServletContainerInitializerde, 而这个类的方法又会扫描找到WebApplicationInitializer的实现类,调用它的onStartup方法,从而起到启动web.xml相同的作用。

本文参考:

蚂蚁课堂:http://www.mayikt.com/
文档:https://docs.oracle.com/javaee/7/api/javax/servlet/ServletContainerInitializer.html?is-external=true

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring MVC 是一个基于 Servlet API 构建的 Web 框架,它是 Spring Framework 的一部分,提供了 Model-View-Controller(MVC)架构模式的实现。Spring MVC 的核心思想是将应用程序的业务逻辑与视图分离,使应用程序更容易维护和扩展。 Spring MVC 的源码涉及到很多模块,比如 DispatcherServlet、HandlerMapping、HandlerAdapter、ViewResolver 等等。下面我们从 DispatcherServlet 入手,深度解析 Spring MVC 的源码。 1. DispatcherServlet DispatcherServlet 是整个 Spring MVC 框架的核心,它是一个 Servlet,所有的请求都会经过它。DispatcherServlet 的主要作用是将请求分派给相应的 HandlerMapping,然后将处理结果返回给客户端。DispatcherServlet 在初始化时会加载一些配置文件,比如 applicationContext.xml、spring-mvc.xml 等等。这些配置文件中定义了 Spring MVC 的各个组件,包括 HandlerMapping、HandlerAdapter、ViewResolver 等等。 2. HandlerMapping HandlerMapping 用来映射请求到相应的处理器,它根据请求的 URL 和其他条件来确定最终的处理器。Spring MVC 中提供了多种 HandlerMapping 实现,比如 BeanNameUrlHandlerMapping、RequestMappingHandlerMapping、SimpleUrlHandlerMapping 等等。其中,RequestMappingHandlerMapping 是最常用的 HandlerMapping 实现,它会扫描应用程序中所有带有 @Controller 注解的类,并将其中所有带有 @RequestMapping 注解的方法注册为处理器。 3. HandlerAdapter HandlerAdapter 用来确定请求处理器的类型,并调用相应的方法来处理请求。Spring MVC 中提供了多种 HandlerAdapter 实现,比如 HttpRequestHandlerAdapter、SimpleControllerHandlerAdapter、AnnotationMethodHandlerAdapter 等等。其中,AnnotationMethodHandlerAdapter 是最常用的 HandlerAdapter 实现,它会根据方法的参数类型和返回值类型动态地确定请求处理器的类型,并调用相应的方法来处理请求。 4. ViewResolver ViewResolver 用来将逻辑视图名解析为实际的视图对象,它根据逻辑视图名和其他条件来确定最终的视图对象。Spring MVC 中提供了多种 ViewResolver 实现,比如 InternalResourceViewResolver、FreeMarkerViewResolver、VelocityViewResolver 等等。其中,InternalResourceViewResolver 是最常用的 ViewResolver 实现,它会将逻辑视图名解析为 JSP 文件名,并返回一个 InternalResourceView 对象。 5. ModelAndView ModelAndView 是 Spring MVC 中最常用的视图模型对象,它包含了视图名称和模型数据。在处理请求时,请求处理器将模型数据填充到 ModelAndView 对象中,并返回一个 ModelAndView 对象作为处理结果。DispatcherServlet 会将 ModelAndView 对象传递给 ViewResolver,ViewResolver 会使用视图名称和模型数据来渲染响应结果。 以上就是 Spring MVC 源码深度解析的简要介绍,需要注意的是,Spring MVC 的源码非常庞大,涉及到很多细节问题。如果需要深入研究 Spring MVC 的源码,需要花费大量的时间和精力。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值