Springmvc source code analysis (III)-dispatcherservlet initialization process

Source: Internet
Author: User
Tags call back

When we first learned servlet programming and Java Web, there were not so many frameworks. We develop a simple function to do the simple thing, that is, inherit httpservlet, rewrite the doget and dopost methods as needed, and jump to the defined JSP page. Register the servlet class in Web. xml after the servlet class is compiled.

There is nothing else. Start the Web server and enter the address in the browser to view the page that we wrote on the browser. To better understand the above process, you need to learn about the three stages of the servlet lifecycle, the so-called "init-service-Destroy ".

I think it is enough for you to understand springmvc design ideas. Springmvc can certainly be called a complex framework, but it also follows the simplest Law in the servlet world, that is, "init-service-Destroy ". We need to analyze the springmvc initialization process, which is actually to analyze the init () method of the dispatcherservlet class. Let's take a look at the source code of dispatcherservlet with this simple idea.

1. <init-param> Configure element read

Use Eclipse IDE to open the source code of the dispatcherservlet class. CTRL + T to check it.

The initialization entry method of the dispatcherservlet class Init () is defined in the parent class httpservletbean. The httpservletbean class acts as a class that directly inherits from the httpservlet class and overwrites the init () method of the httpservlet class, implements its own initialization behavior.

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

The initservletbean () method here is an empty method in the httpservletbean class without any implementation. Its purpose is to leave it to the subclass to implement its own initialization logic, that is, the template method design pattern. Springmvc uses this pattern vividly here. The init () method is the template method in the template method pattern. The actual initialization process of springmvc is triggered by the initservletbean () method overwritten in the subclass frameworkservlet.

Let's take a look at the code of the try and catch block package in the init () method, which involves the very underlying classes in spring, such as beanwrapper, propertyvalues, and resourceeditor. To learn more about the specific code implementation, you must have a deep understanding of the source code of the Spring framework. Here, we can avoid complexity and simplicity. We can analyze the code effects and design ideas in this try and catch what the code in the block is doing:

  • Register a string to the resource file editor, so that the <init-param> configuration element under the servlet can use the form "classpath:" To specify the source of the spring MVC Framework bean configuration file.
  • Read the <init-param> configuration element under the servlet dispatcherservlet in Web. XML to the dispatcherservlet using the JavaBean method (that is, the setter method.

I want to use the following example to illustrate these two points.

The configuration of dispatcherservlet registered in Web. XML is as follows:

<! -- Spring MVC configuration start --> <servlet-Name> appservlet </servlet-Name> <servlet-class> Org. springframework. web. servlet. dispatcherservlet </servlet-class> <init-param> <param-Name> contextconfiglocation </param-Name> <param-value> classpath: spring/spring-servlet.xml </param-value> </init-param> <load-on-startup> 1 </load-on-startup> </servlet> <servlet-mapping> <servlet-Name> appservlet </servlet-Name> <URL-pattern>/</UR L-pattern> </servlet-mapping> <! -- Springmvc configuration ended -->

As you can see, I registered a <init-param> element named contextconfiglocation with the value "classpath: spring/spring-servlet.xml ", this is also a method that is often used to specify the springmvc configuration file path. The above try, Catch Block package code to play a role, one is to convert the "classpath: spring/spring-servlet.xml" string into a resource file under the classpath path, allows the framework to initialize and read configuration elements. In my project is the profile spring-servlet.xml under the spring folder.

Another function is to read the value of contextconfiglocation and set it to dispatcherservlet through the setcontextconfiglocation () method. The setcontextconfiglocation () method is defined in the frameworkservlet class, that is, the direct parent class of dispatcherservlet inherited from the class diagram above.

We set a breakpoint on the setcontextconfiglocation () method to start the web project. We can see the following debugging results.

The author of the httpservletbean class is Rod Johnson, the father of spring. As a master of pojo's programming philosophy, he used the dependency injection idea in the design of the httpservletbean class to read the configuration elements of <init-param>. The purpose of the class httpservletbean is to read the <init-param> configuration information of the servlet class in the form of dependency injection ", in addition, it is obviously a setter injection.

By understanding the design philosophy of the httpservletbean class, we also know how to benefit from it. Specifically, we inherit the httpservletbean class (just like what dispatcherservlet does) and define an attribute in the class. After adding the setter method to this attribute, we can define a value for the <init-param> element. After the class is initialized, the value will be injected. We can use it directly to avoid the use of the getinitparameter () method on the sample board, in addition, you can also enjoy the spring resource editor function for free on the web. in XML, you can use "classpath:" To directly specify the resource files under the class path.

Note: Although springmvc uses strings to declare and set the contextconfiglocation parameter for the convenience of subsequent context initialization, it can also be obtained successfully if it is declared as a resource type. Readers are encouraged to inherit httpservletbean to write a test servlet class and set a parameter for debugging. This will help you better understand the process of obtaining configuration parameters.

2. Create container Context

As mentioned in the previous article, springmvc uses spring containers to accommodate its own configuration elements and has its own bean container context. During spring MVC initialization, a key step is to establish the container context, which occurs in the frameworkservlet class by Init () the initservletbean () method in the method is triggered.

@Overrideprotected final void initServletBean() throws ServletException {getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");if (this.logger.isInfoEnabled()) {this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");}long startTime = System.currentTimeMillis();try {this.webApplicationContext = initWebApplicationContext();initFrameworkServlet();}catch (ServletException ex) {this.logger.error("Context initialization failed", ex);throw ex;}catch (RuntimeException ex) {this.logger.error("Context initialization failed", ex);throw ex;}if (this.logger.isInfoEnabled()) {long elapsedTime = System.currentTimeMillis() - startTime;this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +elapsedTime + " ms");}}

The initframeworkservlet () method is an empty method without any implementation. Apart from some sample code, the initservletbean () method is very clear:

this.webApplicationContext = initWebApplicationContext();

This simple and straightforward Code breaks through the frameworkservlet class. In springmvc class system, it is
Used to extract the webapplicationcontext
Context.

The initwebapplicationcontext () method encapsulates the entire process of establishing the spring container context. The logic in the method is as follows:

  1. Obtains the root context initialized by contextloaderlistener and registered in servletcontext, and is recorded as rootcontext.
  2. If webapplicationcontext is no longer blank, it indicates that this servlet class is registered to the container programmatically (servletcontext. addservlet () in servlet 3.0 +), and the context is also passed in programmatically. If the input context has not been initialized, set the rootcontext context to its parent context and initialize it. Otherwise, use it directly.
  3. Determine whether the context settings have been completed in step 1 by referencing the WAC variable null (that is, whether the context has been passed in programmatically). If WAC = NULL is true, this servlet is not registered to the container by programming. In this case, the value of the contextattribute is used as the key to search for the context in the servletcontext. The context has been initialized and registered under the contextattribute in other ways and used directly.
  4. Check whether the reference of the WAC variable is null. If WAC = NULL is true, the context initialization policy in step 2 and Step 3 fails. At this time, createwebapplicationcontext (rootcontext) is called ), create a new context that uses rootcontext as the parent context and serves as the container context of spring MVC configuration elements. In most cases, the context we use is the new context.
  5. All the above three context initialization policies call back the onrefresh (applicationcontext context) method (the callback method varies with different policies). The onrefresh method is overwritten in the dispatcherservlet class, based on the context obtained above, the default implementation class in spring MVC is initialized.
  6. Finally, publish the context to servletcontext, that is, set the context to a property of servletcontext by using a value related to the servlet Class Registration Name in Web. xml. You can change the value of publishcontext to determine whether to publish to servletcontext. The default value is true.

Tracking the code in the frameworkservlet class at the above six points can clearly understand the creation process of the entire container context and understand the design purpose of the frameworkservlet class, it is used to establish a spring container context associated with the servlet and register it in the servletcontext. Jumping away from springmvc, we can also inherit the frameworkservlet class to get the benefits of integrating with spring containers. frameworkservlet is a class that can be used independently like httpservletbean. Throughout the springmvc design, the principle of opening and closing is everywhere, which is clearly one of them here.

3. initialize spring MVC default implementation class

The initialization process is transferred in the frameworkservlet class. After the context is established, the callback of the onrefresh (applicationcontext context) method is used to enter the dispatcherservlet class.

@Overrideprotected void onRefresh(ApplicationContext context) {initStrategies(context);}

The dispatcherservlet class overwrites the onrefresh (applicationcontext context) method in the parent class frameworkservlet and provides initialization of various programming elements of springmvc. Of course, these programming elements exist as beans in the container context. The specific initialization policy is encapsulated in the initstrategies () method.

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

Take the inithandlermappings (context) method as an example to analyze the initialization policies of these springmvc programming elements. Other methods are initialized using similar policies.

private void initHandlerMappings(ApplicationContext context) {this.handlerMappings = null;if (this.detectAllHandlerMappings) {// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.Map<String, HandlerMapping> matchingBeans =BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);if (!matchingBeans.isEmpty()) {this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values());// We keep HandlerMappings in sorted order.OrderComparator.sort(this.handlerMappings);}}else {try {HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);this.handlerMappings = Collections.singletonList(hm);}catch (NoSuchBeanDefinitionException ex) {// Ignore, we'll add a default HandlerMapping later.}}// Ensure we have at least one HandlerMapping, by registering// a default HandlerMapping if no other mappings are found.if (this.handlerMappings == null) {this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);if (logger.isDebugEnabled()) {logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");}}}

The default value of the detectallhandlermappings variable is true. When the default implementation class of the handlermapping interface is initialized, all the beans of the handlermapping type in the context are registered in the list variable handlermappings. If you set it to false manually, you will try to get the bean named handlermapping, create a list with only one element, and assign it to handlermappings. If the handlermappings variable is still empty after the above process, it means that you have not provided your own bean definition of the handlermapping type in the context. Springmvc uses the default initialization policy to initialize handlermappings.

Click getdefastrategstrategies.

@SuppressWarnings("unchecked")protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {String key = strategyInterface.getName();String value = defaultStrategies.getProperty(key);if (value != null) {String[] classNames = StringUtils.commaDelimitedListToStringArray(value);List<T> strategies = new ArrayList<T>(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("Error loading DispatcherServlet's default strategy class [" + className +"] for interface [" + key + "]: problem with class file or dependent class", err);}}return strategies;}else {return new LinkedList<T>();}}

It is a model method that undertakes the default initialization policies for all springmvc programming elements. The content of the method is straightforward, that is, to get the implementation class from the defaultstrategies properties variable by passing the class name as the key, and then reflect the initialization.

It should be noted that the strategstrategies variable is initialized and loaded in the static initialization code block of dispatcherservlet.

private static final Properties defaultStrategies;static {// Load default strategy implementations from properties file.// This is currently strictly internal and not meant to be customized// by application developers.try {ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);}catch (IOException ex) {throw new IllegalStateException("Could not load 'DispatcherServlet.properties': " + ex.getMessage());}}
private static final String DEFAULT_STRATEGIES_PATH = "DispatcherServlet.properties";

In this dispatcherservlet. properties, the key-value pair, recorded the springmvc default implementation class, it in the jar package of the spring-webmvc-3.1.3.RELEASE.jar, In the org. springframework. Web. servlet package.

# Default implementation classes for DispatcherServlet's strategy interfaces.# Used as fallback when no matching beans are found in the DispatcherServlet context.# Not meant to be customized by application developers.org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolverorg.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolverorg.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMappingorg.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapterorg.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver,\org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolverorg.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslatororg.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolverorg.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager

So far, we have analyzed the execution process of the inithandlermappings (context) method. Other initialization processes are very similar to this method. After all the initialization methods are executed, springmvc officially completes initialization and waits for the arrival of Web requests.

4. Summary

Looking back at the entire springmvc initialization process, we can see that through httpservletbean, frameworkservlet, dispatcherservlet three different class levels, springmvc designers abstract three different responsibilities, using the template method, the design pattern is fixed in three levels. Httpservletbean completes the dependency injection of the <init-param> configuration element, frameworkservlet completes the establishment of the container context, and dispatcherservlet completes the initialization policy of the specific programming elements of springmvc.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.