Spring WebMVC zero configuration startup principle

First, let’s take a look at the first sentence of the Spring WebMVC official website.

Spring Web MVC is the original web framework built on the Servlet API and has been included in the Spring Framework from the very beginning. The formal name, “Spring Web MVC,” comes from the name of its source module (spring-webmvc), but it is more commonly known as “Spring MVC”.

Spring Web MVC is the original web framework built on the Servlet API and has been included in the Spring framework from the very beginning. The first sentence is very important.

 The second point is that DispatcherServlet ultimately inherits the parent class of HttpServlet, so the above sentence is very important.

 The DispatcherServlet introduced in the official document mentions how to quickly configure a web application

 Such as web.xml and springmvc-servlet.xml

Let's see how the official website configures web.xml

<web-app>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/app-context.xml</param-value>
    </context-param>

    <servlet>
        <servlet-name>app</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <!-- 扫描springmvc-servlet.xml -->
            <param-name>contextConfigLocation</param-name>
            <param-value></param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <!-- 拦截形式 -->
    <servlet-mapping>
        <servlet-name>app</servlet-name>
        <url-pattern>/app/*</url-pattern>
    </servlet-mapping>

</web-app>

 At the same time, the official website also gives such a paragraph that is implemented using Java config technology. It can be seen that the configuration is basically the same as the above configuration, so can we remove the web.xml?

public class MyWebApplicationInitializer implements WebApplicationInitializer {

    @Override
    public void onStartup(ServletContext servletCxt) {

        // Load Spring web application configuration
        AnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext();
        ac.register(AppConfig.class);
        ac.refresh();//这几段可以去掉

        // Create and register the DispatcherServlet
        DispatcherServlet servlet = new DispatcherServlet(ac);
        ServletRegistration.Dynamic registration = servletCxt.addServlet("app", servlet);
        registration.setLoadOnStartup(1);
        registration.addMapping("/app/*");
    }
}

The answer is yes. The question is why it is enough to implement the WebApplicationInitializer interface. Why does the onStartup method execute when the container is started?

Because a specification after servlet 3.0, such as general containers such as tomcat, jetty, etc., have implemented this specification, this method will be executed when the container is started. And there is a class in our spring mvc that also implements this specification, so it is verified that the first sentence Spring Web MVC is the original web framework built on the Servlet API . Let's take a look at the relationship between this class and the WebApplicationInitializer interface.

// This interface ServletContainerInitializer is the interface of servlet
// And the WebApplicationInitializer.class in the annotation is the interface we just implemented, and all implementations will be executed in this method
// The onStartup method overridden by this interface of WebApplicationInitializer. At the end of this code, you can see the loop execution
@HandlesTypes({WebApplicationInitializer.class})
public class SpringServletContainerInitializer implements ServletContainerInitializer {
    public SpringServletContainerInitializer() {
    }

    public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext) throws ServletException {
        List<WebApplicationInitializer> initializers = new LinkedList();
        Iterator var4;
        if (webAppInitializerClasses != null) {
            var4 = webAppInitializerClasses.iterator();

            while(var4.hasNext()) {
                Class<?> waiClass = (Class)var4.next();
                if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) && WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
                    try {
                        initializers.add((WebApplicationInitializer)ReflectionUtils.accessibleConstructor(waiClass, new Class[0]).newInstance());
                    } catch (Throwable var7) {
                        throw new ServletException("Failed to instantiate WebApplicationInitializer class", var7);
                    }
                }
            }
        }

        if (initializers.isEmpty()) {
            servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
        } else {
            servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
            AnnotationAwareOrderComparator.sort(initializers);
            var4 = initializers.iterator();

            while(var4.hasNext()) {
                WebApplicationInitializer initializer = (WebApplicationInitializer)var4.next();
                initializer.onStartup(servletContext);
            }

        }
    }
}

Then another problem is that the file springmvc-servlet.xml specified to be scanned in our web.xml cannot be scanned? What to do then.

We can see the code ac.register(AppConfig.class); I believe that comrades who have seen spring can see it at a glance. The scanning of the springmvc-servlet.xml file implemented by Java config technology works, but our web.xml and springmvc-servlet.xml will configure more notes in the actual project, such as view parser, message converter, listener and interceptor. Let's now take a look at what can be done in this AppConfig.

The official  MVC Config  configuration document adds this annotation and implements the methods in this interface (including almost all configurations that can be implemented using Javaconfig technology)

For example, our view jumps, and the message converter rewrites the method in the interface to complete the configuration

@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
    registry.jsp("/page/",".html");
}
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
    converters.add(new FastJsonHttpMessageConverter());
}

Of course, you also need to declare the configuration class and scan directory

@Configuration
@ComponentScan("com")
@EnableWebMvc

Through this we have removed web.xml and springmvc-servlet.xml and then can start the container through the embedded tomcat. Below is my environment

Remove web.xml

public class MyWebApplicationInitializer implements WebApplicationInitializer {
    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        AnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext();
        ac.register(AppConfig.class);
        DispatcherServlet servlet = new DispatcherServlet(ac);
        ServletRegistration.Dynamic registration = servletContext.addServlet("springmvc", servlet);
        registration.setLoadOnStartup(1);
        registration.addMapping("*.do");
    }
}

Remove springmvc-servlet.xml

@Configuration
@ComponentScan("com")
@EnableWebMvc
public class AppConfig implements WebMvcConfigurer {}

How to realize the embedded tomcat. We know that springboot starts the tomcat container through the main method. We also have one

Add dependencies (Springboot's embedded tomcat is not a way of adding dependencies, but instantiated and handed over to the spring container to manage. We will introduce the principle of springboot embedded tomcat before 2019-12-10)

The dependencies are as follows

<dependency>
  <groupId>org.apache.tomcat.embed</groupId>
  <artifactId>tomcat-embed-core</artifactId>
  <version>8.5.31</version>
</dependency>
public class App {
    public static void main(String[] args) throws Exception{
        Tomcat tomcat = new Tomcat();
        tomcat.setPort(80);
        Context context = tomcat.addContext("/", System.getProperty("java.io.tmpdir"));
        context.addLifecycleListener((LifecycleListener) Class.forName(tomcat.getHost().getConfigClass()).newInstance());
        tomcat.start();
        tomcat.getServer().await();
    }
}

Then we can write a test controller to test it. After the main method runs, you will see such a log message

 The next article introduces the  Spring WebMVC initialization Controller process

Guess you like

Origin blog.csdn.net/qq_38108719/article/details/103443557