1. WebApplicationInitializer
随着JavaConfig
配置方式逐步替代配置,WebApplicationInitializer
可以看做Web.xml
的替代,此接口在Web容器启动的时候会记载这个接口的实现类,从而起到Web.xml
的作用,从而我们可以通过其自定义配置Web组件.
首先我们进入这个接口,它仅有一个方法#onStartup
方法,我们并不容易看出其用意何在?而此方法又是何时被调用的?
public interface WebApplicationInitializer {
void onStartup(ServletContext var1) throws ServletException;
}
但是在这个类所属的包下有另一个类- - -SpringServletContainerInitializer
@HandlesTypes({WebApplicationInitializer.class})
public class SpringServletContainerInitializer implements ServletContainerInitializer {
public SpringServletContainerInitializer() {
}
public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext) throws ServletException {
List<WebApplicationInitializer> initializers = new LinkedList();
Iterator var4;
if(webAppInitializerClasses != null) {
var4 = webAppInitializerClasses.iterator();
while(var4.hasNext()) {
Class<?> waiClass = (Class)var4.next();
if(!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) && WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
try {
initializers.add((WebApplicationInitializer)waiClass.newInstance());
} catch (Throwable var7) {
throw new ServletException("Failed to instantiate WebApplicationInitializer class", var7);
}
}
}
}
if(initializers.isEmpty()) {
servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
} else {
servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
AnnotationAwareOrderComparator.sort(initializers);
var4 = initializers.iterator();
while(var4.hasNext()) {
WebApplicationInitializer initializer = (WebApplicationInitializer)var4.next();
initializer.onStartup(servletContext);
}
}
}
}
在SpringServletContainerInitializer#onStartup
方法中,首先判断参数WebAppInitializerClasses
这个Set
集合是否为空.若不为空并且是WebApplicationInitializer
接口的实现,就将其转存到另一个集合中,之后对集合进行排序并遍历调用其#onStartup
方法,从而我们可以知晓之前所说的WebApplicationInitializer
是如何在Web
流程中被调用的.
但是另外两个问题又了:
1.SpringServletContainerInitializer#onStartup
方法又是如何被调用的呢?
2.其类上注解@HandlesType
又有什么作用呢?
1.
官方声明中指出:为了支持无xml配置化,提供了ServletContainerInitializer
,通过SPI
机制,当启动Web
容器的时候,会自动到相应的jar
包下找到META-INF/services
下ServletContainerInitializer
的全路径名的文件,并根据其内容的全路径进行实例化.而我们可以发现文件中的名称就是SpringServletContainerInitializer
,所以SpringServletContainerInitializer
作为ServletContainerInitializer
的实现,通过SPI
机制在Web
容器加载的时候会被自动调用.
2.
- 而对于
@HandlesType
注解,其作用为:读取@HandlesType
注解value
的值,根据其值将扫描路径下所有该值所表示的接口实现,并将其生成一个Set
集合提供给#onStartup
方法使用.- 在
ApplicationContext
内部启动时会通知ServletContainerInitializer#onStartup
方法,而这个方法的第一个参数Set
集合就是注解Value
值指定的Class
集合,而在这里即为WebApplicationInitializer
实现的集合- 在下列代码中所示,在
Tomcat
所有容器的抽象类中ContainerMBean#addChild
方法中,Tomcat
在为Host
容器添加Context
子容器时,会为其分配一个ContextConfig
类,其中通过#processServletContainerInitializer
方法来获取@HandlesType
注解
public class ContainerMBean extends BaseModelMBean {
public void addChild(String type, String name) throws MBeanException {
//........
try {
if(contained instanceof StandardHost) {
HostConfig config = new HostConfig();
contained.addLifecycleListener(config);
} else if(contained instanceof StandardContext) {
ContextConfig config = new ContextConfig();
contained.addLifecycleListener(config);
}
}
}
}
-------------------------------------------------------------------------------------
public class ContextConfig implements LifecycleListener {
protected void webConfig() {
//.........
if(this.ok) {
this.processAnnotations(orderedFragments, webXml.isMetadataComplete(), javaClassCache);
}
}
protected void processServletContainerInitializers() {
//.........
ServletContainerInitializer sci = (ServletContainerInitializer)var13.next();
this.initializerClassMap.put(sci, new HashSet());
HandlesTypes ht;
try {
ht = (HandlesTypes)sci.getClass().getAnnotation(HandlesTypes.class);
}
}
}
2.AbStractAnnotationConfigDispatcherServletInitializer
- 官方文档声明中指出:
Spring MVC
中我们其实是可以创建多个DispatcherServlet
的,只需要创建多个继承自AbstractAnnotationConfigDispatcherServletInitializer
的子类即可,而每个DispatcherServlet
都有自己的应用上下文(servlet application context)
,这个应用上下文只针对当前的DispatcherServlet
有用,这也就是#getServletConfigClasses
的作用,用来获取这个DispatcherServlet
的应用上下文的配置类- 而除了每个
DispatcherServlet
配置类的应用上下文,还有一个公共的跟应用上下文(root application context)
,这个应用上下文的作用时为了在多个DispatcherServlet
之间共享,这也就是#getRootConfigClasses
的作用,用于返回根应用上下文的配置类,Spring
框架的机制会保证如果当前DispatcherServlet
的应用上下文中没有找到想要的Bean
,就会去根应用上下文中找.
public class SpittrWebApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer{
//将DispatcherServlet映射到"/"
@Override
protected String[] getServletMappings(){
return new String[]{"/"};
}
//配置root applicationContext应用上下文的bean
@Override
protected Class<?>[] getRootConfigClasses(){
return new Class<?>[]{RootConfig.class};
}
//配置servlet applicationContext应用上下文的bean
@Override
protected Class<?>[] getServletConfigClasses(){
return new Class<?>[]{Webconfig.class};
}
}
--------------------------------------------------------------------------------------------------------------------------------------------------------
@Configuration
@EnableWebMvc
@ComponentScan("spitter.web")
public class WebConfig implements WebMvcConfigurer{//Spring5中此接口方法均为默认方法,替代之前适配器类
//配置jsp视图解析器.否则会使用默认的BeanNameViewResolver
@Bean
public viewResolver viewResolver(){
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF.views");
resolver.setSuffix(".jsp");
return resolver;
}
//配置静态资源处理
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer){
configurer.enable();
}
}
--------------------------------------------------------------------------------------------------------------------------------------------------------
@Configuration
@EnableWebMvc
@ComponentScan(basePackage = {"spitter"},excludeFilters = {
@Filter(type = FilterType.ANNOTATION, value = EnableWebMvc.class)
})
public class RootConfig{
}
3.总结
AbStractAnnotationConfigDispatcherServletInitializer
就是Spring
给我们通过javaConfig
方式自定义DispatcherServlet
组件的实现方法,如果需要了解其他相关内容:
Spring
容器的相关知识可以参考:Spring容器及其初始化SpringMvc
自定义配置化相关知识可以参考:
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。