Recently, I looked at the source code of spring and suddenly wondered how spring started its own without web.xml configuration. Given the limited ability and the first time I read the source code and posted a blog, please forgive me if I don’t know what to do~
The IDE I use is IntelliJ IDEA, which is easier to read the source code than myEclipse, and I like it very much in the black background. Then the project is run under maven tomcat7 plugin. The spring version is 4.3.2.RELEASE.
If you have written a spring web with pure annotation configuration, you should know that you need to inherit an initialization class to load the bean, and then our customized functions and beans will be loaded from this class. Below is one of my WebInitializers
@Order(1)public class WebMvcInit extends AbstractAnnotationConfigDispatcherServletInitializer { protected Class<?>[] getRootConfigClasses() { return new Class[]{RootConfig.class,WebSecurityConfig.class}; } protected Class<?>[] getServletConfigClasses() { return new Class[]{WebConfig.class}; } protected String[] getServletMappings() { return new String[]{"/"}; } @Override protected Filter[] getServletFilters() { return new Filter[]{new HiddenHttpMethodFilter()}; }}First, look at the structure of the AbstractAnnotationConfigDispatcherServletInitializer class. This is also an uml function of IDEA. Right-click Diagrams->show Diagrams in the class.
Then we directly click on AbstractAnnotationConfigDispatcherServletInitializer. You can see that this class is very simple, with only four methods. Then we pay attention to createRootApplicationContext()
@Override protected WebApplicationContext createRootApplicationContext() { Class<?>[] configClasses = getRootConfigClasses(); if (!ObjectUtils.isEmpty(configClasses)) { AnnotationConfigWebApplicationContext rootAppContext = new AnnotationConfigWebApplicationContext(); rootAppContext.register(configClasses); return rootAppContext; } else { return null; } }This method roughly means to obtain the RootClasses sent by the user (programmer) and then register the beans. These are not what we are concerned about, but this method should be executed after startup, so we can look up from this method
Under IDEA, Ctrl+G can find a method or class to call, and then set the search range to project and library
We found that the registerContextLoaderListener(ServletContext servletContext) method under AbstractContextLoaderInitializer calls the createRootApplicationContext() of the subclass to get the WebApplicationContext, and continue to find the caller of the registerContextLoaderListener(ServletContext servletContext) method. As a result, it is found that it is the onStartup(ServletContext servletContext) under this class. The AbstractContextLoaderInitializer class is posted below.
public abstract class AbstractContextLoaderInitializer implements WebApplicationInitializer { /** Logger available to subclasses */ protected final Log logger = LogFactory.getLog(getClass()); @Override public void onStartup(ServletContext servletContext) throws ServletException { registerContextLoaderListener(servletContext); } /** * Register a {@link ContextLoaderListener} against the given servlet context. The * {@code ContextLoaderListener} is initialized with the application context returned * from the {@link #createRootApplicationContext()} template method. * @param servletContext the servlet context to register the listener against */ protected void registerContextLoaderListener(ServletContext servletContext) { WebApplicationContext rootAppContext = createRootApplicationContext(); if (rootAppContext != null) { 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"); } } /** * Create the "<strong>root</strong>" application context to be provided to the * {@code ContextLoaderListener}. * <p>The returned context is delegated to * {@link ContextLoaderListener#ContextLoaderListener(WebApplicationContext)} and will * be established as the parent context for any {@code DispatcherServlet} application * contexts. As such, it typically contains middle-tier services, data sources, etc. * @return the root application context, or {@code null} if a root context is not * desired * @see org.springframework.web.servlet.support.AbstractDispatcherServletInitializer */ protected abstract WebApplicationContext createRootApplicationContext(); /** * Specify application context initializers to be applied to the root application * context that the {@code ContextLoaderListener} is being created with. * @since 4.2 * @see #createRootApplicationContext() * @see ContextLoaderListener#setContextInitializers */ protected ApplicationContextInitializer<?>[] getRootApplicationContextInitializers() { return null; }}Note that we skipped the AbstractDispatcherServletInitializer abstract class (see the uml diagram). This class mainly configures DispatcherServlet, which is the implementation of spring mvc and other functions.
Then who will load the AbstractContextLoaderInitializer? WebApplicationInitializer is already an interface, and there will be no abstract class to call it. So I tried to search the WebApplicationInitializer interface. Because large projects like spring are definitely interface-oriented, the call is generally written to the interface. Then we found the SpringServletContainerInitializer class, which implements the ServletContainerInitializer interface. This class probably means starting Up all WebApplicationInitializers. It can be said that this class is very close to our goal. Below is the SpringServletContainerInitializer
@HandlesTypes(WebApplicationInitializer.class)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()); } 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); } }}In the last foreach, start all WebApplicationInitializers. So the question is, who will start SpringServletContainerInitializer? Spring will definitely not be able to start it by itself.
In the web environment, there are only web containers. We can make a breakpoint in one of the above places and then debug it (in fact, we can completely debug = = throughout the process, which is accurate and fast, but this lacks the meaning of searching, and the scenery along the road is quite good)
You can see the startInternal method of the StandardContext class under the package org.apache.catalina.core. This is already in the scope of tomcat, so our goal has been achieved. Note that the ServletContainerInitializer interface is not under the spring package, but is javax.servlet
I guess that tomcat uses the ServletContainerInitializer interface of javax.servlet to find the classes that implement this interface in the container, then call their OnStartUp, and then spring's SpringServletContainerInitializer can start all WebApplicationInitializers, which contains the WebInitializer we wrote ourselves. In addition, spring security is also configured with annotation to implement WebApplicationInitializer, so spring is very extensible. Let’s look at the tomcat source code in the next few days to understand the tomcat mechanism.
The above is all the content of this article. I hope it will be helpful to everyone's learning and I hope everyone will support Wulin.com more.