Spring's disappeared web.xml

I found a web project directory structure from the Internet, as follows:

Isn’t it a familiar rush ^-^ 

A few years ago, this was a well-deserved mainstream, but with the development of technology, servlet3.0 and springboot were born, web projects based on xml configuration are gone forever, replaced by springboot. Today, let’s not talk about how springboot is played. Today’s topic is how web.xml disappears. Because the current mainstream technology is the Spring Family Bucket, the purpose of exploration is also how spring makes web.xml disappear. The core of springmvc is DispacherServlet. When it is used, the <servlet> tag is configured in web.xml. Then when web.xml disappears, how is DispacherServlet registered in the container? Solving this problem can also answer the reason why web.xml disappeared.

Servlet3.0

Starting from servlet3.0, it supports the ServletContainerInitializer interface, which means it provides a way to initialize the servlet container at the code level, such as registering servlet, listener, and filter. The introduction of ServletContainerInitializer in the official document is as follows:

The ServletContainerInitializer class is found through the jar services API. For each application, when the application starts, a ServletContainerInitializer instance is created by the container. The ServletContainerInitializer implementation provided by the framework must be bound to a file called javax.servlet.ServletContainerInitializer in the META-INF/services directory of the jar package. According to the jar services API, specify the implementation of ServletContainerInitializer. In addition to ServletContainerInitializer, we also have an annotation—HandlesTypes. The HandlesTypes annotation on the ServletContainerInitializer implementation is used to indicate some classes of interest. They may specify the annotations in the value of HandlesTypes (type, method, or automatic level annotation), or the superclass of its type inherits/implements these classes one.

Simply put, as long as the implementation class of ServletContainerInitializer is configured in a specific directory file in the jar, the servlet container will call this implementation class during the startup process, so as to achieve the function of registering servlet, listener, and filter, then web.xml The usefulness of can be completely replaced.

spring-web package

Those who are familiar with spring must know that the spring-web package is essential when developing web applications. Let’s take a look at the directory structure of this package.

Was it a pleasant surprise? For servlet3.0, spring supports the characteristics of its ServletContainerInitializer. Seeing this, I have a bottom line. Spring supports servlet registration in this way. Let's take a look at how SpringServletContainerInitializer is implemented. Source code

//1
@HandlesTypes(WebApplicationInitializer.class) 
public class SpringServletContainerInitializer implements ServletContainerInitializer {

	@Override
	public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
			throws ServletException {

		List<WebApplicationInitializer> initializers = new LinkedList<>();

		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 {
                        //2
						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);
		for (WebApplicationInitializer initializer : initializers) {
            //3
			initializer.onStartup(servletContext);    
		}
	}

}

//1 Obtain the classes of interest (WebApplicationInitializer and its subclasses) through the @HandlesTypes annotation as a collection webAppInitializerClasses

Into the onStartup method

//2 Filter out the implementation classes in the webAppInitializerClasses collection

//3 Traverse the implementation class of WebApplicationInitializer and call the onStartup method

Implementation class of WebApplicationInitializer

As shown in the figure, the abstract class in the red box specifically helps to realize the registration of DispatcherServlet. In addition, I saw a familiar class, did you? Yes, it is SpringBootServletInitializer, familiar with it, there is no rush to return everything. If Springboot needs to use an external web container to start, you need to modify the Application on the basis of the previous one. One of them is to inherit the SpringBootServletInitializer. I will not discuss it here. If you are interested, you can study it by yourself. I will also follow springboot related articles. , Friends who want to know the details can follow me^-^

Okay, let’s take a look at these three abstract classes

We are concerned about the onStartup method, the implementation of onStartup is explained in AbstractDispatcherServletInitializer

First call the onStartup method of the parent class AbstractContextLoaderInitializer, and then register the DispatcherServlet

The onStartup method in AbstractContextLoaderInitializer

The first step is to create a root container (parent container), implemented in AbstractDispatcherServletInitializer

Create AnnotationConfigWebApplicationContext, register @Configuration annotated classes, generally including service, dao (repository), bean

2. Create a ContextLoaderListener and register it in the servlet container. When the servlet container is started, refresh the root container.

Register DispatcherServlet

The first step, createServletApplicationContext(), is also implemented in AbstractAnnotationConfigDispatcherServletInitializer, which is actually an AnnotationConfigWebApplicationContext, which is bound to DispatcherServlet as a subcontainer

Initialize an AnnotationConfigWebApplicationContext instance, register @Configuration annotated classes, usually controllers, and springmvc-related beans, such as ViewRover and Interceptor, are also stored in the sub-container.

The second step is to create DispatcherServlet and bind the child container to DispatcherServlet as an attribute

The third step is to add DispatcherServlet to the Servlet container, and set the servlet-mapping path, loadOnstartup order, so when the servlet container is started, the init method of DispatcherServlet will be called, which mainly calls the initServletBean method, which is implemented in FrameworkServlet, on the code

Look at the initWebApplicationContext method

First get the root container, then set the root container as the father of the word container (here also confirms the source of the parent container and word container), and finally refresh the word container (friends familiar with spring ioc should be very aware of the importance of refresh to the spirng container )

At this point, the three inherited classes under WebApplicationInitializer have been introduced. Is it right? Suddenly, it turns out that spring is playing like this. Actually it's not over yet. I still remember when I said at the beginning that SpringServletContainerInitializer will pass in the interested WebApplicationInitailizer class and its subclasses when executing the onStartup method, and then remove the interface and abstract classes . What we just introduced are all abstract classes. My impulse? Shaoan not restless. I was also confused before, why didn't I provide an implementation class? Since I am working on a springboot project, it hides this problem. In fact, sprigboot does not take this route at all to register DispatcherServlet to the servlet container. I don’t mention how springboot is played here. What should I do if I don’t use springboot and want to go this way to register a servlet? It's very simple, there is no implementation class, just create one. Is it very simple? I have been confused for a long time (555). Upload code

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.stereotype.Controller;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

public class AnnotationConfigDispatcherServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class[]{RootConfig.class};
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class[]{ServletConfig.class};
    }

    @Override
    protected String[] getServletMappings() {
        return new String[]{"/*"};
    }
}

@Configuration
@ComponentScan(excludeFilters = {@Filter(type = FilterType.ANNOTATION, classes = {Controller.class})})
class RootConfig {

}

@Configuration
@ComponentScan(useDefaultFilters = false, includeFilters = {@Filter(type = FilterType.ANNOTATION, classes = {Controller.class})})
class ServletConfig 

The main logical abstract class has been implemented for help. I only need to implement three methods. getRootConfigClasses helps to create the root container, getServletConfigClasses helps to create the sub-container, and getServletMappings helps to configure the mapping path of DispatcherServlet.

Guess you like

Origin blog.csdn.net/qq_28411869/article/details/101053098