SpringMVC源码解析(上)

前言:

    做过web开发的小伙伴都应该使用过SpringMVC(或者Struts1/Struts2)等框架来处理前端请求,并响应对应的页面;

    也有些特别老的项目,直接使用Servlet来处理web请求。

 

    当然,无论使用哪种技术来处理Web请求,底层都是Servlet

    Servlet应用发布在Web容器(如Tomcat)中,Web容器对请求参数进行封装,对请求路径进行解析,然后将请求转发到对应的Servlet中,调用Servlet的doGet()或doPost()方法来进行业务处理,处理完成后,返回对应的页面。整个请求结束。

    Servlet与Web容器的关系,请参考:https://www.ibm.com/developerworks/cn/java/j-lo-servlet/ 

 

1.Servlet的简单使用

    1)创建一个Web应用,名为servletDemo,笔者使用JDK版本为1.8.0.133,Tomcat版本为8.5

    2)创建Servlet

@WebServlet("/HelloForm")
public class HelloForm extends HttpServlet {
    private static final long serialVersionUID = 1L;
       
   
    public HelloForm() {
        super();
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 直接跳转到WEB-INF/res.html页面
        request.getRequestDispatcher("/res.html").forward(request, response);
    }
    
    // 处理 POST 方法请求的方法
    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
}

    3)在WebContent下创建res.html





res


this is res html

    4)在web.xml中添加servlet映射



	
		HelloForm
		servletdemo.HelloForm
	
	
		HelloForm
		/TomcatTest/HelloForm
	
  

    5)测试

    笔者tomcat端口号设置为8081,将servletDemo项目加入到Tomcat中,启动Tomcat,访问路径

http://localhost:8081/servletdemo/TomcatTest/HelloForm

    则可以看到

    说明请求成功。

 

    总结:以上展示了最简单的Servlet使用方式,接收到用户请求后,直接跳转到HTML页面,最关键的代码处理即

request.getRequestDispatcher("/res.html").forward(request, response);

    获得RequestDispatcher然后调用其forward()方法即可。

    读者要记得这句处理,后面关于SpringMVC源码分析的时候,实际最核心的处理也就是在这里。

 

2.SpringMVC简单使用

    1)创建maven-web应用springweb

    2)在src/main/webapp/WEB-INF/web.xml添加以下内容




	springweb

	
		springweb
		org.springframework.web.servlet.DispatcherServlet
		
			contextConfigLocation
			/WEB-INF/springweb-servlet.xml
		
		
	

	
		springweb
		/
	

    主要是配置servlet和servlet-mapping,拦截所有的请求(/)统一交由DispatcherServlet来处理,并配置参数contextConfigLocation,指明xml路径

 

    3)在与web.xml同目录下 创建springweb-servlet.xml



	
	
	
	
	
	
	
	
	
		
		
	

    主要是配置视图解析器

 

    4)创建Controller

package test.SpringMVC;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("/mvc")
public class MvcController {

    @RequestMapping("/hello")
    public String hello(){        
        return "hello";
    }
}

    5)在WEB-INF/jsp下创建hello.jsp



Hello World!

    6)启动springweb项目并测试

    访问 http://localhost:8081/springweb/mvc/hello ,可以看到Hello World!出现,测试成功 

 

    总结:可以看到SpringMVC的配置使用与Servlet差不多,只是SpringMVC将所有的请求都交由DispatcherServlet来处理

    以下进行源码分析的时候,我们重点分析这个DispatcherServlet类就可以了

 

3.DispatcherServlet源码结构分析

    先看下DispatcherServlet的类结构分析

SpringMVC源码解析(上)_第1张图片

   可知,DispatcherServlet实现了HttpServlet,那也就有对应的init()、doGet()等方法,init()用来初始化资源,doGet()用来处理请求

 

    1)DispatcherServlet.init()初始化资源(默认实现在HttpServletBean类中)

	@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, 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;
		}

		// 重点在这里,子类负责实现,主要来初始化ApplicationContext及一些Resolve
		initServletBean();

		if (logger.isDebugEnabled()) {
			logger.debug("Servlet '" + getServletName() + "' configured successfully");
		}
	}

    2)DispatcherServlet.doGet()

    一路跟踪到doDispatch()方法,这里是真正的业务实现方法

	protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
		HttpServletRequest processedRequest = request;
		HandlerExecutionChain mappedHandler = null;
		boolean multipartRequestParsed = false;

		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

		try {
			ModelAndView mv = null;
			Exception dispatchException = null;

			try {
				processedRequest = checkMultipart(request);
				multipartRequestParsed = (processedRequest != request);

				// 1.获取handler(主要包含request相对应的Controller处理类和拦截器列表)
				mappedHandler = getHandler(processedRequest);
				if (mappedHandler == null || mappedHandler.getHandler() == null) {
					noHandlerFound(processedRequest, response);
					return;
				}

				// 2.获取HandlerAdapter
				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

				// Process last-modified header, if supported by the handler.
				String method = request.getMethod();
				boolean isGet = "GET".equals(method);
				if (isGet || "HEAD".equals(method)) {
					long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
					if (logger.isDebugEnabled()) {
						logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
					}
					if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
						return;
					}
				}

				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					return;
				}

				// 3.根据获取到的HandlerAdapter,调用其handler方法,
                // handler方法会先执行拦截器的方法,然后执行Controller的对应方法获取对应的ModelAndView,也就是对应的视图
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

				if (asyncManager.isConcurrentHandlingStarted()) {
					return;
				}

				applyDefaultViewName(processedRequest, mv);
				mappedHandler.applyPostHandle(processedRequest, response, mv);
			}
			...
            // 4.跳转到指定页面(跳转到指定的视图)
			processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
		}
		...
	}

    总结:

    由以上分析可知,初始化资源的任务放在init()方法中;

    处理请求时,主要步骤可分为:

    * 获取请求对应的HandlerExecutionChain(主要包含request相对应的Controller处理类和拦截器列表)

    * 获取HandlerAdapter

    * HandlerAdapter.handler()方法会调用拦截器的方法和Controller的方法处理,最终返回一个ModelAndView(主要就是视图路径名称)

    * 跳转到指定视图界面

 

    对框架结构的简单分析之后,会发现与最原始的Servlet的使用差不多,只不过包装的过程更复杂点而已。

    下文笔者会对请求过程进行详细分析,请关注 SpringMVC源码解析(下)。

 

参考:Spring源码深度解析(郝佳)

你可能感兴趣的:(Spring)