Spring MVC —— 关于 WebApplicationInitializer

前言

WebApplicationInitializer 入手了解代码驱动 Spring web MVC 的模式及部分细节

版本

Spring 5.3.x

SpringServletContainerInitializer

// 它会收集所有 WebApplicationInitializer 的实现类
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {

	@Override
	public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
			throws ServletException {

		List<WebApplicationInitializer> initializers = Collections.emptyList();

		// 实例化所有的 WebApplicationInitializer 实现类
		if (webAppInitializerClasses != null) {
			initializers = new ArrayList<>(webAppInitializerClasses.size());
			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) {
					}
				}
			}
		}

		// 执行它们的 onStartup 方法
		AnnotationAwareOrderComparator.sort(initializers);
		for (WebApplicationInitializer initializer : initializers) {
			initializer.onStartup(servletContext);
		}
	}

}
  • 它是 Spring 提供的 ServletContainerInitializer 的子类,关于 ServletContainerInitializer,它是 Servlet 提供的一个 SPISevlet 容器在启动时会执行对应实现类比如 SpringServletContainerInitializer,以上都是 Servlet 提供的能力
  • 该类会收集所有 WebApplicationInitializer 的实现类(由注解 @HandlesTypes(WebApplicationInitializer.class) 决定)并执行它们的 onStartup,这就是 Spring 进行的拓展了,依此实现基于 Class 的容器配置

WebApplicationInitializer

public interface WebApplicationInitializer {

	void onStartup(ServletContext servletContext) throws ServletException;

}
  • 顶层接口 WebApplicationInitializer
  • 提供了方法 onStartup,基于此来拓展 ServletContext

AbstractContextLoaderInitializer

public abstract class AbstractContextLoaderInitializer implements WebApplicationInitializer {

	// ...
	
	@Override
	public void onStartup(ServletContext servletContext) throws ServletException {
		registerContextLoaderListener(servletContext);
	}

	protected void registerContextLoaderListener(ServletContext servletContext) {

		// 创建根容器
		WebApplicationContext rootAppContext = createRootApplicationContext();
		if (rootAppContext != null) {

			// 注册一个 ContextLoaderListener
			ContextLoaderListener listener = new ContextLoaderListener(rootAppContext);

			// 允许指定 ApplicationContextInitializer
			listener.setContextInitializers(getRootApplicationContextInitializers());
			servletContext.addListener(listener);
		}
		else {
		}
	}

	// 创建根容器,子类实现
	@Nullable
	protected abstract WebApplicationContext createRootApplicationContext();

	// 子类可以指定 ApplicationContextInitializer
	@Nullable
	protected ApplicationContextInitializer<?>[] getRootApplicationContextInitializers() {
		return null;
	}

}
  • WebApplicationInitializer 的抽象子类
  • 它主要是创建 根容器WebApplicationContext) 并给 ServletContext 注册了一个 ContextLoaderListener
  • ContextLoaderListener 会在 contextInitialized 阶段配置并启动(refresh)根容器,比如配置容器的 configLocations
  • 根容器会被绑定在 ServletContext
  • 至此,Servlet 初始化后会被创建一个 WebApplicationContext 启动并作为 根容器 绑定

AbstractDispatcherServletInitializer

public abstract class AbstractDispatcherServletInitializer extends AbstractContextLoaderInitializer {

	@Override
	public void onStartup(ServletContext servletContext) throws ServletException {
		super.onStartup(servletContext);
		registerDispatcherServlet(servletContext);
	}

	protected void registerDispatcherServlet(ServletContext servletContext) {
		String servletName = getServletName();

		// 创建 web 子容器
		WebApplicationContext servletAppContext = createServletApplicationContext();

		// 创建并注册 DispatcherServlet 实例,子类可以拓展
		FrameworkServlet dispatcherServlet = createDispatcherServlet(servletAppContext);
		dispatcherServlet.setContetInitializers(getServletApplicationContextInitializers());

		ServletRegistration.Dynamic registration = servletContext.addServlet(servletName, dispatcherServlet);

		registration.setLoadOnStartup(1);
		registration.addMapping(getServletMappings());
		registration.setAsyncSupported(isAsyncSupported());

		// ...

	}

	// ...

}
  • AbstractContextLoaderInitializer 的抽象子类
  • 它主要是创建 web 子容器,并为 ServletContext 创建一个 DispatcherServlet
  • DispatcherServletSpring MVC 的核心 Servlet,主要负责路由的匹配、分发、执行、视图返回等
  • 不同于 ContextLoaderListener 驱动的 根容器web 子容器DispatcherServlet 驱动,因为 web 子容器 不像 根容器,它有自己独有的组件,以实现 web MVC 的能力
  • 具体的细节可见后续章节学习 DispatcherServlet
根容器 和 web子容器 更多是一个逻辑上的分层
根容器:通常包含的是通用的 bean 组件,即普通的 Component
web 子容器:通常包含的是 web 组件,比如 @Controller 类来
负责请求的路由与处理等
AbstractAnnotationConfigDispatcherServletInitializer
public abstract class AbstractAnnotationConfigDispatcherServletInitializer
		extends AbstractDispatcherServletInitializer {

	/**
	 * 根容器:创建 AnnotationConfigWebApplicationContext 实例并注册
	 *  		getRootConfigClasses 方法返回的配置类
	 */
	@Override
	@Nullable
	protected WebApplicationContext createRootApplicationContext() {
		Class<?>[] configClasses = getRootConfigClasses();
		if (!ObjectUtils.isEmpty(configClasses)) {
			AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
			context.register(configClasses);
			return context;
		}
		// 可能没有
		else {
			return null;
		}
	}

	/**
	 * web子容器:创建 AnnotationConfigWebApplicationContext 实例并注册
	 *  		getServletConfigClasses 方法返回的配置类
	 */
	@Override
	protected WebApplicationContext createServletApplicationContext() {
		AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
		Class<?>[] configClasses = getServletConfigClasses();
		if (!ObjectUtils.isEmpty(configClasses)) {
			context.register(configClasses);
		}
		return context;
	}

	// 子类指定根容器配置类
	@Nullable
	protected abstract Class<?>[] getRootConfigClasses();

	// 子类指定子容器配置类
	@Nullable
	protected abstract Class<?>[] getServletConfigClasses();

}
  • 最完整的抽象基类,一般都是基于此类配置
  • 它实现了父类留下的 createRootApplicationContext 方法和 createServletApplicationContext,来创建对应的 根容器web 子容器 实例
  • 我们提供的最终实现只需要指定容器读取的对应配置类即可
  • 其中 根容器 可以不指定配置类,则不创建,也就是可能不存在 根容器,但子容器一定要存在,因为 web 组件 都在子容器中

demo

至此,如果基于编程(而不是传统的 web.xml)来配置 Spring Web MVC 容器就比较清晰了,见示例:

public class MyWebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class[] { RootConfig.class };
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class[] { WebConfig.class };
    }

    @Override
    protected String[] getServletMappings() {
        return new String[] { "/" };
    }
}

-------------- RootConfig 

@Configuration
@ComponentScan({
        "com.xsn.demo.spring.webmvc.service"
        , "com.xsn.demo.spring.webmvc.dao"
})
public class RootConfig {

    // 可以注册根容器组件 ...
    
}

-----------------WebConfig 

@Configuration
@ComponentScan({
        "com.xsn.demo.spring.webmvc.controller"
})
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

	// 可以注册web子容器组件 ...
	// 可以基于 WebMvcConfigurer 拓展

}
  • 如示例,我们提供自己的 MyWebInitializer,只需要指定对应的 配置类 即可
  • 配置类可以作为驱动类配合诸如 @ComponengScan 注解来指定扫描路径,同时也可以注册单独的组件
  • 容器的父子关系就是基于 ApplicationContext 管理的,因此 子容器 可见 根容器 组件,反之则不可见
  • 至此,不需要额外的配置,使用 Web 容器(Tomcat) 启动应用即可正常访问

总结

至此,我学习了如何基于 编程 配置 Spring Web MVC 应用,同时对其原理也有了大体的认识:Spring 替我们创建了一个 DispatcherServlet 对象,它持有基于我们指定配置类创建的 AnnotationConfigWebApplicationContext 实例,但是还有以下问题还待了解:

  • ContextLoadListener 监听并启动(refresh)我们的根容器,那 web 子容器 又在何时被谁(DispatcherServlet)启动(refresh
  • DispatcherServlet 基于 web 子容器 创建哪些 Spring web 组件
  • DispatcherServlet 处理请求的更多流程细节
  • 关于上述可在下文了解

Spring MVC —— 关于 DispatcherServlet

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值