SpringFramework之HandlerMapping的倆個默認實現類的初始化

注:SpringFramework的版本是4.3.x。java

1.HandlerMapping的倆個默認實現類

    們由DispatcherServlet的初始化簡析得知默認的HandlerMapping是BeanNameUrlHandlerMapping和DefaultAnnotationHandlerMapping,這倆個類的繼承圖以下圖二、圖3所示,web

                

                                               圖2 BeanNameUrlHandlerMapping的類繼承圖spring

                 

                                             圖3 DefaultAnnotationHandlerMapping的類繼承圖mvc

    BeanNameUrlHandlerMapping是spring-webmvc模塊的,DefaultAnnotationHandlerMapping是spring-webmvc-porlet的。咱們主要分析這倆個HandlerMapping。app

    BeanNameUrlHandlerMapping的用法以下,ide

<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
<bean name="/hello" class="com.mjduan.project.example8_aop.HelloController"/>

    HelloController的源碼以下,this

public class HelloController extends AbstractController {
    @Override
    protected ModelAndView handleRequestInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
        return null;
    }
}

    BeanNameUrlHandlerMapping初始化的時序圖,以下圖4所示:url

                

                                           圖4 BeanNameUrlHandlerMapping的初始化時序圖spa

    圖3的說明:因爲ApplicationObjectSupport實現了ApplicationContextAware接口,全部在執行setApplicationContext的時候會初始化AbstractUrlHandlerMapping的屬性handlerMap。.net

    圖4的步驟6中,會從applicationContext中取出全部的MappedInterceptor,放到AbstractHandlerMapping的屬性adaptedInterceptors中,這些MappedInterceptor是HandlerInterceptor的子類,在構造HandlerExecutionChain時用到。

    咱們再來分析AbstractDetectingUrlHandlerMapping的detectHandlers方法,源碼以下List-1所示,

    List-1 AbstractDetectingUrlHandlerMapping的detectHandlers()源碼

protected void detectHandlers() throws BeansException {
	if (logger.isDebugEnabled()) {
		logger.debug("Looking for URL mappings in application context: " + getApplicationContext());
	}
	String[] beanNames = (this.detectHandlersInAncestorContexts ?
			BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
			getApplicationContext().getBeanNamesForType(Object.class));

	// Take any bean name that we can determine URLs for.
	for (String beanName : beanNames) {
		String[] urls = determineUrlsForHandler(beanName);
		if (!ObjectUtils.isEmpty(urls)) {
			// URL paths found: Let's consider it a handler.
			registerHandler(urls, beanName);
		}
		else {
			if (logger.isDebugEnabled()) {
				logger.debug("Rejected bean name '" + beanName + "': no URL paths identified");
			}
		}
	}
}

        determineUrlsForHandler方法的實現是在BeanNameUrlHandlerMapping中,源碼以下List-2所示,

    List-2 BeanNameUrlHandlerMapping的determineUrlsForHandler方法源碼

public class BeanNameUrlHandlerMapping extends AbstractDetectingUrlHandlerMapping {

	/**
	 * Checks name and aliases of the given bean for URLs, starting with "/".
	 */
	@Override
	protected String[] determineUrlsForHandler(String beanName) {
		List<String> urls = new ArrayList<String>();
		if (beanName.startsWith("/")) {
			urls.add(beanName);
		}
		String[] aliases = getApplicationContext().getAliases(beanName);
		for (String alias : aliases) {
			if (alias.startsWith("/")) {
				urls.add(alias);
			}
		}
		return StringUtils.toStringArray(urls);
	}
}

    由List-1的代碼可知,從applicationContext中取出全部的beanName,以後遍歷全部的beanName,若是該beanName以"/"開頭,則將這個beanName視爲url,記錄。

    通常狀況下,咱們不會使用BeanNameUrlHandlerMapping的,BeanNameUrlHandlerMapping使用起來感受不是很靈活。

3.SimpleUrlHandlerMapping的用法

    SimpleUrlHandlerMapping的通常使用方式以下,prop中key的值,是spring bean。這種是之前的用法,如今基本都使用註解的方式了,不多用這種了。注:下面這段代碼來源: https://blog.csdn.net/trigl/article/details/50494492

<bean
    class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
    <property name="mappings">
        <props>
            <prop key="/upfile.do">upfile</prop>
            <prop key="/upfiles.do">upfiles</prop>
            <prop key="/extjs.do">SpringMVC</prop>
            <prop key="/show.do">show</prop>
        </props>
    </property>
</bean>

4.DefaultAnnotationHandlerMapping的分析

    List-3 DefaultAnnotationHandlerMapping的initApplicationContext()源碼

@Override
public void initApplicationContext() throws BeansException {
	super.initApplicationContext();
	detectHandlers();
}

/**
 * Register all handlers specified in the Portlet mode map for the corresponding modes.
 * @throws org.springframework.beans.BeansException if the handler couldn't be registered
 */
protected void detectHandlers() throws BeansException {
	ApplicationContext context = getApplicationContext();
	String[] beanNames = context.getBeanNamesForType(Object.class);
	for (String beanName : beanNames) {
		Class<?> handlerType = context.getType(beanName);
		RequestMapping mapping = context.findAnnotationOnBean(beanName, RequestMapping.class);
		if (mapping != null) {
			// @RequestMapping found at type level
			String[] modeKeys = mapping.value();
			String[] params = mapping.params();
			boolean registerHandlerType = true;
			if (modeKeys.length == 0 || params.length == 0) {
				registerHandlerType = !detectHandlerMethods(handlerType, beanName, mapping);
			}
			if (registerHandlerType) {
				AbstractParameterMappingPredicate predicate = new TypeLevelMappingPredicate(
						params, mapping.headers(), mapping.method());
				for (String modeKey : modeKeys) {
					registerHandler(new PortletMode(modeKey), beanName, predicate);
				}
			}
		}
		else if (AnnotationUtils.findAnnotation(handlerType, Controller.class) != null) {
			detectHandlerMethods(handlerType, beanName, mapping);
		}
	}
}

    從List-3中的detectHandlers可知,

  •     先獲取applicationContext中全部的beanName,然後獲取其對應的Class<?>,判斷類上是否有RequestMapping註解,有的話,進行解析,以後registerHandler。
  •     若類上沒有找到RequestMapping註解,則判斷類上是否有Controller註解,若是有,那麼執行detectHandlerMethods,這個方法的源碼有點多,我只是給出部分,以下List-4所示:

    List-4 DefaultAnnotationHandlerMapping的detectHandlerMethods方法源碼

protected boolean detectHandlerMethods(Class<?> handlerType, final String beanName, final RequestMapping typeMapping) {
	final Set<Boolean> handlersRegistered = new HashSet<Boolean>(1);
	Set<Class<?>> handlerTypes = new LinkedHashSet<Class<?>>();
	handlerTypes.add(handlerType);
	handlerTypes.addAll(Arrays.asList(handlerType.getInterfaces()));
	for (Class<?> currentHandlerType : handlerTypes) {
		ReflectionUtils.doWithMethods(currentHandlerType, new ReflectionUtils.MethodCallback() {
			@Override
			public void doWith(Method method) {
				PortletRequestMappingPredicate predicate = null;
				String[] modeKeys = new String[0];
				String[] params = new String[0];
				if (typeMapping != null) {
					params = PortletAnnotationMappingUtils.mergeStringArrays(typeMapping.params(), params);
				}
				ActionMapping actionMapping = AnnotationUtils.findAnnotation(method, ActionMapping.class);
				RenderMapping renderMapping = AnnotationUtils.findAnnotation(method, RenderMapping.class);
				ResourceMapping resourceMapping = AnnotationUtils.findAnnotation(method, ResourceMapping.class);
				EventMapping eventMapping = AnnotationUtils.findAnnotation(method, EventMapping.class);
				RequestMapping requestMapping = AnnotationUtils.findAnnotation(method, RequestMapping.class);
				if (actionMapping != null) {
					params = PortletAnnotationMappingUtils.mergeStringArrays(params, actionMapping.params());
					predicate = new ActionMappingPredicate(actionMapping.name(), params);
				}
   ......

    從List-4可知,detectHandlerMethods方法,對類的方法進行遍歷,以後逐個處理每一個方法。

    DefaultAnnotationHandlerMapping處理的就是咱們平時所用的基於註解的方式。

相關文章
相關標籤/搜索