SpringMVC源码解读

AbstractDetectingUrlHandlerMapping是通过扫描方式注册Handler,收到请求时由AbstractUrlHandlerMapping的getHandlerInternal进行分发.

共有5个子类,一个抽象类.

与SimpleUrlHandlerMapping类似,通过覆写initApplicationContext,然后调用detectHandlers进行初始化.

detectHandlers通过BeanFactoryUtils扫描应用下的Object,然后预留determineUrlsForHandler给子类根据Handler生成对应的url.

注册使用的registerHandler依然由AbstractUrlHandlerMapping提供.

// AbstractDetectingUrlHandlerMapping

1     /**
2      * Calls the {@link #detectHandlers()} method in addition to the
3      * superclass's initialization.
4      */
5     @Override
6     public void initApplicationContext() throws ApplicationContextException {
7         super.initApplicationContext();
8         detectHandlers();
9     }

这边一样是调用AbstractHandlerMapping的initApplicationContext初始化拦截器.

主角上场,detectHandlers,扫描Handlers

// AbstractDetectingUrlHandlerMapping

 1     /**
 2      * Register all handlers found in the current ApplicationContext.
 3      * <p>The actual URL determination for a handler is up to the concrete
 4      * {@link #determineUrlsForHandler(String)} implementation. A bean for
 5      * which no such URLs could be determined is simply not considered a handler.
 6      * @throws org.springframework.beans.BeansException if the handler couldn't be registered
 7      * @see #determineUrlsForHandler(String)
 8      */
 9     protected void detectHandlers() throws BeansException {
10         if (logger.isDebugEnabled()) {
11             logger.debug("Looking for URL mappings in application context: " + getApplicationContext());
12         }
13         String[] beanNames = (this.detectHandlersInAncestorContexts ?
14                 BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
15                 getApplicationContext().getBeanNamesForType(Object.class));
16 
17         // Take any bean name that we can determine URLs for.
18         for (String beanName : beanNames) {
19             String[] urls = determineUrlsForHandler(beanName);
20             if (!ObjectUtils.isEmpty(urls)) {
21                 // URL paths found: Let's consider it a handler.
22                 registerHandler(urls, beanName);
23             }
24             else {
25                 if (logger.isDebugEnabled()) {
26                     logger.debug("Rejected bean name '" + beanName + "': no URL paths identified");
27                 }
28             }
29         }
30     }

这边预留的模板方法定义如下:

1     /**
2      * Determine the URLs for the given handler bean.
3      * @param beanName the name of the candidate bean
4      * @return the URLs determined for the bean,
5      * or {@code null} or an empty array if none
6      */
7     protected abstract String[] determineUrlsForHandler(String beanName);

我们再来看看模板方法在BeanNameUrlHandlerMapping和AbstractControllerUrlHandlerMapping中的实现吧.

BeanNameUrlHandlerMapping非常简单,就实现了determineUrlsForHandler.

其中的alias应该是应该就是通过beanName在配置文件中配置的.

// BeanNameUrlHandlerMapping

 1     /**
 2      * Checks name and aliases of the given bean for URLs, starting with "/".
 3      */
 4     @Override
 5     protected String[] determineUrlsForHandler(String beanName) {
 6         List<String> urls = new ArrayList<String>();
 7         if (beanName.startsWith("/")) {
 8             urls.add(beanName);
 9         }
10         String[] aliases = getApplicationContext().getAliases(beanName);
11         for (String alias : aliases) {
12             if (alias.startsWith("/")) {
13                 urls.add(alias);
14             }
15         }
16         return StringUtils.toStringArray(urls);
17     }

再来看看AbstractControllerUrlHandlerMapping中的实现

  isEligibleForMapping判断controller是否被排除在外(通过包package排除或类class排除).

  buildUrlsForHandler由子类实现具体的url生成规则

  isControllerType判断是否Controller的子类

  buildUrlsForHandler预留给子类生产url的模板方法.

// AbstractControllerUrlHandlerMapping

 1     /**
 2      * This implementation delegates to {@link #buildUrlsForHandler},
 3      * provided that {@link #isEligibleForMapping} returns {@code true}.
 4      */
 5     @Override
 6     protected String[] determineUrlsForHandler(String beanName) {
 7         Class beanClass = getApplicationContext().getType(beanName);
 8         if (isEligibleForMapping(beanName, beanClass)) {
 9             return buildUrlsForHandler(beanName, beanClass);
10         }
11         else {
12             return null;
13         }
14     }

 // AbstractControllerUrlHandlerMapping

 1     /**判断controller是否被排除在外(通过包package排除或类class排除).
 2      * Determine whether the specified controller is excluded from this mapping.
 3      * @param beanName the name of the controller bean
 4      * @param beanClass the concrete class of the controller bean
 5      * @return whether the specified class is excluded
 6      * @see #setExcludedPackages
 7      * @see #setExcludedClasses
 8      */
 9     protected boolean isEligibleForMapping(String beanName, Class beanClass) {
10         if (beanClass == null) {
11             if (logger.isDebugEnabled()) {
12                 logger.debug("Excluding controller bean '" + beanName + "' from class name mapping " +
13                         "because its bean type could not be determined");
14             }
15             return false;
16         }
17         if (this.excludedClasses.contains(beanClass)) {
18             if (logger.isDebugEnabled()) {
19                 logger.debug("Excluding controller bean '" + beanName + "' from class name mapping " +
20                         "because its bean class is explicitly excluded: " + beanClass.getName());
21             }
22             return false;
23         }
24         String beanClassName = beanClass.getName();
25         for (String packageName : this.excludedPackages) {
26             if (beanClassName.startsWith(packageName)) {
27                 if (logger.isDebugEnabled()) {
28                     logger.debug("Excluding controller bean '" + beanName + "' from class name mapping " +
29                             "because its bean class is defined in an excluded package: " + beanClass.getName());
30                 }
31                 return false;
32             }
33         }
34         return isControllerType(beanClass);
35     }

 // AbstractControllerUrlHandlerMapping

1     /**
2      * Determine whether the given bean class indicates a controller type
3      * that is supported by this mapping strategy.
4      * @param beanClass the class to introspect
5      */
6     protected boolean isControllerType(Class beanClass) {
7         return this.predicate.isControllerType(beanClass);
8     }

 // ControllerTypePredicate

这边提供2个api,分别判断是Controller的子类还是MultiActionController的子类.

 1 /**
 2  * Internal helper class that identifies controller types.
 3  *
 4  * @author Juergen Hoeller
 5  * @since 2.5.3
 6  */
 7 class ControllerTypePredicate {
 8 
 9     public boolean isControllerType(Class beanClass) {
10         return Controller.class.isAssignableFrom(beanClass);
11     }
12 
13     public boolean isMultiActionControllerType(Class beanClass) {
14         return MultiActionController.class.isAssignableFrom(beanClass);
15     }
16 
17 }

预留生成url的模板方法

 // AbstractControllerUrlHandlerMapping

1     /**
2      * Abstract template method to be implemented by subclasses.
3      * @param beanName the name of the bean
4      * @param beanClass the type of the bean
5      * @return the URLs determined for the bean
6      */
7     protected abstract String[] buildUrlsForHandler(String beanName, Class beanClass);

再来看看AbstractControllerUrlHandlerMapping的2个实现ControllerBeanNameUrlHandlerMapping和ControllerClassNameUrlHandlerMapping.

其实这两个,很简单,一个是根据beanName来生产url,一个是根据className来生产url.

// ControllerBeanNameUrlHandlerMapping

 1     @Override
 2     protected String[] buildUrlsForHandler(String beanName, Class beanClass) {
 3         List<String> urls = new ArrayList<String>();
 4         urls.add(generatePathMapping(beanName));
 5         String[] aliases = getApplicationContext().getAliases(beanName);// 也获取配置的别名
 6         for (String alias : aliases) {
 7             urls.add(generatePathMapping(alias));
 8         }
 9         return StringUtils.toStringArray(urls);
10     }

// ControllerBeanNameUrlHandlerMapping

 1     /**对path添加前后缀,还有/
 2      * Prepends a '/' if required and appends the URL suffix to the name.
 3      */
 4     protected String generatePathMapping(String beanName) {
 5         String name = (beanName.startsWith("/") ? beanName : "/" + beanName);
 6         StringBuilder path = new StringBuilder();
 7         if (!name.startsWith(this.urlPrefix)) {
 8             path.append(this.urlPrefix);
 9         }
10         path.append(name);
11         if (!name.endsWith(this.urlSuffix)) {
12             path.append(this.urlSuffix);
13         }
14         return path.toString();
15     }

// ControllerClassNameUrlHandlerMapping

直接委托给generatePathMappings实现

1     @Override
2     protected String[] buildUrlsForHandler(String beanName, Class beanClass) {
3         return generatePathMappings(beanClass);
4     }

// ControllerClassNameUrlHandlerMapping

  通过buildPathPrefix获取path的前缀

  通过ClassUtils获取className,如BookController(不带包名),同时使用cglib代理的问题一并解决

  根据大小写是否敏感,转换className(默认caseSensitive = false;)

  isMultiActionControllerType判断Controller是否MultiActionController的子类,就是controller是否包含多个handler

 1     /**
 2      * Generate the actual URL paths for the given controller class.
 3      * <p>Subclasses may choose to customize the paths that are generated
 4      * by overriding this method.
 5      * @param beanClass the controller bean class to generate a mapping for
 6      * @return the URL path mappings for the given controller
 7      */
 8     protected String[] generatePathMappings(Class beanClass) {
 9         StringBuilder pathMapping = buildPathPrefix(beanClass);
10         String className = ClassUtils.getShortName(beanClass);
11         String path = (className.endsWith(CONTROLLER_SUFFIX) ?
12                 className.substring(0, className.lastIndexOf(CONTROLLER_SUFFIX)) : className);
13         if (path.length() > 0) {
14             if (this.caseSensitive) {
15                 pathMapping.append(path.substring(0, 1).toLowerCase()).append(path.substring(1));
16             }
17             else {
18                 pathMapping.append(path.toLowerCase());
19             }
20         }
21         if (isMultiActionControllerType(beanClass)) {
22             return new String[] {pathMapping.toString(), pathMapping.toString() + "/*"};
23         }
24         else {
25             return new String[] {pathMapping.toString() + "*"};
26         }
27     }

// ControllerClassNameUrlHandlerMapping

 1     /**
 2      * Build a path prefix for the given controller bean class.
 3      * @param beanClass the controller bean class to generate a mapping for
 4      * @return the path prefix, potentially including subpackage names as path elements
 5      */
 6     private StringBuilder buildPathPrefix(Class beanClass) {
 7         StringBuilder pathMapping = new StringBuilder();
 8         if (this.pathPrefix != null) {
 9             pathMapping.append(this.pathPrefix);
10             pathMapping.append("/");
11         }
12         else {
13             pathMapping.append("/");
14         }
15         if (this.basePackage != null) {
16             String packageName = ClassUtils.getPackageName(beanClass);
17             if (packageName.startsWith(this.basePackage)) {
18                 String subPackage = packageName.substring(this.basePackage.length()).replace('.', '/');
19                 pathMapping.append(this.caseSensitive ? subPackage : subPackage.toLowerCase());
20                 pathMapping.append("/");
21             }
22         }
23         return pathMapping;
24     }

// AbstractControllerUrlHandlerMapping

predicate.isMultiActionControllerType具体实现看上面的ControllerTypePredicate

1     /**
2      * Determine whether the given bean class indicates a controller type
3      * that dispatches to multiple action methods.
4      * @param beanClass the class to introspect
5      */
6     protected boolean isMultiActionControllerType(Class beanClass) {
7         return this.predicate.isMultiActionControllerType(beanClass);
8     }
原文地址:https://www.cnblogs.com/leftthen/p/5208169.html