您当前的位置: 首页 >  spring

恐龙弟旺仔

暂无认证

  • 3浏览

    0关注

    282博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

SpringMVC源码解析(下)

恐龙弟旺仔 发布时间:2018-07-19 10:50:49 ,浏览量:3

前言:

    我们接着上一篇文章 SpringMVC源码解析(上) 来继续分析,下面来看DispatcherServlet的源码详细解析

    init()方法用来初始化资源;

    doGet()/doPost()等方法用来接收并处理请求;

    下面就按照这两块来分析DispatcherServlet源码

 

1.DispatcherServlet.init(默认实现在HttpServletBean类中)     1)init()初始化
	public final void init() throws ServletException {
		if (logger.isDebugEnabled()) {
			logger.debug("Initializing servlet '" + getServletName() + "'");
		}

		try {
            // 1.获取web.xml中关于DispatcherServlet的init-parm配置,封装为PropertyValues
			PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
            
            // 2.将当前DispatcherServlet封装成BeanWrapper
			BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
			ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
			bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
			initBeanWrapper(bw);
            
            // 3.将PropertyValues的参数封装到BeanWrapper
			bw.setPropertyValues(pvs, true);
		}
		catch (BeansException ex) {
			if (logger.isErrorEnabled()) {
				logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
			}
			throw ex;
		}

		// 4.初始化资源(重要方法)
		initServletBean();

		if (logger.isDebugEnabled()) {
			logger.debug("Servlet '" + getServletName() + "' configured successfully");
		}
	}
    2)initServletBean()方法默认在子类FrameworkServlet中实现
	protected final void initServletBean() throws ServletException {
		...
		try {
			this.webApplicationContext = initWebApplicationContext();// 重要方法
			initFrameworkServlet();
		}
        ...
	}
        
    // initWebApplicationContext()
    protected WebApplicationContext initWebApplicationContext() {
		WebApplicationContext rootContext =
				WebApplicationContextUtils.getWebApplicationContext(getServletContext());
		WebApplicationContext wac = null;

		...
        // 1.先去查看当前应用中是否有ApplicationContext,如果有就用这个,没有的话则新建
		if (wac == null) {
			wac = createWebApplicationContext(rootContext);// 在3)中详细分析
		}

		if (!this.refreshEventReceived) {
            // 2.重要的初始化方法,会在这里初始化HandlerMapping、Resolver等各种资源
			onRefresh(wac);// 会在4)中详细分析
		}
        ...
		return wac;
	}
    3)FrameworkServlet.createWebApplicationContext()分析如何创建ApplicationContext
	protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
		Class contextClass = getContextClass();
        ...
		ConfigurableWebApplicationContext wac =
				(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);

		wac.setEnvironment(getEnvironment());
		wac.setParent(parent);
		wac.setConfigLocation(getContextConfigLocation());

		configureAndRefreshWebApplicationContext(wac);

		return wac;
	}

    //configureAndRefreshWebApplicationContext()
	protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
        ...
        
        // 初始化各种资源
		wac.setServletContext(getServletContext());
		wac.setServletConfig(getServletConfig());
		wac.setNamespace(getNamespace());
		wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));

		ConfigurableEnvironment env = wac.getEnvironment();
		if (env instanceof ConfigurableWebEnvironment) {
			((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
		}

		postProcessWebApplicationContext(wac);
		applyInitializers(wac);
        
        // 重要方法,最终实现在AbstractApplicationContext.refresh(),
        // 会初始化一个ApplicationContext,加载bean到Spring中
		wac.refresh();
	}
    4)FrameworkServlet.onfresh()默认实现在DispatcherServlet
	@Override
	protected void onRefresh(ApplicationContext context) {
        // 主要是为了刷新DispatcherServlet中的一些全局变量,如multipartResolver、handlerMappings等
		initStrategies(context);
	}

	protected void initStrategies(ApplicationContext context) {
        // 1.获取Spring中的相关resolver_bean,这个比较简单,直接context.getBean()来获取
		initMultipartResolver(context);
		initLocaleResolver(context);
		initThemeResolver(context);
        
        // 2.加载容器中合适的HandlerMapping(重要方法)
		initHandlerMappings(context);// 在5)中详细分析
        
        // 3.加载容器中合适的HandlerAdapter
		initHandlerAdapters(context);
		initHandlerExceptionResolvers(context);
		initRequestToViewNameTranslator(context);
        
        // 4.初始化 viewResolvers
		initViewResolvers(context);
		initFlashMapManager(context);
	}
   5)initHandlerMappings(context)初始化handlerMappings信息
	private void initHandlerMappings(ApplicationContext context) {
		this.handlerMappings = null;

        // 1.默认detectAllHandlerMappings为true,所以直接走该段逻辑
		if (this.detectAllHandlerMappings) {
			// 真正实现就是这句话
            // 代码实现:ListableBeanFactory.getBeansOfType(type, includeNonSingletons, allowEagerInit)
            // 主要就是从容器中获取关于HandlerMapping接口的实现
            // 类DelegatingWebMvcConfiguration实现了@configuration,并且在其父类WebMvcConfigurationSupport中
            /**
            *    @Bean
	             public RequestMappingHandlerMapping requestMappingHandlerMapping() {}
                 @Bean
	             public BeanNameUrlHandlerMapping beanNameHandlerMapping() {}
            *
            */
            // 默认加载这两个bean到容器中,则HandlerMappings会添加这两个bean
			Map matchingBeans =
					BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
			if (!matchingBeans.isEmpty()) {
				this.handlerMappings = new ArrayList(matchingBeans.values());
				// We keep HandlerMappings in sorted order.
				AnnotationAwareOrderComparator.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.
			}
		}

		// 2.如果获取不到handlerMappings,则查询默认值
        // 默认值在spring-webmvc.jar下DispatcherServlet.properties中
        // 默认值内容为:org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
	//org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping
		if (this.handlerMappings == null) {
			this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
			if (logger.isDebugEnabled()) {
				logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");
			}
		}
	}

    注意:由上述代码分析可知,RequestMappingHandlerMapping类在Spring容器初始化的时候就会被加载进来,下面分析下这个类,   有其类层次结构可知其实现了InitializingBean接口,在该bean初始化的时候会默认执行其afterPropertiesSet()方法,下面看下这个方法

	public void afterPropertiesSet() {
		this.config = new RequestMappingInfo.BuilderConfiguration();
		this.config.setUrlPathHelper(getUrlPathHelper());
		this.config.setPathMatcher(getPathMatcher());
		this.config.setSuffixPatternMatch(this.useSuffixPatternMatch);
		this.config.setTrailingSlashMatch(this.useTrailingSlashMatch);
		this.config.setRegisteredSuffixPatternMatch(this.useRegisteredSuffixPatternMatch);
		this.config.setContentNegotiationManager(getContentNegotiationManager());

		super.afterPropertiesSet();// 重点看这里
	}

    // super.afterPropertiesSet()
	@Override
	public void afterPropertiesSet() {
		initHandlerMethods();
	}

	protected void initHandlerMethods() {
		...
        // 1.默认加载所有的bean
		String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
				BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
				getApplicationContext().getBeanNamesForType(Object.class));

		for (String beanName : beanNames) {
			if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
				Class beanType = null;
				try {
                    // 2.获取该bean
					beanType = getApplicationContext().getType(beanName);
				}
				...
                // 3.如果该bean有Controller注解或者RequestMapping注解,则将该RequestMapping对应的方法注册到Handler中
                // isHandler()方法内容体为 AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
				if (beanType != null && isHandler(beanType)) {
					detectHandlerMethods(beanName);// 重要方法,主要的解析Controller中的方法就在这
				}
			}
		}
		handlerMethodsInitialized(getHandlerMethods());
	}

    // detectHandlerMethods()
	protected void detectHandlerMethods(final Object handler) {
		Class handlerType = (handler instanceof String ?
				getApplicationContext().getType((String) handler) : handler.getClass());
		final Class userType = ClassUtils.getUserClass(handlerType);

        // 1.获取该handler类所有的注解方法,对应于请求路径
		Map methods = MethodIntrospector.selectMethods(userType,
				new MethodIntrospector.MetadataLookup() {
					@Override
					public T inspect(Method method) {
						try {
							return getMappingForMethod(method, userType);
						}
						catch (Throwable ex) {
							throw new IllegalStateException("Invalid mapping on handler class [" +
									userType.getName() + "]: " + method, ex);
						}
					}
				});

		if (logger.isDebugEnabled()) {
			logger.debug(methods.size() + " request handler methods found on " + userType + ": " + methods);
		}

        // 2.将注解方法注册到RequestMappingHandlerMapping中
		for (Map.Entry entry : methods.entrySet()) {
			Method invocableMethod = AopUtils.selectInvocableMethod(entry.getKey(), userType);
			T mapping = entry.getValue();
			registerHandlerMethod(handler, invocableMethod, mapping);
		}
	}

    总结1:初始化的主要内容包括两部分:

    1)创建ApplicationContext,加载bean到容器中

    2)初始化HandlerMappings等资源

    3)加载实现了HandlerMapping接口的类,如RequestMappingHandlerMapping,在初始化该bean完成的时候,调用afterPropertiesSet()方法,完成对Controller的检索,将所有的@RequestMapping方法注册到RequestMappingHandlerMapping中

 

2.DispatcherServlet.doGet()方法具体实现

    可知其默认实现在FrameworkServlet类中,方法如下:

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

		processRequest(request, response);
	}

	protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		...
		try {
			doService(request, response);
		}
		...
	}
       
    // doService()默认实现在DispatcherServlet类中,方法如下:
    protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
		...
		try {
			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);

				// 2.获取HandlerMapping,主要是为了寻找请求对应的Controller类方法
                // 这个方法之前已经放在RequestMappingHandlerMapping中了
				mappedHandler = getHandler(processedRequest);// 在1)中详细分析
				if (mappedHandler == null || mappedHandler.getHandler() == null) {
					noHandlerFound(processedRequest, response);
					return;
				}

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

				...

				// 4.调用HandlerAdapter.handler方法
                // handler方法会先执行拦截器的方法,然后执行Controller的对应方法获取对应的ModelAndView,也就是对应的视图
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());// 在2)中详细分析

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

				applyDefaultViewName(processedRequest, mv);
				mappedHandler.applyPostHandle(processedRequest, response, mv);
			}
			...
            // 5.跳转到指定页面
			processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);// 在3)中详细分析
		}
		...
	}     
    1)getHandler(processedRequest)根据请求获取对应HandlerExecutionChain
	public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		// 1.获取请求对应的Handler
        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 = getApplicationContext().getBean(handlerName);
		}

        // 2.封装请求链,主要是为了把请求方法对应的拦截器添加到HandlerExecutionChain
		HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
		if (CorsUtils.isCorsRequest(request)) {
			CorsConfiguration globalConfig = this.corsConfigSource.getCorsConfiguration(request);
			CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
			CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
			executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
		}
		return executionChain;
	}

   // getHandlerInternal()获取请求对应的Handler

	protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
        // 1.获取请求路径(去掉项目名之后的信息)
		String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
		if (logger.isDebugEnabled()) {
			logger.debug("Looking up handler method for path " + lookupPath);
		}
		this.mappingRegistry.acquireReadLock();
		try {
            // 2.从之前注册到RequestMappingHandlerMapping的HandlerMethod map中查找与请求路径匹配的HandlerMethod
			HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
			if (logger.isDebugEnabled()) {
				if (handlerMethod != null) {
					logger.debug("Returning handler method [" + handlerMethod + "]");
				}
				else {
					logger.debug("Did not find handler method for [" + lookupPath + "]");
				}
			}
			return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
		}
		finally {
			this.mappingRegistry.releaseReadLock();
		}
	}

    2)ha.handle(processedRequest, response, mappedHandler.getHandler())

    兜兜转转,笔者该例的ha实现类为RequestMappingHandlerAdapter,其handle方法具体实现在该类的handleInternal()方法中

	protected ModelAndView handleInternal(HttpServletRequest request,
			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

		ModelAndView mav;
		checkRequest(request);

		// synchronizeOnSession默认为false
		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 {
			// 重要方法,请求调用Controller具体实现方法就在这里实现
			mav = invokeHandlerMethod(request, response, handlerMethod);
		}

		...
		return mav;
	}

    //invokeHandlerMethod()
	protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

		ServletWebRequest webRequest = new ServletWebRequest(request, response);
		try {
			...// 主要是一堆数据封装

            // 1.在这个方法中会调用Controller类实现方法,并获得响应
			invocableMethod.invokeAndHandle(webRequest, mavContainer);// 下面详细分析该方法
			if (asyncManager.isConcurrentHandlingStarted()) {
				return null;
			}

            // 2.获取响应对应的ModelAndView
			return getModelAndView(mavContainer, modelFactory, webRequest); // 下面详细分析该方法
		}
		finally {
			webRequest.requestCompleted();
		}
	}

    // invokeAndHandle()真正的Controller层方法调用

	public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {
        // 具体实现在这
		Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
		...
		try {
			this.returnValueHandlers.handleReturnValue(
					returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
		}
		...
	}

    // invokeForRequest
    public Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {
        // 1.获取请求参数
		Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
		if (logger.isTraceEnabled()) {
			logger.trace("Invoking '" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) +
					"' with arguments " + Arrays.toString(args));
		}
        
        // 2.Method反射来执行方法
		Object returnValue = doInvoke(args);
		if (logger.isTraceEnabled()) {
			logger.trace("Method [" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) +
					"] returned [" + returnValue + "]");
		}
		return returnValue;
	}    
        
    // doInvoke
    protected Object doInvoke(Object... args) throws Exception {
		ReflectionUtils.makeAccessible(getBridgedMethod());
		try {
            // 即Method.invoke(Object obj, Object... args)
			return getBridgedMethod().invoke(getBean(), args);
		}
		...
	}

    // getModelAndView()调用完Controller方法之后,返回对应的视图界面

	private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
			ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {

		modelFactory.updateModel(webRequest, mavContainer);
		if (mavContainer.isRequestHandled()) {
			return null;
		}
		ModelMap model = mavContainer.getModel();
        // 直接从mavContainer中获取viewName并封装即可
		ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
		if (!mavContainer.isViewReference()) {
			mav.setView((View) mavContainer.getView());
		}
		if (model instanceof RedirectAttributes) {
			Map flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
			HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
			RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
		}
		return mav;
	}

    经过上面的一堆分析之后,我们已经根据请求调用了对应Controller的方法,并且根据方法返回值获取对应的视图页面名称

    下面就是最后一步了,跳转到指定的视图

    3)processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException)
	private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
			HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {

		boolean errorView = false;
        ...

		// Did the handler return a view to render?
		if (mv != null && !mv.wasCleared()) {
            // 真正实现方法在这
			render(mv, request, response);
			if (errorView) {
				WebUtils.clearErrorRequestAttributes(request);
			}
		}
		...
	}
        
    // render()
    protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
		// Determine locale for request and apply it to the response.
		Locale locale = this.localeResolver.resolveLocale(request);
		response.setLocale(locale);

		View view;
		if (mv.isReference()) {
			// 1.获取对应的View信息,view包含了ModelAndView中viewName对应的页面全路径信息
			view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
			if (view == null) {
				throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
						"' in servlet with name '" + getServletName() + "'");
			}
		}
		...
		try {
			if (mv.getStatus() != null) {
				response.setStatus(mv.getStatus().value());
			}
            // 2.真正跳转方法,笔者该例中默认实现在AbstractView中
			view.render(mv.getModelInternal(), request, response);
		}
		...
	}

        
    // view.render()
    public void render(Map model, HttpServletRequest request, HttpServletResponse response) throws Exception {
		if (logger.isTraceEnabled()) {
			logger.trace("Rendering view with name '" + this.beanName + "' with model " + model +
				" and static attributes " + this.staticAttributes);
		}

		Map mergedModel = createMergedOutputModel(model, request, response);
		prepareResponse(request, response);
		renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);// 重点在这,实现类为当前调用类,笔者的为InternalResourceView类
	}
        
        
    // renderMergedOutputModel()
    protected void renderMergedOutputModel(
			Map model, HttpServletRequest request, HttpServletResponse response) throws Exception {

		// 1.获取转发路径
		String dispatcherPath = prepareForRendering(request, response);

		// 2.获取RequestDispatcher
		RequestDispatcher rd = getRequestDispatcher(request, dispatcherPath);
		if (rd == null) {
			throw new ServletException("Could not get RequestDispatcher for [" + getUrl() +
					"]: Check that the corresponding file exists within your web application archive!");
		}

		// If already included or response already committed, perform include, else forward.
		if (useInclude(request, response)) {
			response.setContentType(getContentType());
			if (logger.isDebugEnabled()) {
				logger.debug("Including resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'");
			}
			rd.include(request, response);
		}

		else {
			// Note: The forwarded resource is supposed to determine the content type itself.
			if (logger.isDebugEnabled()) {
				logger.debug("Forwarding to resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'");
			}
            // 3.forward转发模式,
			rd.forward(request, response);
		}
	}

    总结3):分析完之后,可以发现,无论方法封装了多少层,其最本质的方法也就是我们之前在 SpringMVC源码解析(上)中使用Servlet进行转发时候的那样

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

    总结:通过两篇博客来分析了SpringMVC的具体调用过程,主要还是分两步走:

    1)初始化bean,将HandlerMapping实现类相关bean加载到Spring中,这些bean在初始化完成之后,也会扫描所有的@RequestMapping方法,并注册到该HandlerMapping中去

    2)根据请求路径获取对应的HandlerExecutionChain(主要包含request相对应的Controller处理类和拦截器列表),然后调用HandlerAdapter相关方法来执行Controller方法和拦截器方法,并返回视图界面名称。根据视图名称进行跳转操作,完成响应

 

 

 

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

关注
打赏
1655041699
查看更多评论
立即登录/注册

微信扫码登录

0.0534s