0,注意
①,要想使用servlet3.0,必须使用tomcat7.0及以上版本
1,下载Servlet3.0 文档
①,来到jcp官网:https://www.jcp.org/en/home/index
②,搜索servlet,然后下载文档
2,阅读文档
①,重点看8.2.4 Shared libraries / runtimes pluggability 章节
②,重点是这一段
③,翻译如下
1、Servlet容器启动会扫描,当前应用里面每一个jar包的
ServletContainerInitializer的实现
2、提供ServletContainerInitializer的实现类;
必须绑定在,META-INF/services/javax.servlet.ServletContainerInitializer
文件的内容就是ServletContainerInitializer实现类的全类名;
④,下面这一段也很关键
⑤,大意是我们可以给ServletContainerInitializer 的实现类添加 @HandlesTypes 注解
⑥,在其onStartup 方法上便可以得到我们感兴趣的类
⑦,总结
总结:容器在启动应用的时候,会扫描当前应用每一个jar包里面
META-INF/services/javax.servlet.ServletContainerInitializer
指定的实现类,启动并运行这个实现类的方法;传入感兴趣的类型;
3,springmvc对ServletContainerInitializer 类是实现
①,配置
②,查看SpringServletContainerInitializer(不复杂,挺简单的)
//感兴趣的类为WebApplicationInitializer
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
//webAppInitializerClasses:所有WebApplicationInitializer 类型的Class
@Override
public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
throws ServletException {
List<WebApplicationInitializer> initializers = new LinkedList<>();
if (webAppInitializerClasses != null) {
for (Class<?> waiClass : webAppInitializerClasses) {
// 如果该Class 不是接口,不是抽象类,并且是WebApplicationInitializer类型的类
if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
try {
//实例化该类,并加入到initializers集合中
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);
//遍历initializers集合中的类
for (WebApplicationInitializer initializer : initializers) {
//调用其onStartup方法
initializer.onStartup(servletContext);
}
}
}
4,WebApplicationInitializer 相关类详解
①,继承关系如下
②,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) {
//根据根容器,创建容器监听,相当于以前在xml文件里配置ContextLoaderListener
ContextLoaderListener listener = new ContextLoaderListener(rootAppContext);
listener.setContextInitializers(getRootApplicationContextInitializers());
servletContext.addListener(listener);
}
else {
logger.debug("No ContextLoaderListener registered, as " +
"createRootApplicationContext() did not return an application context");
}
}
//创建根容器方法是个抽象方法,留给子类实现
protected abstract WebApplicationContext createRootApplicationContext();
}
②,AbstractDispatcherServletInitializer
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
super.onStartup(servletContext);
//注册一个DispatcherServlet
registerDispatcherServlet(servletContext);
}
protected void registerDispatcherServlet(ServletContext servletContext) {
String servletName = getServletName();
Assert.hasLength(servletName, "getServletName() must not return null or empty");
//创建一个servlet容器
WebApplicationContext servletAppContext = createServletApplicationContext();
Assert.notNull(servletAppContext, "createServletApplicationContext() must not return null");
//创建一个dispatcherServlet
FrameworkServlet dispatcherServlet = createDispatcherServlet(servletAppContext);
Assert.notNull(dispatcherServlet, "createDispatcherServlet(WebApplicationContext) must not return null");
dispatcherServlet.setContextInitializers(getServletApplicationContextInitializers());
//给servlet容器 添加dispatcherServlet 得到一个注册器
ServletRegistration.Dynamic registration = servletContext.addServlet(servletName, dispatcherServlet);
if (registration == null) {
throw new IllegalStateException("Failed to register servlet with name '" + servletName + "'. " +
"Check if there is another servlet registered under the same name.");
}
//在容器启动时就加载
registration.setLoadOnStartup(1);
//添加拦截路径
registration.addMapping(getServletMappings());
registration.setAsyncSupported(isAsyncSupported());
Filter[] filters = getServletFilters();
if (!ObjectUtils.isEmpty(filters)) {
for (Filter filter : filters) {
registerServletFilter(servletContext, filter);
}
}
customizeRegistration(registration);
}
// 这是一个抽象方法,留给子类实现的
protected abstract String[] getServletMappings();
}
③,AbstractAnnotationConfigDispatcherServletInitializer
public abstract class AbstractAnnotationConfigDispatcherServletInitializer
extends AbstractDispatcherServletInitializer {
//覆盖了AbstractContextLoaderInitializer的createRootApplicationContext方法,
//用于创建根容器
protected WebApplicationContext createRootApplicationContext() {
//得到根容器的Class
Class<?>[] configClasses = getRootConfigClasses();
if (!ObjectUtils.isEmpty(configClasses)) {
//创建一个web容器
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
//注册配置类
context.register(configClasses);
return context;
}
else {
return null;
}
}
//得到根容器配置类,这是一个抽象方法,留给子类实现,覆盖了AbstractContextLoaderInitializer
protected abstract Class<?>[] getRootConfigClasses();
//得到Servlet容器配置类,覆盖了AbstractDispatcherServletInitializer 的方法
protected abstract Class<?>[] getServletConfigClasses();
}
5,总结
所以我们在用配置类创建springmvc应用时,只需继承AbstractAnnotationConfigDispatcherServletInitializer,覆写其对应方法即可