SpringMVC执行过程-HandlerMapping初始化&handlerMap初始化

基础环境:  spring-boot :2.3.3.RELEASE、jdk1.8

0. 背景:

 通过以下方式可以自定义视图,即访问/testView会跳转success.html页面
 那么这样设置的原理是什么?本文就是探讨这个问题

@Configuration
public class MyConfigure implements WebMvcConfigurer {
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/testView").setViewName("success");
    }
}
1. 入口方法:

 springboot启动类启动后,会自动配置mvc的组件,即WebMvcAutoConfiguration,
 在配置的时候会自动配置适配器,即WebMvcAutoConfigurationAdapter,
 该类在配置的时候会import一个组件,即EnableWebMvcConfiguration,

	@Configuration(proxyBeanMethods = false)
	@Import(EnableWebMvcConfiguration.class)
	@EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class })
	@Order(0)
	public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {

  EnableWebMvcConfiguration继承DelegatingWebMvcConfiguration组件,该组件继承了WebMvcConfigurationSupport,

	@Configuration(proxyBeanMethods = false)
	public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware {
	@Configuration(proxyBeanMethods = false)
	public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {

 在该类中,注入了一个bean,即viewControllerHandlerMapping

 */
public class WebMvcConfigurationSupport implements ApplicationContextAware, ServletContextAware {
	... ... ...
	@Bean
	@Nullable
	public HandlerMapping viewControllerHandlerMapping(
			@Qualifier("mvcPathMatcher") PathMatcher pathMatcher,
			@Qualifier("mvcUrlPathHelper") UrlPathHelper urlPathHelper,
			@Qualifier("mvcConversionService") FormattingConversionService conversionService,
			@Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {
		ViewControllerRegistry registry = new ViewControllerRegistry(this.applicationContext);
		addViewControllers(registry);

		AbstractHandlerMapping handlerMapping = registry.buildHandlerMapping();
		if (handlerMapping == null) {
			return null;
		}
		handlerMapping.setPathMatcher(pathMatcher);
		handlerMapping.setUrlPathHelper(urlPathHelper);
		handlerMapping.setInterceptors(getInterceptors(conversionService, resourceUrlProvider));
		handlerMapping.setCorsConfigurations(getCorsConfigurations());
		return handlerMapping;
	}

 也就是说,启动后会进入视图控制处理器映射器viewControllerHandlerMapping进行初始化
 重点关注addViewControllers(registry);registry.buildHandlerMapping();

2. registrations的初始化:

addViewControllers(registry);方法调用后会进入自定义配置类(上文背景的代码)中addViewControllers方法,
 最后会进入ViewControllerRegistry类中addViewControllers方法,进行registrations的初始化

public class ViewControllerRegistry {
	... ... ...
	private final List<ViewControllerRegistration> registrations = new ArrayList<>(4);
	... ... ...
	public ViewControllerRegistration addViewController(String urlPath) {
		ViewControllerRegistration registration = new ViewControllerRegistration(urlPath);
		registration.setApplicationContext(this.applicationContext);
		this.registrations.add(registration);
		return registration;
	}
2-1. 初始化handle:

 在自定义配置类中registry.addViewController("/testView").setViewName(“success”);
 调用setViewName后会初始化ParameterizableViewController,此为mvc执行流程中的处理器handle

public class ViewControllerRegistration {

	private final String urlPath;

	private final ParameterizableViewController controller = new ParameterizableViewController();
	... ... ... 
	public ViewControllerRegistration(String urlPath) {
		Assert.notNull(urlPath, "'urlPath' is required.");
		this.urlPath = urlPath;
	}
	p
	ublic void setViewName(String viewName) {
		this.controller.setViewName(viewName);
	}
public class ParameterizableViewController extends AbstractController {

	@Nullable
	private Object view;
	... ... ...
	public void setViewName(@Nullable String viewName) {
		this.view = viewName;
	}
3. HandlerMapping的初始化:

 处理器映射器HandlerMapping,默认使用的是简单的url处理器映射器SimpleUrlHandlerMapping
 该处理器映射器的父类将请求url和handle进行key-value储存,以便后续根据请求url直接获取处理器handle

 在上述1中执行registry.buildHandlerMapping();进入方法,从registrations中获取自定义配置的registration,然后进行url-handle的储存。
  `

&emsp;urlMap.put(registration.getUrlPath(), registration.getViewController());

public class ViewControllerRegistry {
	... ...
	private final List<ViewControllerRegistration> registrations = new ArrayList<>(4);

	private int order = 1;
	... ... ... 
	@Nullable
	protected SimpleUrlHandlerMapping buildHandlerMapping() {
		if (this.registrations.isEmpty() && this.redirectRegistrations.isEmpty()) {
			return null;
		}

		Map<String, Object> urlMap = new LinkedHashMap<>();
		for (ViewControllerRegistration registration : this.registrations) {
			urlMap.put(registration.getUrlPath(), registration.getViewController());
		}
		for (RedirectViewControllerRegistration registration : this.redirectRegistrations) {
			urlMap.put(registration.getUrlPath(), registration.getViewController());
		}

		return new SimpleUrlHandlerMapping(urlMap, this.order);
	}

 将该urlMap传入SimpleUrlHandlerMapping构造进行SimpleUrlHandlerMapping初始化urlMap

public class SimpleUrlHandlerMapping extends AbstractUrlHandlerMapping {

	private final Map<String, Object> urlMap = new LinkedHashMap<>();
	... ... ...
	public SimpleUrlHandlerMapping(Map<String, ?> urlMap, int order) {
		setUrlMap(urlMap);
		setOrder(order);
	}

	public void setUrlMap(Map<String, ?> urlMap) {
		this.urlMap.putAll(urlMap);
	}
3-1. 注册储存url-handle:

 在initApplicationContext时,会调用registerHandlers(this.urlMap);
 然后调用抽象父类里的实现方法registerHandler进行handle储存
 initApplicationContext启动的时候就会自动调用,在SimpleUrlHandlerMapping初始化urlMap之后,具体为什么,没有探究

public class SimpleUrlHandlerMapping extends AbstractUrlHandlerMapping {

	private final Map<String, Object> urlMap = new LinkedHashMap<>();
	... ... ...
	public void initApplicationContext() throws BeansException {
		super.initApplicationContext();
		registerHandlers(this.urlMap);
	}

	protected void registerHandlers(Map<String, Object> urlMap) throws BeansException {
		if (urlMap.isEmpty()) {
			logger.trace("No patterns in " + formatMappingName());
		}
		else {
			urlMap.forEach((url, handler) -> {
				// Prepend with slash if not already present.
				if (!url.startsWith("/")) {
					url = "/" + url;
				}
				// Remove whitespace from handler bean name.
				if (handler instanceof String) {
					handler = ((String) handler).trim();
				}
				registerHandler(url, handler);
			});
			if (logger.isDebugEnabled()) {
	... ... ... ... ...

抽象父类AbstractUrlHandlerMapping中registerHandler的实现,对handlerMap储存

public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping implements MatchableHandlerMapping {
	... ... ...
	private final Map<String, Object> handlerMap = new LinkedHashMap<>();
	... ... ...
	protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {
		Assert.notNull(urlPath, "URL path must not be null");
		Assert.notNull(handler, "Handler object must not be null");
		Object resolvedHandler = handler;
		... ... ... 
		Object mappedHandler = this.handlerMap.get(urlPath);
		if (mappedHandler != null) {
			if (mappedHandler != resolvedHandler) {
				throw new IllegalStateException(
						"Cannot map " + getHandlerDescription(handler) + " to URL path [" + urlPath +
						"]: There is already " + getHandlerDescription(mappedHandler) + " mapped.");
			}
		}
		else {
			if (urlPath.equals("/")) {
				if (logger.isTraceEnabled()) {
					logger.trace("Root mapping to " + getHandlerDescription(handler));
				}
				setRootHandler(resolvedHandler);
			}
			else if (urlPath.equals("/*")) {
				if (logger.isTraceEnabled()) {
					logger.trace("Default mapping to " + getHandlerDescription(handler));
				}
				setDefaultHandler(resolvedHandler);
			}
			else {
				//储存url-handle
				this.handlerMap.put(urlPath, resolvedHandler);
			... ... ...
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值