每天叫醒你的不是闹钟,而是梦想
01
追溯web.xml
远古时期,使用servlet做web开发的时候,我们需要配置web.xml文件;
近代时期,使用springMVC做web开发的时候,我们需要配置web.xml文件;
现代时期,使用SpringBoot做web开发的时候,我们不需要配置web.xml文件
这是为什么呢?
02
web.xml干了什么?
要了解SpringBoot为什么不需要配置web.xml文件,我们首先要知道web.xml干了什么,是不是SpringBoot用了什么其他文件代替?
首先我们要从servlet讲起,在servlet-api2.5的时候,我们要注册一个servlet需要引入web.xml,我们需要对每一个请求进行一个类的映射;直到servlet-api3.0的发布,我们注册一个servlet就简单了,只需要引入一个@WebServlet注解。
接着我们进入spring+springmvc(2.5)时代,依旧需要使用web.xml,我们可以了解一下这个时候的web.xml配置文件的内容。
我们去Spring官网查看关于SpringMVC模块提供的web.xml内容
首先是ContextLoaderListener类,该类是指定Spring要加载的配置文件,加载我们的spring配置文件。而在spring的配置文件中,会有
<context:component-scan base-package="com.zero "/>
该行代码的作用是扫描业务类(service,dao层的注解)
接着是我们的DispatcherServlet,该作用是加载spring-mvc的配置文件,在spring-mvc的配置文件中
<context:component-scan base-package="com.zero.controller"></context:component-scan>
该行代码是扫描映射类(controller的注解)
接着是
<load-on-startup>1<load-on-startup>
该配置表示容器启动的时候执行DispatcherServlet的init方法
最后是
<url-mapping>/*<url-mapping>
拦截请求
总结:web.xml需要做的事情就是 加载spring配置文件,加载springmvc文件,注册一个servlet,拦截请求。
03
SpringMVC5用java代码替代了web.xml
那么我们将刚刚的Spring官网上的内容往上翻
我们可以看到这段代码,上面的最后一行文档告诉我们:
以下Java配置实例注册并初始化DispacherServlet,由Servlet容器自动给检测,也就是说,在SpringMVC5的时候,已经由Java代码代替了web.xml配置文件,那么我们来了解一下官网的这段代码。
public class MyWebApplicationInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletCxt) {
// Load Spring web application configuration 加载spring配置文件
AnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext();
ac.register(AppConfig.class);
ac.refresh();
// Create and register the DispatcherServlet 创建和注册DispatcherServlet
DispatcherServlet servlet = new DispatcherServlet(ac);
ServletRegistration.Dynamic registration = servletCxt.addServlet("app", servlet);
registration.setLoadOnStartup(1); //<load-on-startup>1</load-on-startup>
registration.addMapping("/app/*"); //<url-pattern>/app/*</url-pattern>
}
}
所以在spring5的时候,web.xml配置文件已经完全被Java代码取代。这其实就是一个过渡。
在该配置中,我们可以看到的是MyWebApplicationInitializer 实现了WebApplicationInitializer 这个接口,然后在onStartup方法中实现了配置,那么也就是说WebApplicationInitializer 的实现类只要调用了onStartup方法就可以进行一些初始化。
那么其实在servlet-api3.1的标准中定义了一个规范,如果你想要启动容器的时候定义这么一个配置,那么你需要在某个文件夹下(/META-INF/services/javax.servlet.ServletContainerInitializer)定义一个容器时会回调的类,这个类必须要实现ServletContainerInitializer接口,那么在web容器启动时就会做一些初始化工作,而这些初始化工作是在onStartup方法中执行的。
接下来我们看一下ServletContainerInitializer接口
根据注释:为了支持可以不使用web.xml。提供了ServletContainerInitializer这个接口,该接口通过SPI机制,当web容器启动的时候,会自动添加相应jar包下找到META-INF/services/javax.servlet.ServletContainerInitializer文件中的实现类,将他们实例化。
于是我们找到了这个文件,该文件下有一个实现类,我们进入这个实现类
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
@Override
public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
throws ServletException {
List<WebApplicationInitializer> initializers = new LinkedList<>();
if (webAppInitializerClasses != null) {
for (Class<?> waiClass : webAppInitializerClasses) {
if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
try {
initializers.add((WebApplicationInitializer)
ReflectionUtils.accessibleConstructor(waiClass).newInstance());
}catch (Throwable ex) {
throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
}
}
}
}
那么该实现类就会通过SPI机制,在web容器加载的时候会自动被调用,类上的@handlesTypes注解,它的作用是将感兴趣的一些类注入到ServletContainerInitializerde接口中,而这个类的方法就会扫描到WebApplicationIntializer的实现类,调用它的onStartup方法,从而起到了web.xml的作用。因为我们上面已经说过:WebApplicationInitializer 的实现类只要调用了onStartup方法就可以进行一些初始化,也就是加载了web.xml配置。