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:
在这里插入图片描述

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐