java完结--Springmvc源码解析--------0

-------------------------------------------------------------------------------------------------------------------------------------------------------------

使用maven快速的搭建一个web项目。

引入springmvc或者springweb。

 

这里出现了。

注意包的区别。

测试,这个启动的时候就创建一个单例的bean了:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="date" class="java.util.Date"></bean>
</beans>
package com.liban.demo1;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringTest {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("application.xml");
        Object date = ctx.getBean("date");
        System.out.println(date);
    }
}

 -----测试1----

package com.liban.demo2;

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

public class BeanController implements Controller {

    @Override
    public ModelAndView handleRequest(javax.servlet.http.HttpServletRequest httpServletRequest, javax.servlet.http.HttpServletResponse httpServletResponse) throws Exception {
        System.out.println("BeanController run.......");
        return null;
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="date" class="java.util.Date"></bean>

    <bean name="/test1" class="com.liban.demo2.BeanController"></bean>
</beans>

单单是这样只是放在容器中的并不能生效的。

配置tomcat的插件,也可以不配置:

<plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>2.1</version>
          <configuration>
           <port>8080</port>
           <path>/</path>
           <uriEncoding>UTF-8</uriEncoding>
           <server>tomcat7</server>
          </configuration>
        </plugin>

------------------------------------------------------------------------------------------------------------------------------------------------------------------------

总结:用户请求,控制器DispatcherServler获取Handler,handlerAdapter执行handler,返回ModelAndView。

框架的图:

可以看出来其实DispatcherServlet就是一个servlet。

看下UML图:https://blog.csdn.net/shifters/article/details/7498287

所以我们要在web.xml指定下前端的控制器,注意servlet的话都是在web.xml进行设置的。

知识点:注意servlet要在哪里配置,比如控制器,过滤器,拦截器,要统一在web.xml配置,直接和容器说话。

 <servlet>
      <servlet-name>dispatcherServlet</servlet-name>
      <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:application.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>dispatcherServlet</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>

查看DispatcherServlet的父类。

任意地址都会触发DispatchServlet去找xml配置文件然后启动前端控制器,可以访问了:http://localhost:8080/spring/test1

 

----测试2------ 

DispatchServlet是集成自FrameworkServlet的。 

看源码:

	@Override
	protected void service(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
		if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
			processRequest(request, response);//这个方法
		}
		else {
			super.service(request, response);
		}
	}

这个方法。

还是这个类:

@Override
	protected final void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		processRequest(request, response);//这个方法
	}

	/**
	 * Delegate POST requests to {@link #processRequest}.
	 * @see #doService
	 */
	@Override
	protected final void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		processRequest(request, response);
	}

这两个类都有一个共同得方法:processRequest

进入

processRequest(request, response);

找到关键的方法:

try {
			doService(request, response);
		}

@Override
	protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
		if (logger.isDebugEnabled()) {
			String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";
			logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed +
					" processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");
		}

		// Keep a snapshot of the request attributes in case of an include,
		// to be able to restore the original attributes after the include.
		Map<String, Object> attributesSnapshot = null;
		if (WebUtils.isIncludeRequest(request)) {
			attributesSnapshot = new HashMap<>();
			Enumeration<?> attrNames = request.getAttributeNames();
			while (attrNames.hasMoreElements()) {
				String attrName = (String) attrNames.nextElement();
				if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
					attributesSnapshot.put(attrName, request.getAttribute(attrName));
				}
			}
		}

		// Make framework objects available to handlers and view objects.
		request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
		request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
		request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
		request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());

		if (this.flashMapManager != null) {
			FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
			if (inputFlashMap != null) {
				request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
			}
			request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
			request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
		}

		try {
			doDispatch(request, response);//关键代码
		}
		finally {
			if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
				// Restore the original attribute snapshot, in case of an include.
				if (attributesSnapshot != null) {
					restoreAttributesAfterInclude(request, attributesSnapshot);
				}
			}
		}
	}

关键代码,这个是核心的方法。

这个是这篇博客最核心的方法:

doDispatch(request, response);//关键代码
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 {
				//1.处理二进制的判断是不是二进制的请求,是二进制就处理文件上传相关的
                processedRequest = checkMultipart(request);
				multipartRequestParsed = (processedRequest != request);

				// Determine handler for the current request.
                //2.流程图的获取请求的handler
				mappedHandler = getHandler(processedRequest);
				if (mappedHandler == null) {
					noHandlerFound(processedRequest, response);
					return;
				}

				// Determine handler adapter for the current request.
                //3.这个也是核心的方法
				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;
				}

				// Actually invoke the handler.
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

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

				applyDefaultViewName(processedRequest, mv);
				mappedHandler.applyPostHandle(processedRequest, response, mv);
			}
			catch (Exception ex) {
				dispatchException = ex;
			}
			catch (Throwable err) {
				// As of 4.3, we're processing Errors thrown from handler methods as well,
				// making them available for @ExceptionHandler methods and other scenarios.
				dispatchException = new NestedServletException("Handler dispatch failed", err);
			}
			processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
		}
		catch (Exception ex) {
			triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
		}
		catch (Throwable err) {
			triggerAfterCompletion(processedRequest, response, mappedHandler,
					new NestedServletException("Handler processing failed", err));
		}
		finally {
			if (asyncManager.isConcurrentHandlingStarted()) {
				// Instead of postHandle and afterCompletion
				if (mappedHandler != null) {
					mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
				}
			}
			else {
				// Clean up any resources used by a multipart request.
				if (multipartRequestParsed) {
					cleanupMultipart(processedRequest);
				}
			}
		}
	}

注意上面的注解2进入下面的方法,这个hanlerMappings是已经在容器中的了。

@Nullable
	protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		if (this.handlerMappings != null) {
			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;//虽然是for循环但是只要拿到了就return
				}
			}
		}
		return null;
	}

返回的是执行链。

拿到这个handlerMappings是一个集合,hm找到指定的控制器封装为控制器:

    看到这个是我们自己想要的控制器这个控制器,此时必须匹配我们的路径/test1,request里面带的路径的,和一个拦截器,这个拦截器是系统定义好的也可以是我们定义的。 

1.发送请求

2.获取请求得handler

3.返回handler

往下走:

进入getHandlerAdapter,这个将上次返回的调用链传进去。

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
		if (this.handlerAdapters != null) {
			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 +
				"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
	}

看下这三个适配器,针对不同得hadler使用不同得adapter。

看这个配置文件。

这里面定义了两个了:

可以看到handlerMapper和handlerMapper是已经定义好了。

这个就是为什么一上来就能加载三个得原因。

拿到handler的目的就是想拿到我们的控制器,比如我们定义的/test就拿到了我们自己定义的控制器,在哪里,就在handler链里面。

控制器在创建的时候有三种方式:

继承Controller  也可以使用注解的方式  在web.xml配置的。

下一步就是,返回一个ModelAndView给DispatchServlet。

当前的mapperHandler去匹配。

// Actually invoke the handler.
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

之后就是如下几步。

这个就是在xml定义的controller的流程。

----------------------------------------------------------------------------------------------------------------------------------------------

再次新建一个类,加controller注解:

package com.liban.demo2;

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

@Controller
public class AnotationController {

    @RequestMapping("/test2")
    public ModelAndView handleRequest(javax.servlet.http.HttpServletRequest httpServletRequest, javax.servlet.http.HttpServletResponse httpServletResponse) throws Exception {
        System.out.println("BeanController run.......");
        return null;
    }
}

此时不用在容器配置Bean了,不配置bean你就得配置扫包,如何能识别呢?要在application.xml这样配置:

开启的是mvc扫包。

扫包的代码:

  <context:component-scan base-package="com.liban.demo2"></context:component-scan>

  <mvc:annotation-driven></mvc:annotation-driven>

修改下:

这里可以看到:

-----------------------------------------------------------------------------

开始调试test1:

1.进入核心的方法doDispatch

走到这个方法。 看下注解,通过当前的请求去拿handler。  进入gethandler方法。

看到handlerMappinig不为空。

在配置文件加载的。

调试下拿到的是BeanNameUrl。。。。。。控制器的调用链。

就是控制器。

---

接下来进入第二个非常重要的方法,这个方法,去取适配器。

三个适配器也是在配置文件里面读取的。

拿到匹配的返回适配器。

适配器拿到就说明他知道怎么执行我们之前拿到的控制器了。

往下走到这个方法,这个是执行拦截器的方法是十分重要的。

这个方法:这个就是在handler执行之前执行一堆拦截器的方法。

boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
		HandlerInterceptor[] interceptors = getInterceptors();
		if (!ObjectUtils.isEmpty(interceptors)) {
			for (int i = 0; i < interceptors.length; i++) {
				HandlerInterceptor interceptor = interceptors[i];
				if (!interceptor.preHandle(request, response, this.handler)) {
					triggerAfterCompletion(request, response, null);
					return false;
				}
				this.interceptorIndex = i;
			}
		}
		return true;
	}

接下来:

返回model and view

进入这个方法,注意是simpleControll。。。。。。实现的

注意这里的强转,所以要求test1实现Controller的。

-------------------------------------------------------------------------------------

接下来测试test2,我们重点去看这两个核心的方法:

进去getHandler方法:

注意这次匹配的是什么?

--

注意这个HandlerMapping,集合存放两个,哪个匹配就返回。

两个:一个匹配xml的控制器,一个匹配注解的控制器。

拿到这个handler其实就是拿到了我们自己写的控制器,把控制器和过滤器一起打包放在执行chain里面。

再往下走:

之前用的是simple适配器;

这次用的是Request。

可以看到这个已经拿到了。

封装了我们的控制器和拦截器。

在执行之前先处理下拦截器。

进去:

进入这里:

这个方法和之前的有什么不一样呢?在里面没有强转。

这个就是看到底打了什么注解。

之前的没打注解@Controller是这样的:

@Override
	@Nullable
	public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {

		return ((Controller) handler).handleRequest(request, response);
	}

--------------------------------------------

注意是找到ModelAndView制定的视图。

第三步就是对应流程图的handlerAdapter。

---------------------------------------------

源码位置:luban/mvc

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值