【SpringMVC源码解析】 ServletContainerInitializer SpringMVC源码入口解析

news/2024/7/6 10:33:04

1 ServletContainerInitializer

在web容器启动时为提供给第三方组件机会做一些初始化的工作,servlet规范(JSR356)中通过ServletContainerInitializer实现此功能。每个框架要使用ServletContainerInitializer就必须在对应的jar包的META-INF/services 目录创建一个名为javax.servlet.ServletContainerInitializer的文件,文件内容指定具体的ServletContainerInitializer实现类,那么,当web容器启动时就会运行这个初始化器做一些组件内的初始化工作。

(1) 创建JAVA-WEB项目
在这里插入图片描述
(2) 创建接口及接口相关类

public interface ZrsService {

}

public interface ZrsServiceExtend extends ZrsService {

}

public abstract class AbstractZrsService implements ZrsService {

}

public class ZrsServicImpl implements ZrsService {

}

(3) 创建ServletContainerInitializer接口实现类

/**
* @Description: HandlesTypes,获取当前接口所有相关的类
* @Auther: zhurongsheng
* @Date: 2020/3/5 15:14
*/
@HandlesTypes(value = {ZrsService.class})
public class ZrsServletContainerInitializer implements ServletContainerInitializer {
    @Override
    public void onStartup(Set<Class<?>> classs, ServletContext servletContext) throws ServletException {
        System.out.println("################################");
        for (Class clazz:classs){
            System.out.println(clazz);
        }
    }
}

(4) 在META-INF/services 下创建 javax.servlet.ServletContainerInitializer

com.servlet.ZrsServletContainerInitializer

(5) 工程结构
在这里插入图片描述
(6) 启动tomcat,发现调用了ZrsServletContainerInitializer onStartup方法
在这里插入图片描述

2 SpringMVC环境准备

(1) 创建maven 工程
在这里插入图片描述

(2) 删除webapp下所有*.xml文件
在这里插入图片描述
(3) 引入依赖

<dependencies>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.0.6.RELEASE</version>
  </dependency>
  <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
  <dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.1.0</version>
    <scope>provided</scope>
  </dependency>
  <dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.6</version>
  </dependency>
</dependencies>
<build>
  <plugins>
    <plugin>
      <artifactId>maven-compiler-plugin</artifactId>
      <configuration>
        <source>1.8</source>
        <target>1.8</target>
        <encoding>UTF-8</encoding>
        <compilerArguments>
          <extdirs>${basedir}/src/main/webapp/WEB-INF/lib</extdirs>
        </compilerArguments>
      </configuration>
    </plugin>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-war-plugin</artifactId>
      <version>2.4</version>
      <configuration>
        <failOnMissingWebXml>false</failOnMissingWebXml>
      </configuration>
    </plugin>
  </plugins>
</build>

3 查看spring-web包
在这里插入图片描述

4 初步分析SpringServletContainerInitializer

(1) 源码分析,初始化WebApplicationInitializer接口的bean,并且调用WebApplicationInitializer实现类的onStartup方法

/**
* 获取WebApplicationInitializer接口的所有类型
*/
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
    
    @Override
    public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
            throws ServletException {
        //1 创建存储WebApplicationInitializer的List
        List<WebApplicationInitializer> initializers = new LinkedList<>();
        if (webAppInitializerClasses != null) {
            for (Class<?> waiClass : webAppInitializerClasses) {
                // Be defensive: Some servlet containers provide us with invalid classes,
                // no matter what @HandlesTypes says...
                if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
                        WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
                    try {
                        //2 用反射机制创建WebApplicationInitializer实现类并且添加到List中
                        initializers.add((WebApplicationInitializer)
                                ReflectionUtils.accessibleConstructor(waiClass).newInstance());
                    }
                    catch (Throwable ex) {
                        throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
                    }
                }
            }
        }
        if (initializers.isEmpty()) {
            servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
            return;
        }
        servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
        AnnotationAwareOrderComparator.sort(initializers);
        //3 调用WebApplicationInitializerde的onStartup方法
        for (WebApplicationInitializer initializer : initializers) {
            initializer.onStartup(servletContext);
        }
    }
}

(2)WebApplicationInitializer接口实现类分析 AbstractContextLoaderInitializer
在这里插入图片描述

(3) WebApplicationInitializer接口实现类分析 AbstractDispatcherServletInitializer
在这里插入图片描述

(4) WebApplicationInitializer接口实现类分析 AbstractAnnotationConfigDispatcherServletInitializer

public abstract class AbstractAnnotationConfigDispatcherServletInitializer
        extends AbstractDispatcherServletInitializer {
    /**
     * 创建RootApplicationContext
     */
    @Override
    @Nullable
    protected WebApplicationContext createRootApplicationContext() {
        Class<?>[] configClasses = getRootConfigClasses();
        if (!ObjectUtils.isEmpty(configClasses)) {
            AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
            context.register(configClasses);
            return context;
        }
        else {
            return null;
        }
    }
    /**
     * 创建ServletApplicationContext
     */
    @Override
    protected WebApplicationContext createServletApplicationContext() {
        AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
        Class<?>[] configClasses = getServletConfigClasses();
        if (!ObjectUtils.isEmpty(configClasses)) {
            context.register(configClasses);
        }
        return context;
    }
    /**
     * 获取RootApplicationContext 配置类
     */
    @Nullable
    protected abstract Class<?>[] getRootConfigClasses();
    /**
     * 获取ServletApplicationContext配置类
     */
    @Nullable
    protected abstract Class<?>[] getServletConfigClasses();
}

(5) ServletApplicationContext和RootApplicationContext区别如下图:
ServletApplicationContext:负责Controller 视图相关的…
RootApplicationContext:负责Service、数据库相关的…
在这里插入图片描述

5 AbstractAnnotationConfigDispatcherServletInitializer作用解析

5.1 给ServletWebApplicationContext添加ServletConfig配置文件

(1) 创建配置文件

/**
* @Description: ServletConfig配置文件,ServleWebApplicationContext仅包含Controller方面的注解
* @Auther: zhurongsheng
* @Date: 2020/3/6 14:44
*/
@ComponentScan(value="com.spring",includeFilters={
        @ComponentScan.Filter(type= FilterType.ANNOTATION,classes={Controller.class, RestController.class})
},useDefaultFilters=false)
public class ZrsServletConfig {


}

(2) 创建Controller

@RestController
@RequestMapping("/hello")
public class HelloController {
    @GetMapping("")
    public String hello(){
        return "hello spring mvc";
    }
}

(3) ZrsWebInitializer

/**
* @Description: web容器启动创建时的对象
* @Auther: zhurongsheng
* @Date: 2020/3/5 23:20
*/
public class ZrsWebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
    /**
     *  RootApplicationContext  配置文件
     */
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class<?>[]{};
    }
    /**
     * ServletApplicationContext 配置文件
     */
    @Override
    protected Class<?>[] getServletConfigClasses() {
        System.out.println("ZrsWebInitializer invoke getServletConfigClasses");
        return new Class<?>[]{ ZrsServletConfig.class};
    }
    /**
     *拦截请求(静态资源、js、css.......)
     */
    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }
}

(4) 启动tomcat
在这里插入图片描述
(5) 访问URL
http://localhost:8080/spring_web_war/hello
在这里插入图片描述

5.2 @EnableWebMvc注解,等于在xml中配置

  <!-- 拦截请求,看是否是静态资源,如果不是,交给前端控制器 -->
    <mvc:default-servlet-handler/>
    <!-- 提供controller,json,静态资源 -->
    <mvc:annotation-driven></mvc:annotation-driven>

5.3 WebMvcConfigurer接口代替了springmvc在xml中的配置

(1) 视图解析器
修改ZrsServletConfig配置文件

@ComponentScan(value="com.spring",includeFilters={
        @ComponentScan.Filter(type= FilterType.ANNOTATION,classes={Controller.class, RestController.class})
},useDefaultFilters=false)
@EnableWebMvc
public class ZrsServletConfig implements WebMvcConfigurer {
    /**
     * 配置视图解析器
     */
    @Override
    public void configureViewResolvers(ViewResolverRegistry registry) {
        registry.jsp("/WEB-INF/pages/",".jsp");
    }
}

修改controller

@Controller
@RequestMapping("/hello")
public class HelloController {
    @GetMapping("")
    public String hello(){
        return "hello spring mvc";
    }
    @GetMapping("/page")
    public String page(){
        return "helloworld";
    }
}

创建jsp
在这里插入图片描述
启动tomcat
在这里插入图片描述

(2) 配置静态资源

@ComponentScan(value="com.spring",includeFilters={
        @ComponentScan.Filter(type= FilterType.ANNOTATION,classes={Controller.class, RestController.class})
},useDefaultFilters=false)
@EnableWebMvc
public class ZrsServletConfig implements WebMvcConfigurer {
    /**
     * 配置视图解析器
     */
    @Override
    public void configureViewResolvers(ViewResolverRegistry registry) {
        registry.jsp("/WEB-INF/pages/",".jsp");
    }
    /**
     * 静态资源开关
     */
    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }
}

创建jsp、css:
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
启动tomcat:
在这里插入图片描述


http://www.niftyadmin.cn/n/4556828.html

相关文章

CountDownLatch 源码讲解

CountDownLatch 源码讲解老规矩&#xff0c;先翻译源码。 CountDownLatch 是一种同步辅助工具&#xff0c;允许一个或多个线程等待其他线程中正在执行的一组操作完成。 CountDownLatch是用给定的count参数初始化实例。 await方法会一直阻塞到当前计数为零&#xff0c;当调用c…

高手进 C语言该怎么学起才是最好啊

因为C语言最简单 C语言足够低级 就不要涉足计算机领域 除了“指针” 非常非常地贴近计算机的底层结构 但也许并不需要“面向对象”、“模板”、“函数重载”等等一大堆概念 不必思考应该学什么 而“指针” C语言没有真正意义上的难点 不会让你迷失在概念的汪洋大海 如果你对操作…

C++STL模板库适配器之优先级队列

目录 适配器之优先级队列一丶优先级队列简介(priority_queue)二丶优先级队列代码演示1.优先级队列代码以及使用简介适配器之优先级队列 一丶优先级队列简介(priority_queue) 优先级队列.不采用严格的先进先出的顺序.而是按照优先级. 给定某一时刻位于队列头的元素. 如果两个元素…

【SpringCloud】【Hoxton】基础环境准备

01 基础环境准备 02 一文读懂Eureka 03 Zookeeper注册中心 04 Consule注册中心 05 Ribbon 06 OpenFegin 07 Hystrix全面解析 08 Gateway全面解析 09 Config配置中心 10 Bus消息总线 1 引言 记2020年3月8日 SpringCloud 最新版为Hoxton SR3。 2 创建父工程 (1) spring-cloud…

Semaphore 源码分析

Semaphore 源码分析其实Semaphore源码上的注释就说的比较清楚作用是如何的&#xff0c;下面是从源码翻译过来的解释&#xff1a;计数信号灯。从概念上讲&#xff0c;信号量维护一组许可证。如果需要&#xff0c;acquire方法就是直到获得许可才会继续执行&#xff08;阻塞&#…

C语言递增递减的问题

||| 其实这是前自置增自减、后置自增自减的问题 用VC60编译运行 而对于-i 比如&#xff1a;i&#xff1b;先返回i的值 在进行自增自减 就先返回原先的值 后置的话 相反 然后返回i 的值 比如&#xff1a;i&#xff1b;是先i自增 然后在返回最后所得的值 就先自增自减 如果是前置…

【SpringCloud】【Hoxton】一文读懂Eureka

01 基础环境准备 02 一文读懂Eureka 03 Zookeeper注册中心 04 Consule注册中心 05 Ribbon 06 OpenFegin 07 Hystrix全面解析 08 Gateway全面解析 09 Config配置中心 10 Bus消息总线 1 序言 Eureka 是 Netflix 开发的&#xff0c;一个基于 REST 服务的&#xff0c;服务注册与发…

部署 YApi 接口管理服务

安装 Node curl -sL https://rpm.nodesource.com/setup_8.x | bash - yum install -y nodejs安装 MongoDB vi /etc/yum.repos.d/mongodb-org-4.0.repo[mongodb-org-4.0] nameMongoDB Repository baseurlhttps://repo.mongodb.org/yum/redhat/$releasever/mongodb-org/4.0/x86_6…