SpringMVC之ViewResolver&theme&FlashMapManager

1.视图解析之ViewResolver

ViewResolver :只是将String类型的视图名|逻辑视图和locale解析为View类型的视图。
视图View:是用来渲染页面的,通俗点讲就是将程序返回的参数填充到模板里面,生成HTML等文件中。

  • 使用哪个模板。
  • 使用何种技术(或者规则)填充参数。
    这其实就是ViewResolver主要要做的工作。ViewResolver需要找到渲染所用的模板和所用的技术(也就是视图类型)进行渲染,具体的渲染过程则交给不同的视图自己完成。我们最常用的UrlBaseViewResolver系列的解析器都是针对单一视图类型进行解析的,只需要找到使用的模版就可以了,比如InternalResourceViewResolver只是针对JSP类型的视图,FreeMarkerViewResolver只是针对FreeMarker,VelocityViewResolver只针对Velocity。而ResourceBundleViewResolver、XmlViewResolver、BeanNameViewResolver等解析器可以同时解析多种类型的视图。如前面说过的ResourceBundleViewResolver,它是根据properties配置文件解析的,配置文件里就需要同时配置class和url两项内容,比如:
hello.(class)=org.springframework.Web.servlet.view.InternalResourceView
hello.url=/WEB-INF/go.jsp

这样就将hello配置到/WEB-INF/go.jsp模板的jsp类型的视图了,当Controller返回hello时会使用这个视图来进行渲染。这里的jsp类型并不是根据go.jsp的后缀来确定的,这个后缀只是个文件名,可以把它改成任意的格式,视图类型是根据.class的配置项来确定的。

	processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
	...
	mappedHandler.triggerAfterCompletion(request, response, null);

视图解析:

protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
		// Determine locale for request and apply it to the response.
		...
		View view;
		String viewName = mv.getViewName();
		if (viewName != null) {
			// We need to resolve the view name.
			// 解析视图
			view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
			...
		}else {
			view = mv.getView();
		}
		if (mv.getStatus() != null) {
			response.setStatus(mv.getStatus().value());
		}
		// 利用ModelAndView 中数据渲染视图
		view.render(mv.getModelInternal(), request, response);
		...
	}
protected View resolveViewName(String viewName, @Nullable Map<String, Object> model,
		Locale locale, HttpServletRequest request) throws Exception {
	if (this.viewResolvers != null) {
		// 视图解析器 ViewResolver 通过视图名解析视图,返回视图实例View
		for (ViewResolver viewResolver : this.viewResolvers) {
			View view = viewResolver.resolveViewName(viewName, locale);
			if (view != null) {
				return view;
			}
		}
	}
	return null;
}

1.1.初始化变量

注解方法:RequestMappingHandlerMappering AbstractHandlerMethodMapping
AbstractHandlerMethodMapping实现了InitializingBean接口,Spring容器设置初始值后会调用afterPropertiesSet()方法,相当于初始化。
初始化过程:

protected void initHandlerMethods() {
	
	//从容器中获取实例ID
	String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
			BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) :
			obtainApplicationContext().getBeanNamesForType(Object.class));
      //遍历此时容器中所有的Bean ID
	for (String beanName : beanNames) {
		if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
			Class<?> beanType = null;
			beanType = obtainApplicationContext().getType(beanName);
			if (beanType != null && isHandler(beanType)) {
				**detectHandlerMethods**(beanName);
			}
		}
	}
	handlerMethodsInitialized(getHandlerMethods());
}

protected void detectHandlerMethods(final Object handler) {
       //获取到实例
	Class<?> handlerType = (handler instanceof String ?
			obtainApplicationContext().getType((String) handler) : handler.getClass());

	if (handlerType != null) {
		final Class<?> userType = ClassUtils.getUserClass(handlerType);
		//解析方法、类找对对应的注解,从而拿到类以及方法上对应的路径,最后封装在Map中
		Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
				(MethodIntrospector.MetadataLookup<T>) method -> {
					return getMappingForMethod(method, userType);
				});
		for (Map.Entry<Method, T> entry : methods.entrySet()) {
			Method invocableMethod = AopUtils.selectInvocableMethod(entry.getKey(), userType);
			T mapping = entry.getValue();
			//最后将所有相关的信息封装在MappingRegistry对象中
			registerHandlerMethod(handler, invocableMethod, mapping);
		}
	}
}

视图和视图解析器:请求处理方法执行完成之后,最终返回一个ModelAndView对象。对于那些返回String、View以及ModelMap等类型的处理方法,SpringMVC也会在内部将他们封装成一个ModelAndView对象,它包含了逻辑名和模型对象的视图。
Spring MVC借助视图解析器ViewResolver得到最终的视图解析View,最终视图可以是JSP,也可能是Excel、JFreeChart等各种表现形式的视图。
对于最终究竟采取何种视图对象对模型数据进行渲染,处理器并不关心,处理器工作的重点聚焦在生产模型数据的工作上,从而实现MVC的充分解耦。
视图作是 渲染模型数据将模型数据以某 种形式呈现给客户。
为了实现视图模型和具体实现技术 耦 Spring 在org.springframework.web.servlet 包中定义了一个度抽 View接口。

2.theme主题

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.FlashMapManager

FlashMap相关的内容在前面已经介绍过了,主要用在redirect中传递参数。而FlashMapManager是用来管理FlashMap的,定义如下:

public interface FlashMapManager {
	FlashMap retrieveAndUpdate(HttpServletRequest request, HttpServletResponse response);
	void saveOutputFlashMap(FlashMap flashMap, HttpServletRequest request, HttpServletResponse response);

}

retrieveAndUpdate方法用于恢复参数,并将恢复过的和超市的参数从保存介质中删除;saveOutputFlashMap用于将参数保存起来。默认实现是SessionFlashMapManager,它将参数保存到session中。

整个redirect的参数通过FlashMap传递的过程分三步:

  1. 在处理器中将需要传递的参数设置到outputFlashMap中,可以直接使用request.getAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE);拿到outputFlashMap,然后将参数put进去,也可以将需要参数设置到处理器的RedirectAttribute类型的参数中,当处理器处理完请求时,如果是redirect类型的返回值RequestMappingHandlerAdapter会将其设置到outputFlashMap中。
  2. 在RedirectView的renderMergedOutputModel方法中调用FlashMapManager的saveOutputFlashMap方法,将outputFlashMap中的参数设置到session中。
  3. 请求redirect后DiapatchServlet的doservice会调用FlashMapManager的redirectAndUpdate方法从session中获取inputFlashMap并设置到Request的属性中备用,同时从session中删除。

4.拦截器HandlerInterceptor

拦截器以实现WebMvcConfigurationSupport接口为例「不推荐使用」。
根据SpringBoot启动原理WebMvcAutoConfiguration#requestMappingHandlerMapping方法返回的RequestMappingHandlerMapping实例必定会通过反射被调用。

@Configuration
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
		ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {
	@Bean
	@Primary
	@Override
	public RequestMappingHandlerMapping requestMappingHandlerMapping() {
		// Must be @Primary for MvcUriComponentsBuilder to work
		return super.requestMappingHandlerMapping();
	}
}
@Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
	RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping();
	mapping.setOrder(0);
	//此处就会回调自定义拦截器addInterceptors方法,最终被 WebMvcConfigurationSupport#interceptors持有
	//以及AbstractHandlerMapping#interceptors属性持有。
	mapping.setInterceptors(getInterceptors());
	mapping.setContentNegotiationManager(mvcContentNegotiationManager());
	mapping.setCorsConfigurations(getCorsConfigurations());
	...
	return mapping;
}

执行aware#setApplicationContext:

public final void setApplicationContext(@Nullable ApplicationContext context) throws BeansException {
	...
	// 最终执行 AbstractHandlerMapping#initInterceptors方法
	initApplicationContext(context);
}
protected void initInterceptors() {
	if (!this.interceptors.isEmpty()) {
		for (int i = 0; i < this.interceptors.size(); i++) {
			Object interceptor = this.interceptors.get(i);
			if (interceptor == null) {
				throw new IllegalArgumentException("Entry number " + i + " in interceptors array is null");
			}
			// 最终的拦截器由 AbstractHandlerMapping属性adaptedInterceptors持有。
			// 在 DispatcherServlet处理目标handler过程中初始化HandlerExecutionChain 时会通过 AbstractHandlerMapping自己持有拦截器,并由此分别调用拦截器的 preHandle、postHandle、afterCompletion等方法
			this.adaptedInterceptors.add(adaptInterceptor(interceptor));
		}
	}
}

参考Servlet

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值