调度器dispatcher和视图解析器viewResolver

1、请求调度器dispatcher

当请求从客户端发出时,会被后端的dispatcherServlet拦截到,DispatcherServlet将其拦截到的所有请求转发给ioc容器中注册的请求解析器handlerMapping,解析得到字符串之后,根据解析的字符串,在ioc容器中查找用于处理请求的请求处理器。
@Controller
使用该注解标注的类会成为ioc容器实例化的对象
相当于手动在ioc容器中配置一个bean
然后在控制器中进行计算和处理之后会返回模型数据,我们要将这个模型数据返回给客户端,通过将这些数据嵌入到一个页面中传递给用户,那么我们如何完成这一过程呢?

2、在控制器中常用的几种模型数据对象

SpringMVC提供了如下几种方法供我们使用:

  • ModelAndView
  • Map和Model
  • @SessionAttributes

2.1、ModelAndView

modelAndView里面的set方法,能够给该对象set view(视图)和模型数据,然后return给dispatherServlet,这个调度器会通过视图解析器找到对应的视图,并将相应的模型数据填充到其中,至此整个过程结束。
简单的代码如下:
ModelAndView的构造方法

public ModelAndView() {
}
 
public ModelAndView(String viewName) {
	this.view = viewName;
}
 
public ModelAndView(View view) {
	this.view = view;
}
 
public ModelAndView(String viewName, Map<String, ?> model) {
	this.view = viewName;
	if (model != null) {
		getModelMap().addAllAttributes(model);
	}
}
 
public ModelAndView(View view, Map<String, ?> model) {
	this.view = view;
	if (model != null) {
		getModelMap().addAllAttributes(model);
	}
}
 
public ModelAndView(String viewName, String modelName, Object modelObject) {
	this.view = viewName;
	addObject(modelName, modelObject);
}
 
public ModelAndView(View view, String modelName, Object modelObject) {
	this.view = view;
	addObject(modelName, modelObject);
}

给ModelAndView设置模型数据和视图


public void setViewName(String viewName) {
	this.view = viewName;
}
 
public void setView(View view) {
	this.view = view;
}

public ModelAndView addObject(String attributeName, Object attributeValue) {
	getModelMap().addAttribute(attributeName, attributeValue);
	return this;
}
 
public ModelAndView addAllObjects(Map<String, ?> modelMap) {
	getModelMap().addAllAttributes(modelMap);
	return this;
}

使用实例

@RequestMapping(value="/renderView", method={RequestMethod.GET, RequestMethod.POST})
public ModelAndView renderView() {
	User user = new User();
	user.setName("lmy");
	user.setAge(20);
	
	ModelAndView view = new ModelAndView();
	view.addObject("name", "lmy");
	view.addObject("user", user);
	view.setViewName("show");
	return view;
}

前台页面


<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
	<head>
		<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
		<title>Insert title here</title>
	</head>
	<body>
		user name: ${user.name}<br>
		user age: ${user.age}
	</body>
</html>

2.2、Map和Model

在我们的请求处理器中可以加上Map或者Model类型的参数,这些类型主要是为了存储模型数据,它和ModelAndView的一个区别就是这些类型是没有存储视图信息,它是通过该请求处理器的返回值来返回一个视图名,其实在后台会将这些Map或者Model中的模型数据和返回的视图名再次封装成一个ModelAndView。


@RequestMapping(value="/renderView", method={RequestMethod.GET, RequestMethod.POST})
public String renderView1(Map<String, Object> map) {
	User user = new User();
	user.setName("lmy86263");
	user.setAge(24);
	map.put("user", user);
	return "show";
}

2.3、@SessionAttributes

光看这个名字其实就很明显了,这些模型数据是存放在HttpSession中的,说明这里面的数据可以被多个请求所共享。这个注解不能使用在方法上,而是使用在控制器类上。另外它有两种使用方式:

  • 通过属性名指定需要放到会话中的属性;
  • 通过模型属性的对象类型指定哪些模型属性要放到Session中;

@SessionAttributes(names={"names"}, types={User.class})
@Controller
public class ViewController {
	@RequestMapping(value="/renderView1", method={RequestMethod.GET, RequestMethod.POST})
	public String renderView1(Map<String, Object> map) {
		User user = new User();
		user.setName("lmy86263");
		user.setAge(24);
		map.put("user", user);
		map.put("names", Arrays.asList("tom", "jack", "lmy"));
		return "show";
	}
}

前台页面


<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
	<head>
		<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
		<title>Insert title here</title>
	</head>
	<body>
		sessionScope: user name: ${sessionScope.user.name}<br>
		sessionScope: user age: ${sessionScope.user.age} <br>
		sessionScope: names: ${sessionScope.names} <br>
	</body>
</html>

3、视图解析器viewResolver

Spring Boot 自动配置好了SpringMVC

以下是SpringBoot对SpringMVC的默认配置:WebMvcAutoConfiguration)

  • Inclusion of ContentNegotiatingViewResolver and BeanNameViewResolver beans.

    • 自动配置了ViewResolver(视图解析器作用:根据方法的返回值得到视图对象(View),视图对象决定如何渲染(转发?重定向?))
    • ContentNegotiatingViewResolver:组合所有的视图解析器的;
    • 如何定制:我们可以自己给容器中添加一个视图解析器;自动的将其组合进来;
  • Support for serving static resources, including support for WebJars (see below).静态资源文件夹路径,webjars

  • Static index.html support. 静态首页访问

  • Custom Favicon support (see below). favicon.ico
    在ContentNegotiatingViewResolver类中有initServletContext的初始化方法,该方法代码如下

	protected void initServletContext(ServletContext servletContext) {
		Collection<ViewResolver> matchingBeans =
				BeanFactoryUtils.beansOfTypeIncludingAncestors(getApplicationContext(), ViewResolver.class).values();
		if (this.viewResolvers == null) {
			this.viewResolvers = new ArrayList<ViewResolver>(matchingBeans.size());
			for (ViewResolver viewResolver : matchingBeans) {
				if (this != viewResolver) {
					this.viewResolvers.add(viewResolver);
				}
			}
		}
		else {
			for (int i = 0; i < viewResolvers.size(); i++) {
				if (matchingBeans.contains(viewResolvers.get(i))) {
					continue;
				}
				String name = viewResolvers.get(i).getClass().getName() + i;
				getApplicationContext().getAutowireCapableBeanFactory().initializeBean(viewResolvers.get(i), name);
			}

		}
		if (this.viewResolvers.isEmpty()) {
			logger.warn("Did not find any ViewResolvers to delegate to; please configure them using the " +
					"'viewResolvers' property on the ContentNegotiatingViewResolver");
		}
		AnnotationAwareOrderComparator.sort(this.viewResolvers);
		this.cnmFactoryBean.setServletContext(servletContext);
	}

分析以上代码不难发现,springboot会自动把自定制的viewResolver加入到容器。
ContentNegotiatingViewResolver类中还有resolveViewName解析视图名字的方法

public View resolveViewName(String viewName, Locale locale) throws Exception {
		RequestAttributes attrs = RequestContextHolder.getRequestAttributes();
		Assert.state(attrs instanceof ServletRequestAttributes, "No current ServletRequestAttributes");
		List<MediaType> requestedMediaTypes = getMediaTypes(((ServletRequestAttributes) attrs).getRequest());
		if (requestedMediaTypes != null) {
			List<View> candidateViews = getCandidateViews(viewName, locale, requestedMediaTypes);
			View bestView = getBestView(candidateViews, requestedMediaTypes, attrs);
			if (bestView != null) {
				return bestView;
			}
		}
		if (this.useNotAcceptableStatusCode) {
			if (logger.isDebugEnabled()) {
				logger.debug("No acceptable view found; returning 406 (Not Acceptable) status code");
			}
			return NOT_ACCEPTABLE_VIEW;
		}
		else {
			logger.debug("No acceptable view found; returning null");
			return null;
		}
	}

通过该方法解析得到bestView然后返回。

3、总结

学习springboot自动配置springmvc的时候想起了springmvc处理请求流程,所以查阅如上。还是得打好底层基础,这样处理错误,阅读源码大有裨益。如有理解不当之处,望指正。

发布了16 篇原创文章 · 获赞 0 · 访问量 417

猜你喜欢

转载自blog.csdn.net/weixin_43532415/article/details/104950450