①,要想使用servlet3.0,必須使用tomcat7.0及以上版本java
①,來到jcp官網:https://www.jcp.org/en/home/indexweb
②,搜索servlet,而後下載文檔spring
①,重點看8.2.4 Shared libraries / runtimes pluggability 章節tomcat
②,重點是這一段mvc
③,翻譯以下app
一、Servlet容器啓動會掃描,當前應用裏面每個jar包的
ServletContainerInitializer的實現
二、提供ServletContainerInitializer的實現類;
必須綁定在,META-INF/services/javax.servlet.ServletContainerInitializer
文件的內容就是ServletContainerInitializer實現類的全類名;ide
④,下面這一段也很關鍵spa
⑤,大意是咱們能夠給ServletContainerInitializer 的實現類添加 @HandlesTypes 註解翻譯
⑥,在其onStartup 方法上即可以獲得咱們感興趣的類debug
⑦,總結
總結:容器在啓動應用的時候,會掃描當前應用每個jar包裏面
META-INF/services/javax.servlet.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); } } }
①,繼承關係以下
②,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(); }
因此咱們在用配置類建立springmvc應用時,只需繼承AbstractAnnotationConfigDispatcherServletInitializer,覆寫其對應方法便可