AbstractDetectingUrlHandlerMapping是通過掃描方式注冊(cè)Handler,收到請(qǐng)求時(shí)由AbstractUrlHandlerMapping的getHandlerInternal進(jìn)行分發(fā).
共有5個(gè)子類,一個(gè)抽象類.
與SimpleUrlHandlerMapping類似,通過覆寫initApplicationContext,然后調(diào)用detectHandlers進(jìn)行初始化.
detectHandlers通過BeanFactoryUtils掃描應(yīng)用下的Object,然后預(yù)留determineUrlsForHandler給子類根據(jù)Handler生成對(duì)應(yīng)的url.
注冊(cè)使用的registerHandler依然由AbstractUrlHandlerMapping提供.
1
2
3
4
5
6
7
8
9
10
|
// AbstractDetectingUrlHandlerMapping /** * Calls the {@link #detectHandlers()} method in addition to the * superclass's initialization. */ @Override public void initApplicationContext() throws ApplicationContextException { super .initApplicationContext(); detectHandlers(); } |
這邊一樣是調(diào)用AbstractHandlerMapping的initApplicationContext初始化攔截器.
主角上場(chǎng),detectHandlers,掃描Handlers
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
// AbstractDetectingUrlHandlerMapping /** * 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()); } 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" ); } } } } |
這邊預(yù)留的模板方法定義如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
/** * 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} or an empty array if none */ protected abstract String[] determineUrlsForHandler(String beanName); 我們?cè)賮?lái)看看模板方法在BeanNameUrlHandlerMapping和AbstractControllerUrlHandlerMapping中的實(shí)現(xiàn)吧. BeanNameUrlHandlerMapping非常簡(jiǎn)單,就實(shí)現(xiàn)了determineUrlsForHandler. 其中的alias應(yīng)該是應(yīng)該就是通過beanName在配置文件中配置的. // BeanNameUrlHandlerMapping /** * 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); } |
再來(lái)看看AbstractControllerUrlHandlerMapping中的實(shí)現(xiàn)
isEligibleForMapping判斷controller是否被排除在外(通過包package排除或類class排除).
buildUrlsForHandler由子類實(shí)現(xiàn)具體的url生成規(guī)則
isControllerType判斷是否Controller的子類
buildUrlsForHandler預(yù)留給子類生產(chǎn)url的模板方法.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
|
// AbstractControllerUrlHandlerMapping /** * This implementation delegates to {@link #buildUrlsForHandler}, * provided that {@link #isEligibleForMapping} returns {@code true}. */ @Override protected String[] determineUrlsForHandler(String beanName) { Class beanClass = getApplicationContext().getType(beanName); if (isEligibleForMapping(beanName, beanClass)) { return buildUrlsForHandler(beanName, beanClass); } else { return null ; } } // AbstractControllerUrlHandlerMapping /**判斷controller是否被排除在外(通過包package排除或類class排除). * Determine whether the specified controller is excluded from this mapping. * @param beanName the name of the controller bean * @param beanClass the concrete class of the controller bean * @return whether the specified class is excluded * @see #setExcludedPackages * @see #setExcludedClasses */ protected boolean isEligibleForMapping(String beanName, Class beanClass) { if (beanClass == null ) { if (logger.isDebugEnabled()) { logger.debug( "Excluding controller bean '" + beanName + "' from class name mapping " + "because its bean type could not be determined" ); } return false ; } if ( this .excludedClasses.contains(beanClass)) { if (logger.isDebugEnabled()) { logger.debug( "Excluding controller bean '" + beanName + "' from class name mapping " + "because its bean class is explicitly excluded: " + beanClass.getName()); } return false ; } String beanClassName = beanClass.getName(); for (String packageName : this .excludedPackages) { if (beanClassName.startsWith(packageName)) { if (logger.isDebugEnabled()) { logger.debug( "Excluding controller bean '" + beanName + "' from class name mapping " + "because its bean class is defined in an excluded package: " + beanClass.getName()); } return false ; } } return isControllerType(beanClass); } // AbstractControllerUrlHandlerMapping /** * Determine whether the given bean class indicates a controller type * that is supported by this mapping strategy. * @param beanClass the class to introspect */ protected boolean isControllerType(Class beanClass) { return this .predicate.isControllerType(beanClass); } // ControllerTypePredicate 這邊提供 2 個(gè)api,分別判斷是Controller的子類還是MultiActionController的子類. /** * Internal helper class that identifies controller types. * * @author Juergen Hoeller * @since .. */ class ControllerTypePredicate { public boolean isControllerType(Class beanClass) { return Controller. class .isAssignableFrom(beanClass); } public boolean isMultiActionControllerType(Class beanClass) { return MultiActionController. class .isAssignableFrom(beanClass); } } |
預(yù)留生成url的模板方法
1
2
3
4
5
6
7
8
|
// AbstractControllerUrlHandlerMapping /** * Abstract template method to be implemented by subclasses. * @param beanName the name of the bean * @param beanClass the type of the bean * @return the URLs determined for the bean */ protected abstract String[] buildUrlsForHandler(String beanName, Class beanClass); |
再來(lái)看看AbstractControllerUrlHandlerMapping的2個(gè)實(shí)現(xiàn)ControllerBeanNameUrlHandlerMapping和ControllerClassNameUrlHandlerMapping.
其實(shí)這兩個(gè),很簡(jiǎn)單,一個(gè)是根據(jù)beanName來(lái)生產(chǎn)url,一個(gè)是根據(jù)className來(lái)生產(chǎn)url.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
// ControllerBeanNameUrlHandlerMapping @Override protected String[] buildUrlsForHandler(String beanName, Class beanClass) { List<String> urls = new ArrayList<String>(); urls.add(generatePathMapping(beanName)); String[] aliases = getApplicationContext().getAliases(beanName); // 也獲取配置的別名 for (String alias : aliases) { urls.add(generatePathMapping(alias)); } return StringUtils.toStringArray(urls); } // ControllerBeanNameUrlHandlerMapping /**對(duì)path添加前后綴,還有/ * Prepends a '/' if required and appends the URL suffix to the name. */ protected String generatePathMapping(String beanName) { String name = (beanName.startsWith( "/" ) ? beanName : "/" + beanName); StringBuilder path = new StringBuilder(); if (!name.startsWith( this .urlPrefix)) { path.append( this .urlPrefix); } path.append(name); if (!name.endsWith( this .urlSuffix)) { path.append( this .urlSuffix); } return path.toString(); } // ControllerClassNameUrlHandlerMapping |
直接委托給generatePathMappings實(shí)現(xiàn)
1
2
3
4
5
|
@Override protected String[] buildUrlsForHandler(String beanName, Class beanClass) { return generatePathMappings(beanClass); } // ControllerClassNameUrlHandlerMapping |
通過buildPathPrefix獲取path的前綴
通過ClassUtils獲取className,如BookController(不帶包名),同時(shí)使用cglib代理的問題一并解決
根據(jù)大小寫是否敏感,轉(zhuǎn)換className(默認(rèn)caseSensitive = false;)
isMultiActionControllerType判斷Controller是否MultiActionController的子類,就是controller是否包含多個(gè)handler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
|
/** * Generate the actual URL paths for the given controller class. * <p>Subclasses may choose to customize the paths that are generated * by overriding this method. * @param beanClass the controller bean class to generate a mapping for * @return the URL path mappings for the given controller */ protected String[] generatePathMappings(Class beanClass) { StringBuilder pathMapping = buildPathPrefix(beanClass); String className = ClassUtils.getShortName(beanClass); String path = (className.endsWith(CONTROLLER_SUFFIX) ? className.substring(, className.lastIndexOf(CONTROLLER_SUFFIX)) : className); if (path.length() > ) { if ( this .caseSensitive) { pathMapping.append(path.substring(, ).toLowerCase()).append(path.substring()); } else { pathMapping.append(path.toLowerCase()); } } if (isMultiActionControllerType(beanClass)) { return new String[] {pathMapping.toString(), pathMapping.toString() + "/*" }; } else { return new String[] {pathMapping.toString() + "*" }; } } // ControllerClassNameUrlHandlerMapping /** * Build a path prefix for the given controller bean class. * @param beanClass the controller bean class to generate a mapping for * @return the path prefix, potentially including subpackage names as path elements */ private StringBuilder buildPathPrefix(Class beanClass) { StringBuilder pathMapping = new StringBuilder(); if ( this .pathPrefix != null ) { pathMapping.append( this .pathPrefix); pathMapping.append( "/" ); } else { pathMapping.append( "/" ); } if ( this .basePackage != null ) { String packageName = ClassUtils.getPackageName(beanClass); if (packageName.startsWith( this .basePackage)) { String subPackage = packageName.substring( this .basePackage.length()).replace( '.' , '/' ); pathMapping.append( this .caseSensitive ? subPackage : subPackage.toLowerCase()); pathMapping.append( "/" ); } } return pathMapping; } // AbstractControllerUrlHandlerMapping |
predicate.isMultiActionControllerType具體實(shí)現(xiàn)看上面的ControllerTypePredicate
1
2
3
4
5
6
7
8
|
/** * Determine whether the given bean class indicates a controller type * that dispatches to multiple action methods. * @param beanClass the class to introspect */ protected boolean isMultiActionControllerType(Class beanClass) { return this .predicate.isMultiActionControllerType(beanClass); } |
以上所述是小編給大家介紹的SpringMVC源碼解讀之 HandlerMapping - AbstractDetectingUrlHandlerMapping系列初始化的相關(guān)知識(shí),希望對(duì)大家有所幫助!