0
点赞
收藏
分享

微信扫一扫

Spring源码分析第四弹 - MVC分析

吓死我了_1799 2022-01-06 阅读 86

到本篇文章spring的IOC DI AOP原理就分析完了!但是仅仅是容器内的的调用。
今天继续来分析下spring MVC框架的源码,看下是怎么实现发送一个请求返回数据或者页面的。

MVC原理分析

预先准备

  • 看过手写的应该都知道,MVC基于tomcat容器,依赖servlet包。这里直接采用springboot,引入web包就好了
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
  • 准备一个Controller
@RequestMapping("/v1")
public class HelloController{

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

	@RequestMapping("/home.html")
	@ResponseBody
	public String home() {
		return "home";
	}
}

MVC handleMapping初始化时序图

  • 入口在IOC容器的回调
    handleMapping初始化时序图

MVC初始化时序图

  • HttpServlet包下init方法
    MVC初始化时序图

MVC调用阶段时序图

MVC调用阶段时序图
本章源码比较开散,请自备好晕车药!

MVC源码分析

1.HandlerMapping 初始化阶段

  • 上篇文章有提到过MVC的入口,找到AbstractAutowireCapableBeanFactory.doCreateBean依赖注入完之后的这行代码
exposedObject = initializeBean(beanName, exposedObject, mbd);

//初始化之后
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
	......

	try {
		//mvc在这里实现 接下来走这里
		invokeInitMethods(beanName, wrappedBean, mbd);
	}
	catch (Throwable ex) {
		throw new BeanCreationException(
				(mbd != null ? mbd.getResourceDescription() : null),
				beanName, "Invocation of init method failed", ex);
	}
	if (mbd == null || !mbd.isSynthetic()) {
		//aop在这里实现
		wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
	}
	return wrappedBean;
}


//初始化完bean之后对实现了InitializingBean的类进行通知
protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd)
		throws Throwable {

	......
    //多余的去掉
    //重点看这里
	if (System.getSecurityManager() != null) {
			try {
				AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {					//发送通知
					((InitializingBean) bean).afterPropertiesSet();
					return null;
				}, getAccessControlContext());
			}
			catch (PrivilegedActionException pae) {
				throw pae.getException();
			}
		}
		else {
               //发送回调通知
			((InitializingBean) bean).afterPropertiesSet();
		}
	}
}
  • 既然是MVC,那么就去MVC包里找实现了InitializingBean接口的类,于是在spring-mvc包下找到 org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping 这个类,因为现在是基于springboot架子分析,为什么到这个类就不多说了,看完springboot自动注入篇就懂了
@Override
public void afterPropertiesSet() {
	//初始化一些配置 RequestMappingInfo.BuilderConfiguration config
	this.config = new RequestMappingInfo.BuilderConfiguration();
	this.config.setTrailingSlashMatch(useTrailingSlashMatch());
	this.config.setContentNegotiationManager(getContentNegotiationManager());

	if (getPatternParser() != null) {
		this.config.setPatternParser(getPatternParser());
		Assert.isTrue(!this.useSuffixPatternMatch && !this.useRegisteredSuffixPatternMatch,
				"Suffix pattern matching not supported with PathPatternParser.");
	}
	else {
		this.config.setSuffixPatternMatch(useSuffixPatternMatch());
		this.config.setRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch());
		this.config.setPathMatcher(getPathMatcher());
	}
	//调用父类的方法
	super.afterPropertiesSet();
}
  • 接下来到org.springframework.web.servlet.handler.AbstractHandlerMethodMapping 这个类
@Override
public void afterPropertiesSet() {
	initHandlerMethods();	//初始化mapping的入口
}

protected void initHandlerMethods() {
	for (String beanName : getCandidateBeanNames()) {
		//beanName 不是已 ‘scopedTarget.’ 开头的
		if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
			//走这里
			processCandidateBean(beanName);
		}
	}
	handlerMethodsInitialized(getHandlerMethods());
}



//是否满足解析的条件
protected void processCandidateBean(String beanName) {
    Class<?> beanType = null;
    try {
    	//通过getType去容器中取出实例对象
        beanType = obtainApplicationContext().getType(beanName);
    }
    catch (Throwable ex) {
        // An unresolvable bean type, probably from a lazy bean - let's ignore it.
        if (logger.isTraceEnabled()) {
            logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
        }
    }
    //取出类型并是handler的才走下面 先看isHandler
    if (beanType != null && isHandler(beanType)) {
        detectHandlerMethods(beanName);
    }
}

//先看下 isHandler 只有加了这两个注解的才解析
protected boolean isHandler(Class<?> beanType) {
	//@Controller || @RequestMapping
	return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
			AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}

//接下来detectHandlerMethods 找到对应关系并注册
protected void detectHandlerMethods(Object handler) {
	//用beanName 通过type获取class
	Class<?> handlerType = (handler instanceof String ?
			obtainApplicationContext().getType((String) handler) : handler.getClass());

	if (handlerType != null) {
		Class<?> userType = ClassUtils.getUserClass(handlerType);
        //获的class方法与路径的对应关系 存储的是class类方法和url请求路径
        //详细Debugger图在下面
		Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
				(MethodIntrospector.MetadataLookup<T>) method -> {
					try {
						//这里面的代码就不贴了 获取注解里的值组成请求路径
						//封装成 RequestMappingInfo
						return getMappingForMethod(method, userType);
					}
					catch (Throwable ex) {
						throw new IllegalStateException("Invalid mapping on handler class [" +
								userType.getName() + "]: " + method, ex);
					}
				});
		if (logger.isTraceEnabled()) {
			logger.trace(formatMappings(userType, methods));
		}
		methods.forEach((method, mapping) -> {
			Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);	
            //遍历map进行注册 注意此时的handler还是beanName
			registerHandlerMethod(handler, invocableMethod, mapping);
		});
	}
}

protected void registerHandlerMethod(Object handler, Method method, T mapping) {
    //内部类 保存在Map<T, MappingRegistration<T>> registry 里面
    this.mappingRegistry.register(mapping, handler, method);
}
  • MethodIntrospector.selectMethods() 断点调试图
    方法对应路径图

  • 接下来来到内部类MappingRegistry.register()

public void register(T mapping, Object handler, Method method) {
	// Assert that the handler method is not a suspending one.
	if (KotlinDetector.isKotlinType(method.getDeclaringClass()) && KotlinDelegate.isSuspend(method)) {
		throw new IllegalStateException("Unsupported suspending handler method detected: " + method);
	}
	//加写锁
	this.readWriteLock.writeLock().lock();
	try {
        //进行封装,跟到底就是new HandlerMethod对象,里面保存类beanName和方法相关
		HandlerMethod handlerMethod = createHandlerMethod(handler, method);
		validateMethodMapping(handlerMethod, mapping);
        //保存到map
		this.mappingLookup.put(mapping, handlerMethod);

		List<String> directUrls = getDirectUrls(mapping);
		for (String url : directUrls) {
            //保存到list 其实是一个map List
            //Map<K, List<V>> targetMap key是请求的url 值是bean 方法 容器的封装
			this.urlLookup.add(url, mapping);
		}

		String name = null;
		if (getNamingStrategy() != null) {
			name = getNamingStrategy().getName(handlerMethod, mapping);
			//加到 name -> list
			addMappingName(name, handlerMethod);
		}

		CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
		if (corsConfig != null) {
			this.corsLookup.put(handlerMethod, corsConfig);
		}
		//存储注册信息
		this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
	}
	finally {
		this.readWriteLock.writeLock().unlock();
	}
}
  • 内部成员变量 mappingRegistry,里面有ReentrantReadWriteLock读写锁,是保证线程安全的
private final MappingRegistry mappingRegistry = new MappingRegistry();
class MappingRegistry {
	//保存注册信息的MAP
	//this.registry.put(mapping,
	//new MappingRegistration<>(mapping, handlerMethod, directPaths, name, corsConfig != null));
	private final Map<T, MappingRegistration<T>> registry = new HashMap<>();
	//this.pathLookup.add(path, mapping); 路径对应mapping
	private final MultiValueMap<String, T> pathLookup = new LinkedMultiValueMap<>();
	//this.nameLookup.put(name, newList); List<HandlerMethod> newList
	//名称 #(方法名中的大写字符) list
	private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>();
	//this.corsLookup.put(handlerMethod, corsConfig);
	//跨域配置Map
	private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<>();
	//读写锁
	private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
}

至此,MVC的AbstractHandlerMethodMapping类已经通过Bean的初始化回调接口InitializingBean#afterPropertiesSet()将所有符合条件的类解析,并将方法和请求路径的对应关系存储到成员变量MappingRegistry中,接下来就开始MVC的初始化了
但是注意此时的handler还是beanName

2.MVC初始化阶段

  • 看过前面手写的MVC版本,入口应不用多说了,这里还是贴下
<servlet>
     <servlet-name>mvc</servlet-name>
     //调用哪个类
     <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
     //init方法 传参
     <init-param>
     	//参数名称
         <param-name>contextConfigLocation</param-name>
         //参数值
         <param-value>application.properties</param-value>
     </init-param>
     <load-on-startup>1</load-on-startup>
</servlet>
  • 接下来就直接找到DispatcherServlet类里找init方法,结果没找到,那就只能去父类了。最终在父类找到GenericServlet#init()
public void init(ServletConfig config) throws ServletException {
    //保存了配置文件
    this.config = config;
    //当前类的init 子类实现
    this.init();
}
  • 接下来到HttpServletBean.init()
public final void init() throws ServletException {

	// Set bean properties from init parameters.
	//把init参数封装一下
	PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
	if (!pvs.isEmpty()) {
		try {
			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;
		}
	}

	// Let subclasses do whatever initialization they like.
	//接下来走这个 又是子类实现的方法
	initServletBean();
}
  • 接下来到FrameworkServlet.initServletBean()
//找到这行
this.webApplicationContext = initWebApplicationContext();

//创建web容器 实际上也是继承了Application 这个容器用来存储一些web的东西
protected WebApplicationContext initWebApplicationContext() {
	WebApplicationContext rootContext =
			WebApplicationContextUtils.getWebApplicationContext(getServletContext());
	WebApplicationContext wac = null;

	if (this.webApplicationContext != null) {
		// A context instance was injected at construction time -> use it
		wac = this.webApplicationContext;
		if (wac instanceof ConfigurableWebApplicationContext) {
			ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
			if (!cwac.isActive()) {
				// The context has not yet been refreshed -> provide services such as
				// setting the parent context, setting the application context id, etc
				if (cwac.getParent() == null) {
					// The context instance was injected without an explicit parent -> set
					// the root application context (if any; may be null) as the parent
					cwac.setParent(rootContext);
				}
                //配置 & 刷新容器 接下来先看这个,创建容器
				configureAndRefreshWebApplicationContext(cwac);
			}
		}
	}
	if (wac == null) {
		wac = findWebApplicationContext();
	}
	if (wac == null) {
		wac = createWebApplicationContext(rootContext);
	}

	if (!this.refreshEventReceived) {
		synchronized (this.onRefreshMonitor) {
            //容器初始完成走这里 分析完 配置 & 刷新容器 等下分析这里
			onRefresh(wac);
		}
	}

	if (this.publishContext) {
		// Publish the context as a servlet context attribute.
		String attrName = getServletContextAttributeName();
		getServletContext().setAttribute(attrName, wac);
	}
	return wac;
}
  • 配置 & 刷新容器configureAndRefreshWebApplicationContext
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
	if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
		// The application context id is still set to its original default value
		// -> assign a more useful id based on available information
		if (this.contextId != null) {
			wac.setId(this.contextId);
		}
		else {
			// Generate default id...
			wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
					ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName());
		}
	}
	//web容器的一些参数
	wac.setServletContext(getServletContext());
	wac.setServletConfig(getServletConfig());
	wac.setNamespace(getNamespace());
	wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));

	// The wac environment's #initPropertySources will be called in any case when the context
	// is refreshed; do it eagerly here to ensure servlet property sources are in place for
	// use in any post-processing or initialization that occurs below prior to #refresh
	//在任何情况下,当上下文要先刷新;
	//在这里这样做的原因,以确保servlet属性源已准备就绪
	//在以下刷新之前发生的任何后处理或初始化中使用
	ConfigurableEnvironment env = wac.getEnvironment();
	if (env instanceof ConfigurableWebEnvironment) {
		((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
	}
	//回调后置处理
	postProcessWebApplicationContext(wac);
	//保存web容器
	applyInitializers(wac);
	//重点 点进去到了 AbstractApplicationContext.refresh
	wac.refresh();
}
  • wac.refresh() 接下来到了AbstractApplicationContext.refresh(),是不是很熟悉了,这不是SpringIOC容器的入口吗
public void refresh() throws BeansException, IllegalStateException {
	synchronized (this.startupShutdownMonitor) {
		// Prepare this context for refreshing.
		prepareRefresh();

		// Tell the subclass to refresh the internal bean factory.
		ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

		// Prepare the bean factory for use in this context.
		prepareBeanFactory(beanFactory);

		......
}
  • 无缝整合!创建web容器的同时,也创建好了IOC容器。容器创建完成,接下来我们再回到前面继续看onRefresh方法,到DispatcherServlet.onRefresh()方法
protected void onRefresh(ApplicationContext context) {
	initStrategies(context);
}

//初始化九大组件
protected void initStrategies(ApplicationContext context) {
	//图片上传组件
    initMultipartResolver(context);
    //初始化本地语言环境
    initLocaleResolver(context);
    //初始化模板处理器
    initThemeResolver(context);
    //handlerMapping
    initHandlerMappings(context);
    //初始化参数适配器
    initHandlerAdapters(context);
    //初始化异常拦截器
    initHandlerExceptionResolvers(context);
    //初始化视图预处理器
    initRequestToViewNameTranslator(context);
    //初始化视图转换器
    initViewResolvers(context);
    //初始化参数缓存管理
    initFlashMapManager(context);
}
  • 9大组件只挑几个我们关注的分析,先分析initHandlerMappings()
private void initHandlerMappings(ApplicationContext context) {
	//保存HandlerMapping的list	private List<HandlerMapping> handlerMappings;
	this.handlerMappings = null;
	//找到所有的映射器 默认为true
	if (this.detectAllHandlerMappings) {
		// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
         //翻译过来就是找到所有的HandleMapping 映射器
		Map<String, HandlerMapping> matchingBeans =
				BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
		if (!matchingBeans.isEmpty()) {
            //然后保存到本地的handlerMapping List
			this.handlerMappings = new ArrayList<>(matchingBeans.values());
			// We keep HandlerMappings in sorted order.
			AnnotationAwareOrderComparator.sort(this.handlerMappings);
		}
	}
	.....
}
  • 经过断点调试,springboot中映射器截图就这么多。然后保存到本地变量List<HandlerMapping> handlerMappings
    handlerMaping映射器

其他组件都是去容器中取出对应类型的bean,代码就不贴了。
初始化阶段至此结束,接下来开始调用阶段!

3.MVC调用阶段

用过servlet都知道,需要自己实现doGet/doPost方法,那么直接从DispatcherServlet类开始找,最终在父类FrameworkServlet找到

//还有其他的例如 doPut、doDelete这里就不贴了
@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);
}

FrameworkServletdoGet/doPost方法下processRequest() -> DispatcherServlet#doService() -> DispatcherServlet#doDispatch()一直到这里,中间初始化一些配置和本地线程变量。

DispatcherServlet#doDispatch这个方法有四个关注点,一个一个来

3.1 getHandler()根据当前请求找到对应的请求Bean和管道

  • DispatcherServlet#doDispatch 找到这段代码
//根据当前请求找到对应的请求地址管道
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
	//没找到路径 返回404
	noHandlerFound(processedRequest, response);
	return;
}
  • getHandler一直往下,到AbstractHandlerMapping
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    //拿到HandlerMethod对象 我们接下来看一下这个方法 
	Object handler = getHandlerInternal(request);
	if (handler == null) {
		handler = getDefaultHandler();
	}
	if (handler == null) {
		return null;
	}
	// Bean name or resolved handler?
	if (handler instanceof String) {
		String handlerName = (String) handler;
		handler = obtainApplicationContext().getBean(handlerName);
	}
	//可能会存在多个请求地址 组成封装好的管道链 
	HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);

	......
	//返回管道链
	return executionChain;
}

接下来到AbstractHandlerMethodMapping#getHandlerInternal,这里分为两步,第一步是从注册的信息中获取对应的HandlerMethod bean,先看方法initLookupPath()

3.1.1 initLookupPath()从注册的信息中获取对应的HandlerMethod

//getHandlerInternal到抽象类 AbstractHandlerMethodMapping#getHandlerInternal
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
	//获取请求的url路径
	String lookupPath = initLookupPath(request);
	//加读锁
	this.mappingRegistry.acquireReadLock();
	try {
		//根据请求路径取出对应的handlerMethod beanName和Method
		HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
		//如果不等于null 就去容器中根据beanName获取bean
		return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
	}
	finally {
		this.mappingRegistry.releaseReadLock();
	}
}
  • 获取请求的url路径 AbstractHandlerMapping#initLookupPath()->UrlPathHelper#resolveAndCacheLookupPath()->getLookupPathForRequest()->getPathWithinApplication(),这个代码应该很熟悉,返回请求路径
public String getPathWithinApplication(HttpServletRequest request) {
	//获取项目路径
	String contextPath = getContextPath(request);
	//获取请求路径
	String requestUri = getRequestUri(request);
	//合并 下面有注释 正常情况下 URI会包含项目路径
	String path = getRemainingPath(requestUri, contextPath, true);
	if (path != null) {
		// Normal case: URI contains context path.
		return (StringUtils.hasText(path) ? path : "/");
	}
	else {
		return requestUri;
	}
}

上面已经拿到请求路径了,再回到AbstractHandlerMapping#getHandlerInternal根据请求路径取出对应的handlerMethod,在lookupHandlerMethod()方法里

3.1.2 lookupHandlerMethod()

protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
	List<Match> matches = new ArrayList<>();
	//去本地变量中拿到对应的mapping
	//this.pathLookup.get(urlPath); 上面有提过的一个封装的Map
	//key是路径,value是List RequestMappingInfo
	List<T> directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath);
	if (directPathMatches != null) {
		//用上面的mapping 校验request,最终封装成RequestMappingInfo添加到matches list
		//继续往下看
		addMatchingMappings(directPathMatches, matches, request);
	}
	if (matches.isEmpty()) {
		addMatchingMappings(this.mappingRegistry.getRegistrations().keySet(), matches, request);
	}
	if (!matches.isEmpty()) {
		Match bestMatch = matches.get(0);
		if (matches.size() > 1) {
			Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
			matches.sort(comparator);
			bestMatch = matches.get(0);
			
			//不关注的去掉了
			。。。
		}
		request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.getHandlerMethod());
		handleMatch(bestMatch.mapping, lookupPath, request);
		//返回bean的封装信息
		return bestMatch.getHandlerMethod();
	}
	else {
		return handleNoMatch(this.mappingRegistry.getRegistrations().keySet(), lookupPath, request);
	}
}
  • AbstractHandlerMethodMapping#addMatchingMappings()添加有效的mapping
private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) {
	for (T mapping : mappings) {
		//通过mapping中的一下本地变量校验request
		T match = getMatchingMapping(mapping, request);
		if (match != null) {
			//添加到list
			matches.add(new Match(match, 
			this.mappingRegistry.getRegistrations().get(mapping)));
		}
	}
}
  • 获取mapping的注册信息,
this.mappingRegistry.getRegistrations().get(mapping);

public Map<T, MappingRegistration<T>> getRegistrations() {
	return this.registry;
}
//前面添加 this.mappingRegistry.register(mapping, handler, method);
//上面有提过此时的handler还是beanName
private final Map<T, MappingRegistration<T>> registry = new HashMap<>();
  • 再次回到AbstractHandlerMethodMapping#getHandlerInternal() 此时已经拿到对应的handlerMethod了。但此时的handler还是beanName
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
	String lookupPath = initLookupPath(request);
	this.mappingRegistry.acquireReadLock();
	try {
		//根据请求路径取出对应的handlerMethod beanName和Method
		HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
		//如果不等于null 就去容器中根据beanName获取bean
		//先看createWithResolvedBean方法
		return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
	}
	finally {
		this.mappingRegistry.releaseReadLock();
	}
}
  • handlerMethod.createWithResolvedBean()HandlerMethod#createWithResolvedBean()
public HandlerMethod createWithResolvedBean() {
	Object handler = this.bean;
	if (this.bean instanceof String) {
		Assert.state(this.beanFactory != null, "Cannot resolve bean name without BeanFactory");
		String beanName = (String) this.bean;
		//还是熟悉的味道 getBean就不用多说了吧
		handler = this.beanFactory.getBean(beanName);
	}
	return new HandlerMethod(this, handler);
}

至此,handler才从真正的实例化,得到的就是我们要调用的bean了

3.2 getHandlerAdapter()获取处理器

HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

//返回此处理程序对象的HandlerAdapter。
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
	if (this.handlerAdapters != null) {
		for (HandlerAdapter adapter : this.handlerAdapters) {
          		//类型匹配 满足就返回
			if (adapter.supports(handler)) {
				return adapter;
			}
		}
	}
	throw new ServletException("No adapter for handler [" + handler +
			"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}


//随便看了一个 AbstractHandlerMethodAdapter 其实就是类型匹配
public final boolean supports(Object handler) {
	return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}

3.3 handle()进行调用

  • 上面通过校验已经拿到请求的bean了,回到DispatcherServlet#doDispatch
//通过封装的bean 拿到执行器 Adapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Actually invoke the handler.
//开始执行 往这里走
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
  • 接下来最终会到RequestMappingHandlerAdapter#handle()
//父类抽象类 AbstractHandlerMethodAdapter
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
	return handleInternal(request, response, (HandlerMethod) handler);
}

//RequestMappingHandlerAdapter
protected ModelAndView handleInternal(HttpServletRequest request,
			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
	//注意这里 返回的是一个ModelAndView 
	ModelAndView mav;
	checkRequest(request);

	// Execute invokeHandlerMethod in synchronized block if required.
	//服务器session存在需要加锁 现在能分清session和cookie的区别了吧
	if (this.synchronizeOnSession) {
		HttpSession session = request.getSession(false);
		if (session != null) {
			Object mutex = WebUtils.getSessionMutex(session);
			synchronized (mutex) {
				mav = invokeHandlerMethod(request, response, handlerMethod);
			}
		}
		else {
			// No HttpSession available -> no mutex necessary
			//直接看调用吧 走这里
			mav = invokeHandlerMethod(request, response, handlerMethod);
		}
	}
	else {
		// No synchronization on session demanded at all...
		mav = invokeHandlerMethod(request, response, handlerMethod);
	}

	if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
		if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
			applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
		}
		else {
			prepareResponse(response);
		}
	}

	return mav;
}
  • invokeHandlerMethod()方法关注两行
	//调用 先看调用
	invocableMethod.invokeAndHandle(webRequest, mavContainer);
	if (asyncManager.isConcurrentHandlingStarted()) {
		return null;
	}
	//封装成ModelAndView
	return getModelAndView(mavContainer, modelFactory, webRequest);

3.4 invokeAndHandle()调用

  • ServletInvocableHandlerMethod#invokeAndHandle
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
		Object... providedArgs) throws Exception {
	//调用以及获取返回值 先看这个 
	Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
	setResponseStatus(webRequest);

	//无返回值直接return
	if (returnValue == null) {
		if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
			disableContentCachingIfNecessary(webRequest);
			mavContainer.setRequestHandled(true);
			return;
		}
	}
	else if (StringUtils.hasText(getResponseStatusReason())) {
		mavContainer.setRequestHandled(true);
		return;
	}
	//设置还没有处理返回结果
	mavContainer.setRequestHandled(false);
	Assert.state(this.returnValueHandlers != null, "No return value handlers");
	try {
		//调用返回值的实现 例如返回页面回到ModelAndViewMethodReturnValueHandler
		//返回字符串数据到RequestResponseBodyMethodProcessor
		//最后一点返回值时分析
		this.returnValueHandlers.handleReturnValue(
				returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
	}
	catch (Exception ex) {
		if (logger.isTraceEnabled()) {
			logger.trace(formatErrorForReturnValue(returnValue), ex);
		}
		throw ex;
	}
}
  • InvocableHandlerMethod#invokeForRequest()
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {
		//获取参数 重点看这个
		Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
		if (logger.isTraceEnabled()) {
			logger.trace("Arguments: " + Arrays.toString(args));
		}
		//这里就不多说了 后面代码return method.invoke(getBean(), args);
		return doInvoke(args);
	}
  • getMethodArgumentValues()看过手写的MVC,这个代码应该不陌生,这里简单过一下
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
		Object... providedArgs) throws Exception {
	//获取方法的参数数组
	MethodParameter[] parameters = getMethodParameters();
	if (ObjectUtils.isEmpty(parameters)) {
		return EMPTY_ARGS;	//无参
	}

	Object[] args = new Object[parameters.length];
	for (int i = 0; i < parameters.length; i++) {
		MethodParameter parameter = parameters[i];
		parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
		args[i] = findProvidedArgument(parameter, providedArgs);
		if (args[i] != null) {
			continue;
		}
		if (!this.resolvers.supportsParameter(parameter)) {
			throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
		}
		try {
			//通过转换器赋值
			args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
		}
		catch (Exception ex) {
			// Leave stack trace for later, exception may actually be resolved and handled...
			if (logger.isDebugEnabled()) {
				String exMsg = ex.getMessage();
				if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
					logger.debug(formatArgumentError(parameter, exMsg));
				}
			}
			throw ex;
		}
	}
	return args;
}
  • 再回到ServletInvocableHandlerMethod#invokeAndHandle 分析下返回参数的封装
//挑了页面的返回 ModelAndViewMethodReturnValueHandler
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
		ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {

	if (returnValue == null) {
		//空的设置返回已处理
		mavContainer.setRequestHandled(true);
		return;
	}
	//返回值是modelAndView的 里面是html路径和参数
	ModelAndView mav = (ModelAndView) returnValue;
	if (mav.isReference()) {
		String viewName = mav.getViewName();
		//保存到mavContainer容器里面来
		mavContainer.setViewName(viewName);
		if (viewName != null && isRedirectViewName(viewName)) {
			mavContainer.setRedirectModelScenario(true);
		}
	}
	else {
		View view = mav.getView();
		mavContainer.setView(view);
		if (view instanceof SmartView && ((SmartView) view).isRedirectView()) {
			mavContainer.setRedirectModelScenario(true);
		}
	}
	mavContainer.setStatus(mav.getStatus());
	mavContainer.addAllAttributes(mav.getModel());
}

至此调用业务类已经完成,并且拿到了返回值。接下来分析返回

3.5 返回值处理

3.5.1 非页面返回值分析

  • 找到这里ServletInvocableHandlerMethod#invokeAndHandle返回值这里也分情况,加了@ResponseBody的才能找到
this.returnValueHandlers.handleReturnValue(
					returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
//handleReturnValue一直往下到RequestResponseBodyMethodProcessor
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
		ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
		throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
	//设置返回值已处理
	mavContainer.setRequestHandled(true);
	ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
	ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);

	// Try even with null return value. ResponseBodyAdvice could get involved.
	//即使返回值为空,也要尝试。ResponseBodyAdvice可能会参与进来
    //到最后通过 response.write写出去
	writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}

3.5.2 如果是页面返回的

  • 非页面的在这里其实已经写出去了,这里看到是返回页面的,我们以127.0.0.1/error页面为例

  • 再次回到DispatcherServlet#doDispatch,找到这段代码

//处理返回结果
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);


private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
		@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
		@Nullable Exception exception) throws Exception {

	...

	// Did the handler return a view to render?
    //返回去的页面是否要显示视图
	if (mv != null && !mv.wasCleared()) {
        //渲染 多余的去掉 这里重点关注
		render(mv, request, response);
		if (errorView) {
			WebUtils.clearErrorRequestAttributes(request);
		}
	}
	
	....
}

	protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
	...... 
           
       //不关注的判断先去掉
	try {
		if (mv.getStatus() != null) {
			response.setStatus(mv.getStatus().value());
		}
           //视图的渲染 请求/error 断点看到接下来走ErrorMvcAutoConfiguration.render
		view.render(mv.getModelInternal(), request, response);
	}
	catch (Exception ex) {
		if (logger.isDebugEnabled()) {
			logger.debug("Error rendering view [" + view + "]", ex);
		}
		throw ex;
	}
}
  • 接下来到ErrorMvcAutoConfiguration.render
public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response)
			throws Exception {
		if (response.isCommitted()) {
			String message = getMessage(model);
			logger.error(message);
			return;
		}
		response.setContentType(TEXT_HTML_UTF8.toString());
		StringBuilder builder = new StringBuilder();
		Date timestamp = (Date) model.get("timestamp");
		Object message = model.get("message");
		Object trace = model.get("trace");
		if (response.getContentType() == null) {
			response.setContentType(getContentType());
		}
		builder.append("<html><body><h1>Whitelabel Error Page</h1>").append(
				"<p>This application has no explicit mapping for /error, so you are seeing this as a fallback.</p>")
				.append("<div id='created'>").append(timestamp).append("</div>")
				.append("<div>There was an unexpected error (type=").append(htmlEscape(model.get("error")))
				.append(", status=").append(htmlEscape(model.get("status"))).append(").</div>");
		if (message != null) {
			builder.append("<div>").append(htmlEscape(message)).append("</div>");
		}
		if (trace != null) {
			builder.append("<div style='white-space:pre-wrap;'>").append(htmlEscape(trace)).append("</div>");
		}
		builder.append("</body></html>");
   		//通过write写到页面上
		response.getWriter().append(builder.toString());
	}
  • 在页面上看到就是这样的
    error

至此,整个MVC的源码分析结束了。多看多断点就会发生一切很简单,只是想的太复杂

以上就是本章的全部内容了。

上一篇:Spring源码分析第三弹 - AOP切面编程分析
下一篇:Spring源码分析第五弹 - 神级的spring还有其他什么功效?

问渠那得清如许,为有源头活水来

举报

相关推荐

0 条评论