• Spring MVC之HandlerMap 初始化


         DispatcherServlet请求处理请求的过程中,会发现getHandler实际上是调用AbstractUrlHandlerMapping.getHandlerInternal()。 通过对该段代码进行走读后发现,是通过handlermap.get(urlPath)获取匹配的handler的,那么该handlerMap是在什么时候进行初始化的呢?

       通过DefaultAnnotationHandlerMapping的继承关系可以去分析初始化的过程。



     

    通过继承关系可以知道DefaultAnnotationHandlerMapping实现了ApplicationContextAware接口,这个接口的主要功能就是在Application初始化完成后会条用该接扩的setApplicationContext()方法。



     

    AbstractDetectingUrlHandlerMapping是HandlerMapping初始化的关键入口,在初始化方法中首先调用父类的初始化方法,然后查找处理器。这个抽象类使用了模板方法模式,将具体的determineUrlsForHanlder方法延迟到子类中实现。
    /**
     * Abstract implementation of the {@link org.springframework.web.servlet.HandlerMapping}
     * interface, detecting URL mappings for handler beans through introspection of all
     * defined beans in the application context.
     *
     * @author Juergen Hoeller
     * @since 2.5
     * @see #determineUrlsForHandler
     */
    public abstract class AbstractDetectingUrlHandlerMapping extends AbstractUrlHandlerMapping {
    
    	private boolean detectHandlersInAncestorContexts = false;
    
    
    	/**
    	 * Set whether to detect handler beans in ancestor ApplicationContexts.
    	 * <p>Default is "false": Only handler beans in the current ApplicationContext
    	 * will be detected, i.e. only in the context that this HandlerMapping itself
    	 * is defined in (typically the current DispatcherServlet's context).
    	 * <p>Switch this flag on to detect handler beans in ancestor contexts
    	 * (typically the Spring root WebApplicationContext) as well.
    	 */
    	public void setDetectHandlersInAncestorContexts(boolean detectHandlersInAncestorContexts) {
    		this.detectHandlersInAncestorContexts = detectHandlersInAncestorContexts;
    	}
    
    
    	/**
    	 *在ApplicatonObjectSupport的setApplicationContext()方法中调用该方法
    	 * Calls the {@link #detectHandlers()} method in addition to the
    	 * superclass's initialization.
    	 */
    	@Override
    	public void initApplicationContext() throws ApplicationContextException {
    		// 调用父类的initApplicationContext()方法
    		super.initApplicationContext();
    		//查找Handler
    		detectHandlers();
    	}
    
    	/**
    	 * 注册所有能够从应用上下文中找到的handler
    	 * Register all handlers found in the current ApplicationContext.
    	 * <p>The actual URL determination for a handler is up to the concrete
    	 * {@link #determineUrlsForHandler(String)} implementation. A bean for
    	 * which no such URLs could be determined is simply not considered a handler.
    	 * @throws org.springframework.beans.BeansException if the handler couldn't be registered
    	 * @see #determineUrlsForHandler(String)
    	 */
    	protected void detectHandlers() throws BeansException {
    		if (logger.isDebugEnabled()) {
    			logger.debug("Looking for URL mappings in application context: " + getApplicationContext());
    		}
    		// 从上下文中获取所有的beanNames
    		String[] beanNames = (this.detectHandlersInAncestorContexts ?
    				BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
    				getApplicationContext().getBeanNamesForType(Object.class));
    
    		// 遍历所有BeanNames
    		for (String beanName : beanNames) {
    			//调用子类实现方法,判定这个bean 处理的url
    			String[] urls = determineUrlsForHandler(beanName);
    			// 如果这个bean可以处理url则将调用父类的registerHandler方法完成 url和handler映射关系的注册
    			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");
    				}
    			}
    		}
    	}
    
    
    	/**
    	 * Determine the URLs for the given handler bean.
    	 * @param beanName the name of the candidate bean
    	 * @return the URLs determined for the bean,
    	 * or <code>null</code> or an empty array if none
    	 */
    	protected abstract String[] determineUrlsForHandler(String beanName);
    
    }
     DefaultAnnotationHandlerMapping继承了AbstractDetectingUrlHandlerMapping,实现了父类的抽象方法,
    首先查找这个类是否有RequestMapping的注解以及这个Bean中所有方法是否有这个注解
    public class DefaultAnnotationHandlerMapping extends AbstractDetectingUrlHandlerMapping {
    
    	static final String USE_DEFAULT_SUFFIX_PATTERN = DefaultAnnotationHandlerMapping.class.getName() + ".useDefaultSuffixPattern";
    
    	private boolean useDefaultSuffixPattern = true;
    
    	private final Map<Class<?>, RequestMapping> cachedMappings = new HashMap<Class<?>, RequestMapping>();
    
    
    	/**
    	 * Set whether to register paths using the default suffix pattern as well:
    	 * i.e. whether "/users" should be registered as "/users.*" and "/users/" too.
    	 * <p>Default is "true". Turn this convention off if you intend to interpret
    	 * your <code>@RequestMapping</code> paths strictly.
    	 * <p>Note that paths which include a ".xxx" suffix or end with "/" already will not be
    	 * transformed using the default suffix pattern in any case.
    	 */
    	public void setUseDefaultSuffixPattern(boolean useDefaultSuffixPattern) {
    		this.useDefaultSuffixPattern = useDefaultSuffixPattern;
    	}
    
    
    	/**
    	 * Checks for presence of the {@link org.springframework.web.bind.annotation.RequestMapping}
    	 * annotation on the handler class and on any of its methods.
    	 */
    	@Override
    	protected String[] determineUrlsForHandler(String beanName) {
    		ApplicationContext context = getApplicationContext();
    		Class<?> handlerType = context.getType(beanName);
    		RequestMapping mapping = context.findAnnotationOnBean(beanName, RequestMapping.class);
    		if (mapping != null) {
    			// @RequestMapping found at type level
    			this.cachedMappings.put(handlerType, mapping);
    			Set<String> urls = new LinkedHashSet<String>();
    			String[] typeLevelPatterns = mapping.value();
    			if (typeLevelPatterns.length > 0) {
    				// @RequestMapping specifies paths at type level
    				String[] methodLevelPatterns = determineUrlsForHandlerMethods(handlerType, true);
    				for (String typeLevelPattern : typeLevelPatterns) {
    					if (!typeLevelPattern.startsWith("/")) {
    						typeLevelPattern = "/" + typeLevelPattern;
    					}
    					boolean hasEmptyMethodLevelMappings = false;
    					for (String methodLevelPattern : methodLevelPatterns) {
    						if (methodLevelPattern == null) {
    							hasEmptyMethodLevelMappings = true;
    						}
    						else {
    							String combinedPattern = getPathMatcher().combine(typeLevelPattern, methodLevelPattern);
    							addUrlsForPath(urls, combinedPattern);
    						}
    					}
    					if (hasEmptyMethodLevelMappings ||
    							org.springframework.web.servlet.mvc.Controller.class.isAssignableFrom(handlerType)) {
    						addUrlsForPath(urls, typeLevelPattern);
    					}
    				}
    				return StringUtils.toStringArray(urls);
    			}
    			else {
    				// actual paths specified by @RequestMapping at method level
    				return determineUrlsForHandlerMethods(handlerType, false);
    			}
    		}
    		else if (AnnotationUtils.findAnnotation(handlerType, Controller.class) != null) {
    			// @RequestMapping to be introspected at method level
    			return determineUrlsForHandlerMethods(handlerType, false);
    		}
    		else {
    			return null;
    		}
    	}
    
    	/**
    	 * Derive URL mappings from the handler's method-level mappings.
    	 * @param handlerType the handler type to introspect
    	 * @param hasTypeLevelMapping whether the method-level mappings are nested
    	 * within a type-level mapping
    	 * @return the array of mapped URLs
    	 */
    	protected String[] determineUrlsForHandlerMethods(Class<?> handlerType, final boolean hasTypeLevelMapping) {
    		//在该类中返回值一直是null
    		String[] subclassResult = determineUrlsForHandlerMethods(handlerType);
    		if (subclassResult != null) {
    			return subclassResult;
    		}
    		// 创建一个set,之所以使用set就是set中不会有重复数据
    		final Set<String> urls = new LinkedHashSet<String>();
    		Set<Class<?>> handlerTypes = new LinkedHashSet<Class<?>>();
    		//添加类以及所有接口
    		handlerTypes.add(handlerType);
    		handlerTypes.addAll(Arrays.asList(handlerType.getInterfaces()));
    		// 遍历handlerTypes
    		for (Class<?> currentHandlerType : handlerTypes) {
    		// 反射处理
    			ReflectionUtils.doWithMethods(currentHandlerType, new ReflectionUtils.MethodCallback() {
    				public void doWith(Method method) {
    				   // 获取所有该方法上的RequestMapping注解
    					RequestMapping mapping = AnnotationUtils.findAnnotation(method, RequestMapping.class);
    					// 如果存在RequestMapping注解
    					if (mapping != null) {
    						// 获取注解 value属性值
    						String[] mappedPatterns = mapping.value();
    						// 如果value的值不为空
    						if (mappedPatterns.length > 0) {
    							// 遍历mappedPatterns
    							for (String mappedPattern : mappedPatterns) {
    							    //如果hasTypeLevelMapping=false,同时mappedPattern不是以"/"开头则在其前面加上"/"
    								if (!hasTypeLevelMapping && !mappedPattern.startsWith("/")) {
    									mappedPattern = "/" + mappedPattern;
    								}
    								//将url添加在set中
    								addUrlsForPath(urls, mappedPattern);
    							}
    						}
    						else if (hasTypeLevelMapping) {
    							// empty method-level RequestMapping
    							urls.add(null);
    						}
    					}
    				}
    			}, ReflectionUtils.USER_DECLARED_METHODS);
    		}
    		return StringUtils.toStringArray(urls);
    	}
    
    	/**
    	 * Derive URL mappings from the handler's method-level mappings.
    	 * @param handlerType the handler type to introspect
    	 * @return the array of mapped URLs
    	 */
    	protected String[] determineUrlsForHandlerMethods(Class<?> handlerType) {
    		return null;
    	}
    
    	/**
    	 * Add URLs and/or URL patterns for the given path.
    	 * @param urls the Set of URLs for the current bean
    	 * @param path the currently introspected path
    	 */
    	protected void addUrlsForPath(Set<String> urls, String path) {
    		urls.add(path);
    		if (this.useDefaultSuffixPattern && path.indexOf('.') == -1 && !path.endsWith("/")) {
    			urls.add(path + ".*");
    			urls.add(path + "/");
    		}
    	}
    
    
    	/**
    	 * Validate the given annotated handler against the current request.
    	 * @see #validateMapping
    	 */
    	@Override
    	protected void validateHandler(Object handler, HttpServletRequest request) throws Exception {
    		RequestMapping mapping = this.cachedMappings.get(handler.getClass());
    		if (mapping == null) {
    			mapping = AnnotationUtils.findAnnotation(handler.getClass(), RequestMapping.class);
    		}
    		if (mapping != null) {
    			validateMapping(mapping, request);
    		}
    		request.setAttribute(USE_DEFAULT_SUFFIX_PATTERN, this.useDefaultSuffixPattern);
    	}
    
    	/**
    	 * Validate the given type-level mapping metadata against the current request,
    	 * checking HTTP request method and parameter conditions.
    	 * @param mapping the mapping metadata to validate
    	 * @param request current HTTP request
    	 * @throws Exception if validation failed
    	 */
    	protected void validateMapping(RequestMapping mapping, HttpServletRequest request) throws Exception {
    		RequestMethod[] mappedMethods = mapping.method();
    		if (!ServletAnnotationMappingUtils.checkRequestMethod(mappedMethods, request)) {
    			String[] supportedMethods = new String[mappedMethods.length];
    			for (int i = 0; i < mappedMethods.length; i++) {
    				supportedMethods[i] = mappedMethods[i].name();
    			}
    			throw new HttpRequestMethodNotSupportedException(request.getMethod(), supportedMethods);
    		}
    
    		String[] mappedParams = mapping.params();
    		if (!ServletAnnotationMappingUtils.checkParameters(mappedParams, request)) {
    			throw new UnsatisfiedServletRequestParameterException(mappedParams, request.getParameterMap());
    		}
    
    		String[] mappedHeaders = mapping.headers();
    		if (!ServletAnnotationMappingUtils.checkHeaders(mappedHeaders, request)) {
    			throw new ServletRequestBindingException("Header conditions "" +
    					StringUtils.arrayToDelimitedString(mappedHeaders, ", ") +
    					"" not met for actual request");
    		}
    	}
    
    	@Override
    	protected boolean supportsTypeLevelMappings() {
    		return true;
    	}
    }
    
     
    public abstract class WebApplicationObjectSupport extends ApplicationObjectSupport
    		implements ServletContextAware {
    
    	private ServletContext servletContext;
    
    	//由于该类实现了ServletContextAware接口,在ServletContext初始化完成后,调用该方法
    	public final void setServletContext(ServletContext servletContext) {
    		if (servletContext != this.servletContext) {
    			this.servletContext = servletContext;
    			if (servletContext != null) {
    				initServletContext(servletContext);
    			}
    		}
    	}
    
    	@Override
    	protected boolean isContextRequired() {
    		return true;
    	}
    
    	/**
    	 * 在ApplicationContext初始完成后调用该方法,如果已用上下文是WebApplicationContext则调用initServeltContext
    	 */
    	@Override
    	protected void initApplicationContext(ApplicationContext context) {
    		// 调用父类initApplicationContext,也就是调用ApplicationObjectSupport.initApplictionContext()方法
    		super.initApplicationContext(context);
    		if (this.servletContext == null && context instanceof WebApplicationContext) {
    			this.servletContext = ((WebApplicationContext) context).getServletContext();
    			if (this.servletContext != null) {
    				// 模板方法模式,具体实现延迟到子类中进行
    				initServletContext(this.servletContext);
    			}
    		}
    	}
    
    	/**
    	 * Subclasses may override this for custom initialization based
    	 * on the ServletContext that this application object runs in.
    	 * <p>The default implementation is empty. Called by
    	 * {@link #initApplicationContext(org.springframework.context.ApplicationContext)}
    	 * as well as {@link #setServletContext(javax.servlet.ServletContext)}.
    	 * @param servletContext the ServletContext that this application object runs in
    	 * (never <code>null</code>)
    	 */
    	protected void initServletContext(ServletContext servletContext) {
    	}

  • 相关阅读:
    php解决与处理网站高并发 大流量访问的方法
    mysql事务和锁InnoDB
    从一个死锁看mysql innodb的锁机制
    Git如何删除自己创建的项目
    公众号的坑
    字符串转Unicode码
    字符串转UTF-8码(%开头)
    git介绍和使用
    ng2中文文档地址
    两个数组的排序方法
  • 原文地址:https://www.cnblogs.com/wei-zw/p/8797807.html
Copyright © 2020-2023  润新知