上一节主要阅读了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对象
所以这里就有些不懂为什么要这么做,希望有高手看到我的文章 帮我解答下疑惑