SpringMVC source code analysis 3: Initialization and forwarding the request DispatcherServlet

When we first learn Servlet programming, learning java web, but also not so much framework. We have developed a simple function to do is simply to inherit HttpServlet, if necessary rewrite doGet, doPost method, we jump to the defined jsp page. Servlet class After you have finished registering the Servlet class in the web.xml.

In addition, no other of. We start the web server, enter the address in the browser, you can see the output of our written page on the browser. To better understand this process above, you need to learn about the three stages Servlet life cycle, the so-called "init-service-destroy".

More knowledge, I think for you to understand SpringMVC design ideas, enough. SpringMVC course can be regarded as a complex framework, but at the same time it follows the Servlet world's most simple rules and that is "init-service-destroy". We have to analyze SpringMVC initialization process, in fact, analysis init DispatcherServlet class () method, let's take this simple view, open source code to see what it DispatcherServlet.

Reads the configuration element

Open source DispatcherServlet class with the Eclipse IDE, ctrl + T look.

DispatcherServlet class initialization entry method init () is defined in the parent class HttpServletBean, HttpServletBean class as a direct successor to the class HttpServlet class, override the init () method HttpServlet class implements its own initialization behavior.

@Override
    public 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");
        }
    }

initServletBean here () method is in a class HttpServletBean no empty-implemented method, its purpose is left to the subclass implement its own initialization logic, that is, we often say that the Template Method design pattern. SpringMVC in this vivid use of this mode, init () method is the template method in the Template Method pattern, SpringMVC real initialization process, overwritten by subclasses FrameworkServlet in initServletBean () method is triggered.

Try look at is the init () method, the catch block code package, which involves BeanWrapper, PropertyValues, these internal Spring ResourceEditor very bottom of the class. To go into specific implementation details of the code above, you need to have a fairly in-depth knowledge of the Spring Framework source code. We are here to avoid complicated to simple, from the effect of the code and design ideas above to analyze what the code does in this try, catch blocks:

  • Register a string to the resource file editor, make the following Servlet Shaped configuration element can be used as "classpath:" In this way a given source SpringMVC bean frame profile.
  • In the web.xml Servlet following this DispatcherServlet Configuration element using JavaBean manner (i.e., by the setter method) to read the DispatcherServlet.

These two points, I want to pass below an example to illustrate this point.

I registered in web.xml DispatcherServlet configuration is as follows:

<!-- springMVC配置开始 -->
    <servlet>
        <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>/</url-pattern>
    </servlet-mapping>
    <!-- springMVC配置结束 -->

You can see, I signed up for the named contextConfigLocation Element, a value "classpath: spring / spring-servlet.xml", this is also often used to specify the method SpringMVC profile path. Those above try, catch block code wrapped role of a is to: convert "classpath spring / spring-servlet.xml" this string into a resource file in the classpath, to read the configuration for initializing the frame element. In my project folder in the spring following configuration file spring-servlet.xml.

Another effect is that the value of contextConfigLocation read out, and then () method provided by the DispatcherServlet setContextConfigLocation, this setContextConfigLocation () method is defined in FrameworkServlet class, i.e. the above inheritance hierarchy DispatcherServlet immediate parent class.

We marked the setContextConfigLocation () method above a breakpoint, start a web project, you can see the debugging results below.

Author HttpServletBean class is the father of the famous Spring's Rod Johnson. As a master of POJO programming philosophy, he designed HttpServletBean in this class, the idea of ​​using dependency injection completed Read element configuration. He pulled out HttpServletBean purpose of this class is also here, is the "dependency injection approach to reading Servlet class Configuration information ", but here it is clear that a setter injection.

Understand the design idea HttpServletBean class, we also know how you can benefit from it. Specifically, we inherit HttpServletBean class (like DispatcherServlet do), in the class definition of a property, the property plus setter for this method, we can Elements defined for value. After the class is initialized, the value will be injected in, we can use it directly, avoiding boilerplate getInitParameter () method of use, but also free to enjoy the Spring resource editor functions, you can web.xml, by "classpath:" resources files directly specify the class path.

Note that, although the SpringMVC itself in order to facilitate context initialization later used to declare and set contextConfigLocation string parameter, it will be declared as Resource type, the same can be successfully acquired. Readers are encouraged to write their own inheritance HttpServletBean Servlet class with a test and set a parameter to debug it, this can help you get a better understanding of the configuration parameters of the process.

Create a container context

The article mentioned, SpringMVC Spring container used to hold their own configuration elements, bean container has its own context. In the process of SpringMVC initialization, the key step is to establish the container context, and this context establishment process that takes place in FrameworkServlet class () method is triggered by the init () method initServletBean above.

@Override
    protected 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");
        }
    }

initFrameworkServlet () method is a no-implemented method empty, remove some boilerplate code, then what does this initServletBean () method has been very clear:

this.webApplicationContext = initWebApplicationContext();

This is a straightforward code FrameworkServlet speaks of this class, designed in SpringMVC class System, which is used to establish WebApplicationContext pulled out of the context of the process.

initWebApplicationContext () method, the entire process of building the package container Spring context, logic within follows:

  1. ContextLoaderListener acquired and registered in the initialized by the ServletContext root context, referred to as rootContext
  2. If webApplicationContext has not empty, it indicates that the class is registered Servlet (ServletContext.addServlet Servlet 3.0+ in ()) into the container through programmatic context is also passed by the programming formula. If passed in the context of this has not been initialized, the rootContext context to its parent context, then initialize, or used directly.
  3. Wac whether by reference to a variable is null, step 2 determines whether the setting has been completed context (i.e., whether the incoming context with programmatic manner), if established wac == null, indicating that the programmed by the formula is not a registered Servlet container middle. At this time, the value of the key attribute in ContextAttribute find in the ServletContext context, to find to give, description and context register has been initialized at ContextAttribute, directly otherwise.
  4. Check that wac variable reference to null, if wac == null was established, indicating that context initialization strategy of two steps 2 and 3 without success, this time calling createWebApplicationContext (rootContext), to establish a new parent context of context rootContext as the container element arranged SpringMVC context. In most cases we use the context, it is this new context.
  5. These three initial context policy will callback onRefresh (ApplicationContext context) method (callback methods are different depending on the policy), the method being overwritten OnRefresh DispatcherServlet class, to the context obtained above as the basis, the completion of the default implementation class SpringMVC initialization.
  6. Finally, this will be published in the ServletContext context, that is, the context and the Servlet class with a registered name associated key value in web.xml, set as an attribute of ServletContext. You can decide whether to publish in the ServletContext, the default is true by changing the value of publishContext.

In the above tracking 6:00 FrameworkServlet class code comparison process context can be established clearly understand the entire container, it can be appreciated that FrameworkServlet designed to class, which is used to create a context Spring container and associated Servlet, and register it to the ServletContext. SpringMVC escape open system, we can also inherit FrameworkServlet class, to benefit the Spring container consolidation, FrameworkServlet and HttpServletBean, it is a class that can be used independently. SpringMVC entire design, all reflect the principle of opening and closing, there is apparently also one point.

The default implementation class initialization SpringMVC

In FrameworkServlet class initialization process flow, after the context has been established, through the callback onRefresh (ApplicationContext context) the method proceeds to DispatcherServlet class.

@Override
    protected void onRefresh(ApplicationContext context) {
        initStrategies(context);
    }

DispatcherServlet class overrides the superclass FrameworkServlet onRefresh (ApplicationContext context) method, provides initialization SpringMVC various programming elements. Of course, these program elements, as are one bean container context exists. Specific initialization strategy, encapsulated in 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);
    }

We where initHandlerMappings (context) method, for example, analyze initialize these strategies SpringMVC programming elements, other methods are based on a similar strategy initialized.

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");
            }
        }
    }

detectAllHandlerMappings variable defaults to true, so when the interface is initialized HandlerMapping default implementation class, will HandlerMapping the context of all types of Bean are registered in handlerMappings the List variable. If you manually set it to false, it will attempt to acquire Bean named handlerMapping, and create a new List only one element, assign it to handlerMappings. If, after the above process, handlerMappings variables remain empty, so that you do not provide your own HandlerMapping type of Bean defined in context. At this point, SpringMVC will use the default initialization strategy to initialize handlerMappings.

Point into getDefaultStrategies look.

@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 paradigm of the method, assume all default initialization strategy SpringMVC programming elements. Straightforward method for comparing the contents, is transmitted to the name of the class is a bond, acquired from the implementation class Properties defaultStrategies this variable is then reflected initialization.

Need to explain is to initialize defaultStrategies variables, it is loaded in the static initialization code block DispatcherServlet in.

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";

This DispatcherServlet.properties which, by way of key-value pairs, recorded SpringMVC default implementation class, in this spring-webmvc-3.1.3.RELEASE.jar jar package, the package inside org.springframework.web.servlet.

# 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.AcceptHeaderLocaleResolver

org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver

org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
    org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping

org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
    org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
    org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter

org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver,\
    org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
    org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver

org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator

org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver

org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager

So far, we have finished the analysis process execution initHandlerMappings (context) method, other initialization process and this method is very similar. All initialization method after the implementation, SpringMVC the formal completion of initialization, quietly waiting for the arrival of Web requests.

to sum up

Review the entire initialization process SpringMVC, we see, through HttpServletBean, FrameworkServlet, DispatcherServlet three different class levels, SpringMVC designers Three different functions are abstract design using the template method pattern are fixed in three class hierarchy . Which was completed HttpServletBean Injection dependent configuration elements, FrameworkServlet context establishment is completed container, DispatcherServlet initialization strategy is accomplished SpringMVC specific programming elements.

image description

Guess you like

Origin www.cnblogs.com/xll1025/p/11407916.html