原理简析
1. 背景知识:org.springframework.web.ServletContainerInitializer接口
org.springframework.web.ServletContainerInitializer接口在基于注解的springmvc开发中用于代替web.xml。它只有一个方法:onStartup,可以在其中注册servlet、拦截器(Filter)、监听器(Listener)这三大组件。另外,ServletContainerInitializer还可以使用@HandlesTypes在onStartup方法的参数列表中注入感兴趣的类。servlet容器启动时,会扫描每个jar包的项目根目录下的/META-INF/services/javax.servlet.ServletContainerInitializer文件,执行这个文件中指定的ServletContainerInitializer接口的实现类的onStartup方法。
2. org.springframework.web包提供的ServletContainerInitializer实现类
org.springframework.web包的/META-INF/services/javax.servlet.ServletContainerInitializer文件指定了ServletContainerInitializer接口的实现类:SpringServletContainerInitializer,首先来看一下这个类的spring源码:
package org.springframework.web; @HandlesTypes(WebApplicationInitializer.class) //(1) public class SpringServletContainerInitializer implements ServletContainerInitializer { @Override public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext) throws ServletException { List<WebApplicationInitializer> initializers = new LinkedList<WebApplicationInitializer>(); 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 { initializers.add((WebApplicationInitializer) waiClass.newInstance()); //(2) } 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); for (WebApplicationInitializer initializer : initializers) { initializer.onStartup(servletContext); //(3) } } }
(1) 通过@HandlesType注解在onStartup方法的参数列表中注入感兴趣的类,即WebApplicationInitializer;
(2) 将WebApplicationInitializer的每个实现类,都新建一个实例,并放入initializers列表中;
(3) 遍历initializers列表,对每个WebApplicationInitializer实例执行其onStartup方法。
下一个问题是:WebApplicationInitializer有哪些实现类,是用来干什么的?
3. WebApplicationInitializer的实现类
WebApplicationInitializer的实现类有很多,重点看一下AbstractAnnotationConfigDispatcherServletInitializer
package org.springframework.web.servlet.support; public abstract class AbstractAnnotationConfigDispatcherServletInitializer extends AbstractDispatcherServletInitializer { @Override @Nullable protected WebApplicationContext createRootApplicationContext() { Class<?>[] configClasses = getRootConfigClasses(); if (!ObjectUtils.isEmpty(configClasses)) { AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext(); context.register(configClasses); return context; } else { return null; } } @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(); }
这个类提供了两个方法的实现,以及两个抽象方法供子类继承
(1) createRootApplicationContext:创建根容器;
(2) createServletApplicationContext:创建servlet容器;
(3) getRootConfigClasses:抽象类,用于注册根容器的配置类,相当于spring.xml;
(4) getServletConfigClasses:抽象的类,用于注册servlet容器的配置类,相当于springmvc.xml;
另外,它还从AbstractDispatcherServletInitializer类继承了getServletMappings方法,用于注册servlet的映射。
因此,我们可以自定义一个WebApplicationInitializer,继承AbstractAnnotationConfigDispatcherServletInitializer;在servlet容器启动时,会创建spring根容器和servlet容器,代替web.xml配置文件。
4. 根容器和servlet容器
根容器用于管理@Service、@Repository等业务逻辑层和数据库交互层组件;
servlet容器用于管理@Controller、ViewResolver、HandlerMapping等跟页面处理有关的组件。
使用步骤
0. 导包或添加依赖:spring-web、spring-webmvc
1. 编写数据库访问层、业务逻辑层、控制层等组件,这个跟基于配置文件的springmvc没有区别;
2. 编写根容器和servlet容器的配置类,这里不需要添加@Configuration注解;
3. 自定义WebApplicationInitializer实现类,继承AbstractAnnotationConfigDispatcherServletInitializer;
4. 在3的实现类中注册根容器和servlet容器的配置类,以及servlet映射。
示例Demo