Springmvc source code analysis (1)-starting from abstraction and interfaces

Source: Internet
Author: User
Tags addall

Springmvc is becoming more and more popular as a presentation layer framework after struts2. I believe developers of javaee should have heard of springmvc even if they have never used it. By analyzing springmvc's design ideas and source code implementation, I try to unveil springmvc's mysterious veil one by one from two aspects: abstract design and implementation, the code in this article is based on Spring's 3.1.3release version.

Any framework has its own specific application fields. The design and implementation of the framework must be designed to cope with many common, cumbersome, and basic work in this field. Springmvc, as a presentation layer framework, must also face several major issues in the Web development field and give your own answers:

  • URL-to-framework ing.
  • HTTP Request Parameter binding
  • HTTP Response generation and Output

These three topics constitute a complete Web request process, and each part has a very broad extension. What is the answer of springmvc framework to these questions?

To learn a framework, first of all, we must first understand its design philosophy. This framework is abstracted and global. The core interfaces defined by this framework are the most useful references. The core interface defines the framework skeleton and expresses the Framework Design Idea in the most abstract sense.

Next I will take a Web request process as the carrier to introduce springmvc's core interfaces and classes in turn.

In the browser, enter the address of http://www.xxxx.com/aaa/bbb.ccc, and the browser initiates an httprequest. After the request arrives at your server, it will first be registered on the Web by springmvc. the front-end forwarder in XML receives the dispatcherservlet, which is a standard servlet that accepts and forwards Web requests to internal framework processing units.

The first core interface that appears in front of you is the handlermapping interface defined in the org. springframework. Web. servlet package:

package org.springframework.web.servlet;import javax.servlet.http.HttpServletRequest;public interface HandlerMapping {String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE = HandlerMapping.class.getName() + ".pathWithinHandlerMapping";String BEST_MATCHING_PATTERN_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingPattern";String INTROSPECT_TYPE_LEVEL_MAPPING = HandlerMapping.class.getName() + ".introspectTypeLevelMapping";String URI_TEMPLATE_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".uriTemplateVariables";String PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE = HandlerMapping.class.getName() + ".producibleMediaTypes";HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;}

For ease of reading, I removed the comments from the source code, but I strongly recommend that you remember to read them, in this way, you can get the most accurate description of the class or interface design from the Framework designer's mouth. Several constants defined in the class, so let's leave it alone. The key lies in the unique method in this interface:

HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;

This method is easy to understand for a Java beginner: it only has one parameter of the type httpservletrequest. The declaration of throws exception indicates that it does not handle any type of exceptions, handlerexecutionchain is its return type.

Return to the processing process of dispatcherservlet. After dispatcherservlet receives a Web request, the standard servlet class processing method doget or dopost. After several forwarding times, A list (a bit writable) consisting of handlermapping implementation classes registered in the dispatcherservlet class will be traversed in a loop. The gethandler method is called Based on the httpservletrequest object of the Web request. The first non-null call result is returned. The Traversal method in the dispatcherservlet class is not long. paste it to give you a more intuitive understanding.

/** * Return the HandlerExecutionChain for this request. * <p>Tries all handler mappings in order. * @param request current HTTP request * @return the HandlerExecutionChain, or <code>null</code> if no handler could be found */protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {for (HandlerMapping hm : this.handlerMappings) {if (logger.isTraceEnabled()) {logger.trace("Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");}HandlerExecutionChain handler = hm.getHandler(request);if (handler != null) {return handler;}}return null;}

Yes, the first step is simply done. After a Web request is processed, it will get a handlerexecutionchain object, which is springmvc's answer to URL ing. It should be noted that the gethandler method parameter of the handlermapping interface is httpservletrequest, which means that the implementation class of handlermapping can use
All information is used to generate the handlerexecutionchain object. This includes the request header, URL path, Cookie, session, parameters, and so on. Everything you can get from a Web Request (the most commonly used URL path ).

The first extension point of spirngmvc appears here. We can write any handlermapping implementation class to determine the generation of a Web request to the handlerexecutionchain object based on any policy. It can be said that, starting with the declaration of the first core interface, springmvc exposes its flexibility and ambition: What brother plays is "open-closed".

Handlerexecutionchain is the next core class to be understood. From the name, we can intuitively see that this object is an execution chain encapsulation. Anyone familiar with struts2 knows that action objects are encapsulated by layer-by-layer interceptors. Here we can make an analogy to show that springmvc does absorb part of struts2's design ideas.

The code of the handlerexecutionchain class is not long. It is defined in the org. springframework. Web. servlet package. For more intuitive understanding, first go to the code.

package org.springframework.web.servlet;import java.util.ArrayList;import java.util.Arrays;import java.util.List;import org.springframework.util.CollectionUtils;public class HandlerExecutionChain {private final Object handler;private HandlerInterceptor[] interceptors;private List<HandlerInterceptor> interceptorList;public HandlerExecutionChain(Object handler) {this(handler, null);}public HandlerExecutionChain(Object handler, HandlerInterceptor[] interceptors) {if (handler instanceof HandlerExecutionChain) {HandlerExecutionChain originalChain = (HandlerExecutionChain) handler;this.handler = originalChain.getHandler();this.interceptorList = new ArrayList<HandlerInterceptor>();CollectionUtils.mergeArrayIntoCollection(originalChain.getInterceptors(), this.interceptorList);CollectionUtils.mergeArrayIntoCollection(interceptors, this.interceptorList);}else {this.handler = handler;this.interceptors = interceptors;}}public Object getHandler() {return this.handler;}public void addInterceptor(HandlerInterceptor interceptor) {initInterceptorList();this.interceptorList.add(interceptor);}public void addInterceptors(HandlerInterceptor[] interceptors) {if (interceptors != null) {initInterceptorList();this.interceptorList.addAll(Arrays.asList(interceptors));}}private void initInterceptorList() {if (this.interceptorList == null) {this.interceptorList = new ArrayList<HandlerInterceptor>();}if (this.interceptors != null) {this.interceptorList.addAll(Arrays.asList(this.interceptors));this.interceptors = null;}}public HandlerInterceptor[] getInterceptors() {if (this.interceptors == null && this.interceptorList != null) {this.interceptors = this.interceptorList.toArray(new HandlerInterceptor[this.interceptorList.size()]);}return this.interceptors;}@Overridepublic String toString() {if (this.handler == null) {return "HandlerExecutionChain with no handler";}StringBuilder sb = new StringBuilder();sb.append("HandlerExecutionChain with handler [").append(this.handler).append("]");if (!CollectionUtils.isEmpty(this.interceptorList)) {sb.append(" and ").append(this.interceptorList.size()).append(" interceptor");if (this.interceptorList.size() > 1) {sb.append("s");}}return sb.toString();}}

I believe you haven't read it all, and you don't have to read it all. In fact, only two lines are required.

private final Object handler;private HandlerInterceptor[] interceptors;

Not surprisingly, there are a bunch of interceptors for a real execution object. This is not the implementation in struts2. springmvc does not avoid it, but it uses this encapsulation. After the execution chain handlerexecutionchain is obtained, the next step will be centered on it.

Handlerinterceptor is also the core interface of springmvc, which is defined as follows:

package org.springframework.web.servlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public interface HandlerInterceptor {boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)    throws Exception;void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)throws Exception;void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)throws Exception;}

At this point, the entire execution context of handlerexecutionchain is clear: Before calling its handler object, the array composed of the implementation classes of the handlerinterceptor interface will be traversed, and its prehandle method will be called in sequence, then the real handler object will be called.

After the handler object is called, the required response data is generated. Before the processing result is written to the httpservletresponse object (springmvc is called a rendering view), its posthandle method is called in sequence. After the view rendering is complete, the final aftercompletion method will be called in sequence, and the entire web request processing process will end.

Before a processing object is executed, the interceptor is used to write articles, which has become a classic Framework Design routine. The interceptor in struts2 performs complex work such as parameter binding. What does springmvc interceptor do? We don't care about it for the moment. Although this is a very important detail, the details are details. Let's first understand more important things.

Handlerinterceptor is the exposure of springmvc's second extension point, before a request is actually processed, the request is processed, but the request has not been output to the response, and the request has been output to the response.. The success of the struts2 framework stems from the design of the Interceptor. springmvc incorporates this design idea and introduces new ideas. It rationally divides three different time points, therefore, the process for processing Web requests provides greater scalability.

In the handlerexecutionchain class, what is the handler object declared by object reference? How is it called?

Before answering these questions, let's look at another core interface in springmvc, handleradapter:

package org.springframework.web.servlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public interface HandlerAdapter {boolean supports(Object handler); ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;long getLastModified(HttpServletRequest request, Object handler);}

In dispatcherservlet, in addition to the list of handlermapping implementation classes, a list composed of handleradapter implementation classes is also registered, with code as evidence.

/** List of HandlerMappings used by this servlet */private List<HandlerMapping> handlerMappings;/** List of HandlerAdapters used by this servlet */private List<HandlerAdapter> handlerAdapters;

Next, we will use another piece of code in the dispatcherservlet class to answer the above questions:

/** * Return the HandlerAdapter for this handler object. * @param handler the handler object to find an adapter for * @throws ServletException if no HandlerAdapter can be found for the handler. This is a fatal error. */protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {for (HandlerAdapter ha : this.handlerAdapters) {if (logger.isTraceEnabled()) {logger.trace("Testing handler adapter [" + ha + "]");}if (ha.supports(handler)) {return ha;}}throw new ServletException("No adapter for handler [" + handler +"]: Does your handler implement a supported interface like Controller?");}

This code is already quite clear. The Handler object in the handlerexecutionchain will be passed as a parameter, and the list of handleradapter implementation classes registered in the dispatcherservlet class will be traversed, then return the first Supports Method to return the true handleradapter object, use the handleradapter to implement the handle method in the class to process the handler object, and return the modelandview object that contains the view and data. Handleradapter is the third extension provided by springmvc. You can provide your own implementation class to process handler objects.

The code of the modelandview object is no longer pasted. It is an aggregation class for views and data in springmvc. The view is abstracted by the view of springmvc's last core interface:

package org.springframework.web.servlet;import java.util.Map;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public interface View {String RESPONSE_STATUS_ATTRIBUTE = View.class.getName() + ".responseStatus";String PATH_VARIABLES = View.class.getName() + ".pathVariables";String getContentType();void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception;}

All the data is finally transmitted as a map object to the render method in the view implementation class. Calling this render method completes the rendering of the view to the response. The view implementation class is the result returned from the handle method in the handleradapter. Of course, there is a parsing process from modelandview to the real view implementation class. modelandview can have a real view object or just a view name, springmvc resolves the view name to a real view object.

So far, we have learned about the processing process of a typical complete Web request in springmvc and the core classes and interfaces involved in it.

In a typical springmvc call, handlerexecutionchain encapsulates the handler object as an instance of the class identified by @ controller annotation. According to the @ requestmapping annotation at the class level and method level, the handlerexecutionchain object is generated by the default registered handler (updated to the requestmappinghandlermapping class in 3.1.3, but can also be used for backward compatibility), and then updated to the requestmappinghandleradapter class in annotationmethodhandleradapter (3.1.3, but for backward compatibility, annotationmethodhandleradapter can also be used) to execute this handlerexecutionchain object After the final modelandview object is generated, the view is rendered by the render method of the specific view object.

As you can see, as a presentation layer framework, springmvc is not as radical as struts2 and does not adopt the design idea of completely decoupled from Web containers. Instead, it relies on the native servlet framework object, through reasonable abstraction, a rigorous processing process is developed. The result is that the execution efficiency is higher than that of struts2, and the flexibility is also increased.

 
Http://my.oschina.net/lichhao/blog/99039

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.