How Tomcat loads Spring and SpringMVC and Servlet related knowledge
Overview
Do you know how Tomcat loads Spring and SpringMVC? Today we will figure out this process (record the most critical things)
It will involve large and small knowledge, including design patterns when loading, Servlet knowledge, etc. You will definitely gain something after reading it~
Tomcat
tomcat is a web application server written in Java, also known as a web container, which specifically runs web programs
tomcat start
After tomcat is started, a Jvm (Java Virtual Machine) process will be generated in the operating system, and the HTTP/1.1 protocol messages sent from the configured listening port (default 8080) will be monitored.
The default configuration file is like this
When Tomcat is started, it will load the project in the webapps in its installation directory (the war package will be automatically decompressed into a project)
Small question: Do multiple projects in webapps run on the same JVM?
It runs on the same JVM (the one created when Tomcat starts), and multiple projects are multiple threads. The reason why data is not shared between projects is because the class loaders are different.
Load the web program (Spring+SpringMVC framework)
After tomcat is started, the most important thing is to generate ServletContext (Tomcat's context) , and then load the project according to the web.xml in the webapps project
The following is a partial web.xml of a SpringMVC+Spring project
<!--The following is the configuration required to load Spring--> <!--The place where Spring configures specific parameters--> <context-param> <param-name>contextConfigLocation</param-name> <param-value> classpath:applicationContext.xml </param-value> </context-param> <!--Spring boot class--> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!--The following is the configuration required to load SpringMVC--> <servlets> <servlet-name>project</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> <!--The order in which servlets are loaded, the smaller the value, the higher the priority (positive number) --> <servlet-mapping> <servlet-name>project</servlet-name> <url-pattern> *.html</url-pattern> </servlet-mapping> </servlet>
Initialize Spring
Tomcat will first load into ContextLoaderListener, and then inject the parameters written in applicationContext.xml to complete a series of Spring initialization (such as various beans, database resources, etc.)
Here is the initialization of the Ioc container that is often heard. We searched for this class and found the following code
public class ContextLoaderListener extends ContextLoader implements ServletContextListener { // Omit other methods /** * Initialize the root web application context. */ @Override public void contextInitialized(ServletContextEvent event) { initWebApplicationContext(event.getServletContext ()); } // Omit other methods }
The most important thing here is to initialize the context WebApplicationContext belonging to Spring through ServletContext and store it in ServletContext
We all know how important WebApplicationContext is. We often use webApplicationContext.getBean() to get the classes managed by Spring, so this is also the core of the IOC container
Spring uses this listener to start its own method, which is also a design pattern, called the observer pattern :
The whole process is like this. When Tomcat loads the webapps project, it first loads the classes marked in web.xml through reflection (all put into an array)
At some point, my tomcat (event source, the origin of the event) will launch an event called ServletContextEvent (with various parameters in it)
For any class that implements the ServletContextListener interface, I will call the contextInitialized method inside and pass in the event parameter.
Ahem, now I see if there are any eligible (traversal) in this array, and I find that there is a real implementation of this interface (instanceof class interface), so I call the contextInitialized method
So Spring is dynamically loaded~~
Off topic:
To load a class, you can use the full class name to load through java reflection, Class.forName (class name)
You can also directly new a class to load
Initialize SpringMVC
Looking at the configuration file, the label is servlet, we must first understand what a servlet is
Introduction to Servlets
Servlet is an interface, born for web communication (to put it bluntly, it is a class created by a bunch of big bosses of sun company meeting, and there are several fixed methods)
tomcat has a set of defined programs (in fact, not only tomcat, but also web application servers that can run java written such as Jetty, etc., all have this fixed program)
1. When tomcat loads a class, if it implements the Servlet interface, it will be recorded in a Map, and then the init() method will be executed once to initialize the Servlet
2. When tomcat receives the browser's request, it will find the Servlet processing of the corresponding path in the Map. The path is the parameter written in the <url-pattern> tag, and the service() method is called.
3. When the servlet is about to be destroyed, call the destroy() method once
Do you feel familiar when you see this? It's similar to Spring loading. After implementing an interface, it is arranged by fate (tomcat)~~
Of course, it is too much trouble for us to implement the Servlet interface by ourselves, so HttpServlet (an abstract class) helps us implement most of the methods (including the setting of the http header, doXXX method judgment, etc.)
So we just need to inherit HttpServlet and implement a few methods to use it.
SpringMVC loading
Why talk about Servlet, because the core of SpringMVC is DispatcherServlet (pre-controller), as shown in the figure
DispatcherServlet is implemented by SpringMVC, it has been implemented very well, we don't need to touch it anymore
tomcat loads the DispatcherServlet from web.xml and then calls its init() method
The servlet configuration file defaults to /WEB-INF/<servlet-name>-servlet.xml, so it is now called project-servlet.xml by default
Of course, you can also specify the file yourself
1 <!--The following is the configuration required to load SpringMVC--> 2 <servlet> 3 <servlet-name>project</servlet-name> 4 <servlet- class >org.springframework.web.servlet.DispatcherServlet</servlet - class > 5 <load-on-startup>1</load-on-startup> 6 7 <!--Specify configuration file--> 8 <init-param> 9 <param-name>contextCOnfigLocation</param-name > 10 <param-value>classPath:spring-servlet.xml</param-value> 11 </init-param> 12 13 < servlet-mapping > 14 <servlet-name>project</servlet-name> 15 <url-pattern>*.html</url-pattern> 16 </servlet-mapping> 17 </servlet>
When SpringMVC is loaded, the browser has a request. If it ends with .html, tomcat will hand it over to DispatcherServlet for processing.
The DispatcherServlet will find the corresponding processor processing according to the path, which can be understood as the Controller we wrote received (the specific SpringMVC processing process will write a blog post)
At this point, the browser sends a request, and the process of executing the code we wrote is over ~~Sahua~~
Container issues with Spring and SpringMVC
Since it is mentioned that tomcat loads these two frameworks, you find that there is no, in web.xml, Spring loading is written in front of DispatcherServlet loading
Let's take a look at the initialization method of DispatcherServlet. Since DispatcherServlet is inherited layer by layer, the initialization method has also become
public abstract class HttpServletBean extends HttpServlet implements EnvironmentCapable, EnvironmentAware { // Other methods @Override public final void init() throws ServletException { // Other code // Let subclasses do whatever initialization they like. initServletBean(); } protected void initServletBean()throws ServletException { } // Other methods } public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware { // Other methods @Override protected final void initServletBean() throws ServletException { // Other codes try { this.webApplicationContext = initWebApplicationContext(); initFrameworkServlet(); } catch (ServletException | RuntimeException ex) { logger.error("Context initialization failed" , ex); throw ex; } // Other code } protected WebApplicationContext initWebApplicationContext() { // Get the webApplicationContext created by Spring, the key WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); WebApplicationContext wac = null ; if ( this .webApplicationContext != null ) { // A context instance was injected at construction time -> use it wac = this .webApplicationContext; if (wac instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac; if (! cwac.isActive()) { // The context has not yet been refreshed -> provide services such as // setting the parent context, setting the application context id, etc if (cwac.getParent() = = null ) { // The context instance was injected without an explicit parent -> set // the root application context (if any; may be null) as the parent cwac.setParent(rootContext); // set the parent context } configureAndRefreshWebApplicationContext(cwac); } } } // Other code } // Other method }
We can see that HttpServlet implements init(), leaving the initServletBean() abstract method
The FrameworkServlet implements the initServletBean() method and defines it as final (overriding is not allowed), and initWebApplicationContext() is called in this method
And initWebApplicationContext() shows that if there is a webapplication in tomcat, get it, and then set it as the parent context of SpringMVC
So far DispatcherServlet initialization is complete (of course I omitted other initialization things)
Therefore, there is a parent-child relationship between Spring and SpringMVC containers. Since the child container can access the content of the parent container, but not vice versa, do not want to automatically inject the Controller into the Service.
Hence the following
When the Spring configuration scans the package, what happens if the Controller is also scanned in
<? xml version="1.0" encoding="UTF-8" ?> < beans xmlns ="http://www.springframework.org/schema/beans" xmlns:context ="http://www. springframework.org/schema/context" ...../ omitted here > <!-- scan all packages --> < context:component-scan base-package ="com.shop" ></ context:component-scan > <!-- The correct way of writing is this> <!-- <!-- Scan package Service implementation class --> <context:component-scan base-package ="com.shop.service" ></ context:component-scan > > </ beans >
Then, the Controller will enter the Spring context, and there will be no Controller in SpringMVC. When there is a request to the DispatcherServlet, the Controller will not be found and 404
Small question: So can SpringMVC scan all packages?
This is possible, SpringMVC can also be used without Spring, but adding Spring is for better compatibility with other frameworks (database frameworks, etc. For example, SpringMVC scans service and dao, which does not support transactions)
But if you use Spring, you can't do this, including Spring sweeping the Controller, SpringMVC also sweeping the Controller once, there will be various strange problems
Small question: Why do SpringMVC and Spring need two containers? Springboot can do it with one container.
In fact, this is related to the Spring design concept. After all, a framework cannot do everything, and it always has to cooperate with third-party frameworks. Assuming a common container is used, the probability of bean conflict between multiple third-party frameworks is very high. Each third-party framework maintains its own container and uses Spring as the parent container to live peacefully.
The reason why Springboot is loved by everyone is that it follows the default greater than the configuration. It helps us configure the commonly used frameworks (mostly write the configuration classes and change the startup registration process of the framework), whether to use SpringMVC or SpringMVC at the bottom layer, Whether to use Spring or Spring. So you can use a container to arrange the relevant content reasonably when the framework starts to register (this is not supported by all frameworks, for example, SpringCloud has its own unique container, and set it as the parent container of Springboot~)
The above is over, I hope you all like it, or you won't get lost in 3 consecutive clicks~~
Reference: https://www.cnblogs.com/wyq1995/p/10672457.html
Reference: https://blog.csdn.net/s740556472/article/details/54879954
0 Comments