Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

What is the SpringMVC initialization process?

2025-01-19 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/03 Report--

This article mainly explains "what is the initialization process of SpringMVC". Interested friends may wish to have a look at it. The method introduced in this paper is simple, fast and practical. Let's let the editor take you to learn "what is the SpringMVC initialization process?"

The framework source code is a required course in our Coding promotion, and SSM should be the framework with which friends have the most daily contact, in which the SpringMVC initialization process is relatively simple, so today Song GE will first analyze the SpringMVC initialization process with you.

Even if you have not seen the source code of SpringMVC, you may have heard: DispatcherServlet is the brain of SpringMVC, it is responsible for the scheduling of the entire SpringMVC, it is the most core class in SpringMVC, and the entire top-level architecture design of SpringMVC is reflected here, so to understand the source code of DispatcherServlet, basically the working principle of SpringMVC is also clear in the chest.

However, DispatcherServlet inherits from FrameworkServlet,FrameworkServlet and HttpServletBean, as shown below:

So our analysis starts with HttpServletBean.

1.HttpServletBean

HttpServletBean, which inherits from HttpServlet, is responsible for injecting parameters from init-param into the properties of the current Servlet instance, and also provides subclasses with the ability to increase requiredProperties. It should be noted that HttpServletBean does not depend on the Spring container.

As you know, the initialization of HttpServlet starts with the init method, so let's start with the init method of HttpServletBean:

@ Override public final void init () throws ServletException {/ / Set bean properties from init parameters. PropertyValues pvs = new ServletConfigPropertyValues (getServletConfig (), this.requiredProperties); if (! pvs.isEmpty ()) {try {BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess (this); ResourceLoader resourceLoader = new ServletContextResourceLoader (getServletContext ()); bw.registerCustomEditor (Resource.class, new ResourceEditor (resourceLoader, getEnvironment ()); initBeanWrapper (bw); bw.setPropertyValues (pvs, true) } catch (BeansException ex) {if (logger.isErrorEnabled ()) {logger.error ("Failed to set bean properties on servlet'" + getServletName () + ", ex);} throw ex;}} / / Let subclasses do whatever initialization they like. InitServletBean ();}

In this method, you first get all the configurations of Servlet and convert them to PropertyValues, and then modify the relevant properties of the target Servlet through BeanWrapper. BeanWrapper is a tool provided in Spring that allows you to modify the properties of an object, like this:

Public class Main {public static void main (String [] args) {User user = new User (); BeanWrapper beanWrapper = PropertyAccessorFactory.forBeanPropertyAccess (user); beanWrapper.setPropertyValue ("username", "itboyhub"); PropertyValue pv = new PropertyValue ("address", "www.itboyhub.com"); beanWrapper.setPropertyValue (pv); System.out.println ("user =" + user);}}

Final output:

User = User {username='itboyhub', address='www.itboyhub.com'}

So the preceding bw actually represents the current DispatcherServlet object.

When modifying the relevant properties of the target Servlet through BeanWrapper, an initBeanWrapper method is an empty method. The developer can implement this method in the subclass if necessary, and complete some initialization operations.

After the property is configured, the initServletBean method is finally called for Servlet initialization, but this method is also an empty method that is implemented in the subclass.

This is what HttpServletBean does, which is relatively simple, load the Servlet-related properties and set them to the current Servlet object, and then call the initServletBean method to continue the initialization of Servlet.

2.FrameworkServlet

As you can see from the previous introduction, the entry method for FrameworkServlet initialization is initServletBean, so let's start with the FrameworkServlet#initServletBean method:

@ Override protected final void initServletBean () throws ServletException {/ / omitted. Try {this.webApplicationContext = initWebApplicationContext (); initFrameworkServlet ();} catch (ServletException | RuntimeException ex) {/ / omitted.}}

This method is originally quite long, but put aside the log print exception throw, the remaining core code is actually only two lines:

The initWebApplicationContext method is used to initialize WebApplicationContext.

The initFrameworkServlet method is used to initialize FrameworkServlet, but this method is an empty method and has no concrete implementation. The subclass could have overridden the method to do some initialization, but in fact the subclass did not override the method, so we will ignore it for the time being and not analyze it.

So the most important thing here is actually the initWebApplicationContext method. Let's take a look at it:

Protected WebApplicationContext initWebApplicationContext () {WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext (getServletContext ()); WebApplicationContext wac = null; if (this.webApplicationContext! = null) {wac = this.webApplicationContext; if (wac instanceof ConfigurableWebApplicationContext) {ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac; if (! cwac.isActive ()) {if (cwac.getParent () = = null) {cwac.setParent (rootContext);} configureAndRefreshWebApplicationContext (cwac) }} if (wac = = null) {wac = findWebApplicationContext ();} if (wac = = null) {wac = createWebApplicationContext (rootContext);} if (! this.refreshEventReceived) {synchronized (this.onRefreshMonitor) {onRefresh (wac);}} if (this.publishContext) {String attrName = getServletContextAttributeName (); getServletContext (). SetAttribute (attrName, wac);} return wac;}

The logic here is also clear:

1. First get the rootContext. By default, Spring sets the container to a property of ServletContext, and the key of the property is org.springframework.web.context.WebApplicationContext.ROOT, so you can call the ServletContext#getAttribute method to get the rootContext based on this key.

two。 There are three possibilities for getting a WebApplicationContext instance, that is, the process of assigning a value to a wac variable: 1. If you have assigned a value to webApplicationContext through the constructor, assign it directly to the wac variable, and if you need to set parent, set it, refresh it if you need to refresh it. This method is suitable for the environment after Servlet3.0, because only from Servlet3.0 can you directly call the ServletContext.addServlet method to register Servlet. When you register manually, you can use the WebApplicationContext that you have prepared in advance. Brother Song also said in my recorded Spring Boot video that interested partners can reply to vhr at the official account backstage to view the video details. 2. If the first step fails to assign a value to wac, call the findWebApplicationContext method to try to find the WebApplicationContext object in ServletContext, and assign a value to wac;3 if you find it. If the second step fails to assign a value to wac, then call the createWebApplicationContext method to create a WebApplicationContext object and assign it to the wac, which is generally the way to create the WebApplicationContext. With these three sets of punches, wac must be worth it.

3. When the ContextRefreshedEvent event is not triggered, call the onRefresh method to complete the container refresh (since the first and third ways to obtain WebApplicationContext will eventually call the configureAndRefreshWebApplicationContext method, then publish the event and mark the refreshEventReceived variable as true, so it will only be refreshed when the wac instance is obtained in the second way, which can be analyzed below).

4. Finally, save the wac to ServletContext. When saving, it will decide whether to save or not according to the value of the publishContext variable. PublishContext can be configured through init-param when configuring Servlet in web.xml. The purpose of saving is to facilitate access.

In the above steps, creating a WebApplicationContext object through the createWebApplicationContext method needs to be explained in detail, because WebApplicationContext is generally created in this way. Let's take a look at the relevant methods:

Protected WebApplicationContext createWebApplicationContext (@ Nullable ApplicationContext parent) {Class contextClass = getContextClass (); if (! ConfigurableWebApplicationContext.class.isAssignableFrom (contextClass)) {throw new ApplicationContextException ("Fatal initialization error in servlet with name'" + getServletName () + "': custom WebApplicationContext class [" + contextClass.getName () + "] is not of type ConfigurableWebApplicationContext");} ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext) BeanUtils.instantiateClass (contextClass); wac.setEnvironment (getEnvironment ()); wac.setParent (parent); String configLocation = getContextConfigLocation () If (configLocation! = null) {wac.setConfigLocation (configLocation);} configureAndRefreshWebApplicationContext (wac); return wac;} protected void configureAndRefreshWebApplicationContext (ConfigurableWebApplicationContext wac) {if (ObjectUtils.identityToString (wac) .equals (wac.getId () {/ / The application context id is still set to its original default value /-> assign a more useful id based on available information if (this.contextId! = null) {wac.setId (this.contextId);} else {/ / Generate default id... Wac.setId (ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString (getServletContext (). GetContextPath ()) +'/'+ getServletName ());}} wac.setServletContext (getServletContext ()); wac.setServletConfig (getServletConfig ()); wac.setNamespace (getNamespace ()); wac.addApplicationListener (new SourceFilteringListener (wac, new ContextRefreshListener (); / / The wac environment's # initPropertySources will be called in any case when the context / / is refreshed Do it eagerly here to ensure servlet property sources are in place for / / use in any post-processing or initialization that occurs below prior to # refresh ConfigurableEnvironment env = wac.getEnvironment (); if (env instanceof ConfigurableWebEnvironment) {((ConfigurableWebEnvironment) env) .initPropertySources (getServletContext (), getServletConfig ());} postProcessWebApplicationContext (wac); applyInitializers (wac); wac.refresh ();}

There are two methods involved here:

CreateWebApplicationContext

First, get the creation type, and check the creation type. If there is no problem, call the instantiateClass method to complete the creation, and then configure various properties for the created wac object. The configured configLocation is the SpringMVC configuration file path we configured in the web.xml file, and the default file path is / WEB-INF/ [servletName]-servlet.xml.

ConfigureAndRefreshWebApplicationContext

The configureAndRefreshWebApplicationContext method is also mainly used to configure & refresh WebApplicationContext. In this method, addApplicationListener is called to add a listener to wac to listen for the ContextRefreshedEvent event. When the event is received, the onApplicationEvent method of FrameworkServlet is called, and the onRefresh method is called in this method to complete the refresh. After refreshing, the refreshEventReceived variable is marked as true.

Public void onApplicationEvent (ContextRefreshedEvent event) {this.refreshEventReceived = true; synchronized (this.onRefreshMonitor) {onRefresh (event.getApplicationContext ());}}

This is the general working logic of the FrameworkServlet#initServletBean method. The onRefresh method is involved here, but this is an empty method implemented in the subclass DispatcherServlet, so let's take a look at DispatcherServlet.

3.DispatcherServlet

Let's cut the crap here and look directly at the onRefresh method, as follows:

Override protected void onRefresh (ApplicationContext context) {initStrategies (context);} protected void initStrategies (ApplicationContext context) {initMultipartResolver (context); initLocaleResolver (context); initThemeResolver (context); initHandlerMappings (context); initHandlerAdapters (context); initHandlerExceptionResolvers (context); initRequestToViewNameTranslator (context); initViewResolvers (context); initFlashMapManager (context);}

InitStrategies is called in the onRefresh method to initialize the operation. The content of initStrategies is actually very simple, which is the initialization of nine components. The initialization processes of the nine are similar. Here we take the common initialization method of view parser initViewResolvers as an example to take a look at the initialization process:

Private void initViewResolvers (ApplicationContext context) {this.viewResolvers = null; if (this.detectAllViewResolvers) {/ / Find all ViewResolvers in the ApplicationContext, including ancestor contexts. Map matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors (context, ViewResolver.class, true, false); if (! matchingBeans.isEmpty ()) {this.viewResolvers = new ArrayList (matchingBeans.values ()); / / We keep ViewResolvers in sorted order. AnnotationAwareOrderComparator.sort (this.viewResolvers);}} else {try {ViewResolver vr = context.getBean (VIEW_RESOLVER_BEAN_NAME, ViewResolver.class); this.viewResolvers = Collections.singletonList (vr);} catch (NoSuchBeanDefinitionException ex) {/ / Ignore, we'll add a default ViewResolver later. }} / / Ensure we have at least one ViewResolver, by registering / / a default ViewResolver if no other resolvers are found. If (this.viewResolvers = = null) {this.viewResolvers = getDefaultStrategies (context, ViewResolver.class); if (logger.isTraceEnabled ()) {logger.trace ("No ViewResolvers declared for servlet'" + getServletName () + "': using default strategies from DispatcherServlet.properties");}

The initial viewResolvers variable is a collection into which parsed view parser objects are placed.

First determine whether the detectAllViewResolvers variable is true, and if so, go directly to find all the view parsers in the Spring container, assign the results to viewResolvers, and then sort them. By default, the value of the detectAllViewResolvers variable is true, which can be configured in web.xml if necessary, as shown below:

Springmvc org.springframework.web.servlet.DispatcherServlet contextConfigLocation classpath:spring-servlet.xml detectAllViewResolvers false 1 springmvc /

If the value of detectAllViewResolvers is false, then you will look for a view parser named viewResolver in the Spring container, and you will find a separate view parser.

In general, we don't need to configure the value of detectAllViewResolvers in web.xml, and we load as many view parsers as there are.

As a simple example, we might configure the view parser in the SpringMVC configuration file as follows:

By default, it doesn't matter whether the id of this bean is available or not, and if so, you can take whatever value you want. Anyway, in the end, it is a view parser that is looked up by type rather than id. But if you change detectAllViewResolvers to false in web.xml, then the id value of this bean is more important, it must be viewResolver.

If no ViewResolver instance is found in the Spring container either through type lookup or id lookup, the getDefaultStrategies method is called to get a default ViewResolver instance. The default instance can be obtained as follows:

Protected List getDefaultStrategies (ApplicationContext context, Class strategyInterface) {if (defaultStrategies = = null) {try {/ / Load default strategy implementations from properties file. / / This is currently strictly internal and not meant to be customized / / by application developers. ClassPathResource resource = new ClassPathResource (DEFAULT_STRATEGIES_PATH, DispatcherServlet.class); defaultStrategies = PropertiesLoaderUtils.loadProperties (resource);} catch (IOException ex) {throw new IllegalStateException ("Could not load'" + DEFAULT_STRATEGIES_PATH + "':" + ex.getMessage ());}} String key = strategyInterface.getName (); String value = defaultStrategies.getProperty (key); if (value! = null) {String [] classNames = StringUtils.commaDelimitedListToStringArray (value); List strategies = new ArrayList (classNames.length) For (String className: classNames) {try {Class clazz = ClassUtils.forName (className, DispatcherServlet.class.getClassLoader ()); Object strategy = createDefaultStrategy (context, clazz); strategies.add ((T) strategy);} catch (ClassNotFoundException ex) {throw new BeanInitializationException ("Could not find DispatcherServlet's default strategy class [" + className + "] for interface [" + key + "]", ex) } catch (LinkageError err) {throw new BeanInitializationException ("Unresolvable class definition for DispatcherServlet's default strategy class [" + className + "] for interface [" + key + "]", err);} return strategies;} else {return Collections.emptyList ();}}

This code is actually relatively simple, which is to get the default view parser through reflection.

First, assign a value to defaultStrategies. The value of defaultStrategies is actually loaded from the DispatcherServlet.properties file. Let's take a look at the contents of this file:

As you can see, there are a total of eight default key-value pairs defined here, some of which are one or more. A total of nine components need to be initialized in the previous initStrategies method. By default, only 8 components are defined here, and one MultipartResolver is missing. It is understandable that not all projects upload files, and even if files are uploaded, it is difficult to determine which specific MultipartResolver to use. It is up to the developer to decide.

DefaultStrategies is actually loaded into these eight key-value pairs, where the view parser corresponds to org.springframework.web.servlet.view.InternalResourceViewResolver. Create an instance of this class through reflection. When there is no view parser in the Spring container, the default view parser is this.

This is how initViewResolvers works, and the other 8 are similar, except for initMultipartResolver, as follows:

Private void initMultipartResolver (ApplicationContext context) {try {this.multipartResolver = context.getBean (MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class);} catch (NoSuchBeanDefinitionException ex) {this.multipartResolver = null;}}

As you can see, it just looks up the bean instance based on the name of bean, not the default MultipartResolver.

Speaking of which, Brother Song will tell you a few more details about the configuration of SpringMVC.

The above is about the configuration of the view parser and the file upload parser. I wonder if my friends have noticed that the id of the view parser is optional, while the id of the file upload parser must be multipartResolver. Review our above source code analysis and you will know why!

At this point, I believe you have a deeper understanding of "what is the initialization process of SpringMVC". You might as well do it in practice. Here is the website, more related content can enter the relevant channels to inquire, follow us, continue to learn!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report