Springmvc入门(十二)HandlerMapper源码分析--BeanNameUrlHandlerMapping的Hander的注册

根据源码十一中,可以了解到,DispatcherServlet先对请求进行检查,如果是上传请求则封装,然后根据HandlerMapper的getHandler方法找到对应的Handler。

先看DispatcherServlet中的初始化HandlerMapper,默认是加载三个HandlerMapper的实现类

    //初始化HandlerMapping
    private void initHandlerMappings(ApplicationContext context) {
		this.handlerMappings = null;

        //判断是否默认添加所有的HandlerMapping类,初始值是默认全部添加的
		//有BeanNameUrlHandlerMapping,SimpleUrlHandlerMapping,RequestMappingHandlerMapping
		if (this.detectAllHandlerMappings) {
			
			Map<String, HandlerMapping> matchingBeans =
					BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
			if (!matchingBeans.isEmpty()) {
				this.handlerMappings = new ArrayList<>(matchingBeans.values());
				// 保持HandlerMappings是有序的
				AnnotationAwareOrderComparator.sort(this.handlerMappings);
			}
		}
		 //如果不是默认添加所有的,那么就去context中找一个HandlerMapping的bean
		else {
			try {
				HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
				this.handlerMappings = Collections.singletonList(hm);
			}
			catch (NoSuchBeanDefinitionException ex) {
				
			}
		}

		//如果上面两步没有找到可以使用的handlerMapping,那么就采用默认的handlerMapping
        //默认的HandlerMapping都定义在了DispatcherServlet.properties中,大致定义了如下两个
        //org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
		//org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
		if (this.handlerMappings == null) {
			this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
			if (logger.isDebugEnabled()) {
				logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");
			}
		}
	}

以BeanNameUrlHandlerMapping为例,先看BeanNameUrlHandlerMapping是如何使用的?

先创建一个HelloWorldBeanNameHandler,实现AbstractController的handleRequestInternal方法。

    public class HelloWorldBeanNameHandler extends AbstractController{

	@Override
	protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
			throws Exception {
		System.out.println("bean  HelloWorldBeanNameHandler  world");
		ModelAndView mv = new ModelAndView();
		mv.setViewName("success");
		return mv;
	}

}

然后在springmvc配置文件中,配置这个类,类名的id为请求的路径,一个请求路径对应一个类

<bean id="/helloWorldBeanNameHandler" class="springmvc.helloworld.HelloWorldBeanNameHandler"></bean>
	

先来看看springmvc是如何加载这个bean,并让它作为请求处理类。

BeanNameUrlHandlerMapper的结构示意图

HandlerMapper只是一个接口,定义了查找Handler方法,所以从AbstractHandlerMapper开始,AbstractHandlerMapper继承WebApplicationObjectSupport方法,所以会先执行initApplicationContext方法,AbstractHandlerMapper只加载了全部的拦截器,AbstractHandlerMapper并没有获取请求的bean,也没有查找,而是由子类去实现,但是它实现了查找那些handler的方法,并且保存注册的请求路径和handler的映射关系。我们先看注册,后看查找。

    protected void initApplicationContext() throws BeansException {
		//模板方法,但目前没使用
		extendInterceptors(this.interceptors);
		detectMappedInterceptors(this.adaptedInterceptors);
		initInterceptors();
	}
	protected void detectMappedInterceptors(List<HandlerInterceptor> mappedInterceptors) {
		//将所有的MapperInterceptors类型的Bean添加到mappedInterceptors属性中
		mappedInterceptors.addAll(
				BeanFactoryUtils.beansOfTypeIncludingAncestors(
						obtainApplicationContext(), MappedInterceptor.class, true, false).values());
	}
	protected void initInterceptors() {
		//初始化Interceptor,将全部的拦截器加入adaptedInterceptors中
		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");
				}
				this.adaptedInterceptors.add(adaptInterceptor(interceptor));
			}
		}
	}

子类AbstractUrlHandlerMapping与Handler的注册无关,只是实现了查找的方法,继续看AbstractDetectingUrlHandlerMapping的initApplicationContext的方法,调用父类的initApplicationContext的方法,并且查找了ioc容器中,所有的bean,然后筛选出bean的名词和别名是/开头的

        @Override
	public void initApplicationContext() throws ApplicationContextException {
		super.initApplicationContext();
		detectHandlers();
	}
        protected void detectHandlers() throws BeansException {
		//ioc容器
		ApplicationContext applicationContext = obtainApplicationContext();
		if (logger.isDebugEnabled()) {
			logger.debug("Looking for URL mappings in application context: " + applicationContext);
		}
		//获取ioc容器中所有的bean的名字
		String[] beanNames = (this.detectHandlersInAncestorContexts ?
				BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext, Object.class) :
				applicationContext.getBeanNamesForType(Object.class));
		//遍历bean的名字,将名字匹配的放入父类中的
		for (String beanName : beanNames) {
			//获取路径集合,模板方法,由子类实现
			String[] urls = determineUrlsForHandler(beanName);
			if (!ObjectUtils.isEmpty(urls)) {
				//根据路径和bean的名字进行注册,即url --> bean作为map的映射,调用父类的方法
				//因为一个类可以映射多个路径
				registerHandler(urls, beanName);
			}
			else {
				if (logger.isDebugEnabled()) {
					logger.debug("Rejected bean name '" + beanName + "': no URL paths identified");
				}
			}
		}
	}

主角BeanNameUrlHandlerMaper强势登场,因为父类AbstractUrlHandlerMapping采用了determineUrlsForHandler这个模板方法,由子类去实现,然后筛选出所有的以/开头的bean,然后调用父类AbstractUrlHandlerMapping的registerHandler方法,将路径和bean放入AbstractUrlHandlerMapping父类的HashMap中。

        @Override
	protected String[] determineUrlsForHandler(String beanName) {
		List<String> urls = new ArrayList<>();
		//所有的bean都要是/开头,才是springmvc的handler,不然就是普通的一个对象
		if (beanName.startsWith("/")) {
			urls.add(beanName);
		}
		//获取类的别名的是/开头的
		String[] aliases = obtainApplicationContext().getAliases(beanName);
		for (String alias : aliases) {
			if (alias.startsWith("/")) {
				urls.add(alias);
			}
		}
		return StringUtils.toStringArray(urls);
	}

AbstractUrlHandlerMapping中的registerHandler注册方法,分别处理/,/*以及普通的请求路径

    //遍历url,将bean进行注册
	protected void registerHandler(String[] urlPaths, String beanName) throws BeansException, IllegalStateException {
		Assert.notNull(urlPaths, "URL path array must not be null");
		for (String urlPath : urlPaths) {
			registerHandler(urlPath, beanName);
		}
	}

    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;

		//判断当前的handler是否已经存在,目的是为了创建单例的handler,所以controller中的类都是单例的
		if (!this.lazyInitHandlers && handler instanceof String) {
			String handlerName = (String) handler;
			ApplicationContext applicationContext = obtainApplicationContext();
			if (applicationContext.isSingleton(handlerName)) {
				resolvedHandler = applicationContext.getBean(handlerName);
			}
		}
		//判断此路径的mapper是否已经存在,存在则判断是否是同一个对象,是则报错,一个路径对应多个bean实例
		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 {
			//路径假设是/,则用rootHandler
			if (urlPath.equals("/")) {
				if (logger.isInfoEnabled()) {
					logger.info("Root mapping to " + getHandlerDescription(handler));
				}
				setRootHandler(resolvedHandler);
			}
			//假设路径是/*,则用父类中默认的defaultHandler,
			else if (urlPath.equals("/*")) {
				if (logger.isInfoEnabled()) {
					logger.info("Default mapping to " + getHandlerDescription(handler));
				}
				setDefaultHandler(resolvedHandler);
			}
			//假设路径是/hello这种,则将路径和handler传入handlerMap中
			//路径为key,handler为value
			else {
				this.handlerMap.put(urlPath, resolvedHandler);
				if (logger.isInfoEnabled()) {
					logger.info("Mapped URL path [" + urlPath + "] onto " + getHandlerDescription(handler));
				}
			}
		}
	}

到这里BeanNameUrlHandlerMapping的Handler的注册就完成了,总结下BeanNameUrlHandlerMapping其实就是把配置的bean中的名字或者别名以/开头的,将这些以及它的bean保存在一个HashMap中,这里采用了模板方法,让父类调用子类的方法,添加更加详细的内容,也结合了继承,继承父类的方法的同时,并且给父类增加额外的功能,给父类属性增加内容。

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值