上一节主要阅读了BeanNameUrlHandlerMapping类,并理清了它的父类关系,值得注意的是它的爷爷的爸爸也就是AbstractHandlerMapping实现了HandlerMapping接口,而继承了WebApplicationObjectSupport类,在AbstractDetectingUrlHandlerMapping类中,有这样一个方法:
/**
* Calls the {@link #detectHandlers()} method in addition to the
* superclass's initialization.
*/
@Override
public void initApplicationContext() throws ApplicationContextException {
super.initApplicationContext();
detectHandlers();
}
上一节没有说明这个,经过我翻WebApplicationObjectSupport类和它的父类ApplicationObjectSupport发现有一个setApplicationContext(ApplicationContext context)方法,在其中调用了initApplicationContext方法,前面的setter肯定是在bean构造时用到的,更上层的以后再去阅读理解吧,到这里就追溯到了handler探测的本源。
DefaultAnnoationHandlerMapping类也是继承自AbstractDetectingUrlHandlerMapping类,则必然实现了它的抽象方法:
/**
* 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);
那么detectHandler接下来的执行顺序就和上一节的顺序相同,这时我们跳到了DefaultAnnoationHandlerMapping类中的该方法:
/**
* 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);
for (String typeLevelPattern : typeLevelPatterns) {
if (!typeLevelPattern.startsWith("/")) {
typeLevelPattern = "/" + typeLevelPattern;
}
for (String methodLevelPattern : methodLevelPatterns) {
String combinedPattern = getPathMatcher().combine(typeLevelPattern, methodLevelPattern);
addUrlsForPath(urls, combinedPattern);
}
addUrlsForPath(urls, typeLevelPattern);
}
return StringUtils.toStringArray(urls);
}
else {
// actual paths specified by @RequestMapping at method level
return determineUrlsForHandlerMethods(handlerType);
}
}
else if (AnnotationUtils.findAnnotation(handlerType, Controller.class) != null) {
// @RequestMapping to be introspected at method level
return determineUrlsForHandlerMethods(handlerType);
}
else {
return null;
}
}
既然是默认注解支持的HandlerMapping,那么它必然是去探测@RequestMapping所注解的Handler和Method。这里就有必要来看看RequestMapping接口的定义:
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping {
String[] value() default {};
RequestMethod[] method() default {};
String[] params() default {};
String[] headers() default {};
}
value数组显然是存储所有该Handler中的url,method则是POST GET等方法,RequestMethod是一个enum类型
public enum RequestMethod {
GET, HEAD, POST, PUT, DELETE, OPTIONS, TRACE
}
context.findAnnotationOnBean(beanName, RequestMapping.class)显然是查找了该Handler上所有使用了@RequestMapping的注解,将它们的value,method取出,再封装到RequestMapping类对象中。接下来的加入缓存语句就不用解释了。
我们来看看determineUrlsForHandlerMethods方法的源码:
/**
* 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) {
final Set<String> urls = new LinkedHashSet<String>();
Class<?>[] handlerTypes =
Proxy.isProxyClass(handlerType) ? handlerType.getInterfaces() : new Class<?>[]{handlerType};
for (Class<?> currentHandlerType : handlerTypes) {
ReflectionUtils.doWithMethods(currentHandlerType, new ReflectionUtils.MethodCallback() {
public void doWith(Method method) {
RequestMapping mapping = AnnotationUtils.findAnnotation(method, RequestMapping.class);
if (mapping != null) {
String[] mappedPaths = mapping.value();
for (String mappedPath : mappedPaths) {
addUrlsForPath(urls, mappedPath);
}
}
}
});
}
return StringUtils.toStringArray(urls);
}
这里就比较麻烦了,先从字面意思理解就是查询类中的方法,找到方法对应的RequestMapping的所包含的value,组成一个url数组。
接下来看它的实现,首先是检查了handlerType是否是一个代理类
/**
* Returns true if and only if the specified class was dynamically
* generated to be a proxy class.
*/
public static boolean isProxyClass(Class<?> cl) {
if (cl == null) {
throw new NullPointerException();
}
return proxyClasses.containsKey(cl);
}
/** set of all generated proxy classes, for isProxyClass implementation */
private static Map proxyClasses =
Collections.synchronizedMap(new WeakHashMap());
接下来是反射和回调,我也看到云里雾里的。。。。理解不来,以后再想吧,直到它最后是得到的url就行了。
handlerType.getInterfaces();
用于得到所有handlerType实现的接口的Class对象
new Class<?>[]{handlerType}
得到的还只是handlerType的Class对象
所以这里就有些不懂为什么要这么做,希望有高手看到我的文章 帮我解答下疑惑