SpringMVC源码 HandlerMapping和HandlerAdapter(2)
来源:互联网 发布:java mina框架 编辑:程序博客网 时间:2024/04/25 19:44
上一节我们看到 当***-serlvet.xml中未声明HandlerMapping的实体bean时,默认构造
org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping和
org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping
的实例。
所以本节就从这两个类入手研究,HandlerMapping中最重要的方法是 getHandler 用于获取HandlerExcutionChain的对象。
先来看BeanNameUrlHandlerMapping类,它的内部没有实现getHandler方法,而它继承自AbstractDetectingUrlHandlerMapping,所以自底向上我们去查看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");
- }
- }
- }
- }
whether to detect handler beans in ancestor ApplicationContexts.
- private boolean detectHandlersInAncestorContexts = false;
该方法首先查询了上下文中所有的bean,因为查询的Type是Object.class,得到了所有的bean名称,之后解析bean名称,查看是否存是urlName,determineUrlsForHandler方法在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 (int i = 0; i < aliases.length; i++) {
- if (aliases[i].startsWith("/")) {
- urls.add(aliases[i]);
- }
- }
- return StringUtils.toStringArray(urls);
- }
可以看出它查找了所有beanName以“/”开头的bean,将它们的name作为url返回。
接下来就对那些作为处理url请求的bean进行handler的注册,registerHandler方法在AbstractDetectingUrlHandlerMapping的父类AbstractUrlHandlerMapping类中:
- /**
- * Register the specified handler for the given URL paths.
- * @param urlPaths the URLs that the bean should be mapped to
- * @param beanName the name of the handler bean
- * @throws BeansException if the handler couldn't be registered
- * @throws IllegalStateException if there is a conflicting handler registered
- */
- protected void registerHandler(String[] urlPaths, String beanName) throws BeansException, IllegalStateException {
- Assert.notNull(urlPaths, "URL path array must not be null");
- for (String urlPath : urlPaths) {
- registerHandler(urlPath, beanName);
- }
- }
- /**
- * Register the specified handler for the given URL path.
- * @param urlPath the URL the bean should be mapped to
- * @param handler the handler instance or handler bean name String
- * (a bean name will automatically be resolved into the corresponding handler bean)
- * @throws BeansException if the handler couldn't be registered
- * @throws IllegalStateException if there is a conflicting handler registered
- */
- protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {
- Assert.notNull(urlPath, "URL path must not be null");
- Assert.notNull(handler, "Handler object must not be null");
- Object resolvedHandler = handler;
- // Eagerly resolve handler if referencing singleton via name.
- if (!this.lazyInitHandlers && handler instanceof String) {
- String handlerName = (String) handler;
- if (getApplicationContext().isSingleton(handlerName)) {
- resolvedHandler = getApplicationContext().getBean(handlerName);
- }
- }
- Object mappedHandler = this.handlerMap.get(urlPath);
- if (mappedHandler != null) {
- if (mappedHandler != resolvedHandler) {
- throw new IllegalStateException(
- "Cannot map handler [" + handler + "] to URL path [" + urlPath +
- "]: There is already handler [" + resolvedHandler + "] mapped.");
- }
- }
- else {
- if (urlPath.equals("/")) {
- if (logger.isInfoEnabled()) {
- logger.info("Root mapping to handler [" + resolvedHandler + "]");
- }
- setRootHandler(resolvedHandler);
- }
- else if (urlPath.equals("/*")) {
- if (logger.isInfoEnabled()) {
- logger.info("Default mapping to handler [" + resolvedHandler + "]");
- }
- setDefaultHandler(resolvedHandler);
- }
- else {
- this.handlerMap.put(urlPath, resolvedHandler);
- if (logger.isInfoEnabled()) {
- logger.info("Mapped URL path [" + urlPath + "] onto handler [" + resolvedHandler + "]");
- }
- }
- }
- }
handlerMap就是一个K-V保存url对应的handler,初始时我们传入的Object handler参数是String对象beanNames,lazyInit表示是延迟初始化handler,如果设为true那么在注册handler的时候我们存入的handler还是String,如果是false,该方法中被用来取得bean实例,所以handlerMap中存放的V就是bean的对象,使用Object泛型是因为bean的不一致。
- private final Map<String, Object> handlerMap = new LinkedHashMap<String, Object>();
注意上面的setDefaultHandler方法,它是AbstractUrlHandlerMapping类的父类AbstractHandlerMapping中的方法,在AbstractHandlerMapping中有一个Object对象属性:
- private Object defaultHandler;
从字面意思可以理解为默认的处理器,因为它对应的映射是"/*",即处理所有符合该表达式的url请求。
阅读完BeanNameUrlHandlerMapping和AbstractDetectingUrlHandlerMapping两个类后,我们发现它们中的方法主要进行了handler的set,而handler的get却没有在它们中体现,所以再向上走,我们查看最高的父类AbstractHandlerMapping类,它直接实现了HandlerMapping接口,所以剖析它的getHandler方法更容易让我们理解,handler获取的流程。
- public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
- Object handler = getHandlerInternal(request);
- if (handler == null) {
- handler = getDefaultHandler();
- }
- if (handler == null) {
- return null;
- }
- // Bean name or resolved handler?
- if (handler instanceof String) {
- String handlerName = (String) handler;
- handler = getApplicationContext().getBean(handlerName);
- }
- return getHandlerExecutionChain(handler, request);
- }
该方法首先调用了getHandlerInternal处理request,getHandlerInternal是其子类AbstractUrlHandlerMapping实现的方法,用来查询handlerMap查找request的url对应的handler。
- @Override
- protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
- String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
- Object handler = lookupHandler(lookupPath, request);
- if (handler == null) {
- // We need to care for the default handler directly, since we need to
- // expose the PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE for it as well.
- Object rawHandler = null;
- if ("/".equals(lookupPath)) {
- rawHandler = getRootHandler();
- }
- if (rawHandler == null) {
- rawHandler = getDefaultHandler();
- }
- if (rawHandler != null) {
- // Bean name or resolved handler?
- if (rawHandler instanceof String) {
- String handlerName = (String) rawHandler;
- rawHandler = getApplicationContext().getBean(handlerName);
- }
- validateHandler(rawHandler, request);
- handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
- }
- }
- if (handler != null && this.mappedInterceptors != null) {
- Set<HandlerInterceptor> mappedInterceptors =
- this.mappedInterceptors.getInterceptors(lookupPath, this.pathMatcher);
- if (!mappedInterceptors.isEmpty()) {
- HandlerExecutionChain chain;
- if (handler instanceof HandlerExecutionChain) {
- chain = (HandlerExecutionChain) handler;
- } else {
- chain = new HandlerExecutionChain(handler);
- }
- chain.addInterceptors(mappedInterceptors.toArray(new HandlerInterceptor[mappedInterceptors.size()]));
- }
- }
- if (handler != null && logger.isDebugEnabled()) {
- logger.debug("Mapping [" + lookupPath + "] to handler '" + handler + "'");
- }
- else if (handler == null && logger.isTraceEnabled()) {
- logger.trace("No handler mapping found for [" + lookupPath + "]");
- }
- return handler;
- }
下面来分析getHandlerInternal方法内部,第一句使用类中UrlPathHelper对象urlPathHelper调用getLookupPathForRequest方法解析request,从而得到请求的urlPath。
第二句调用lookupHandler方法,用于查找url path对应的handler,从上面的regist我们可以很快理解,这里肯定是在handlerMap中进行查找,它是AbstractUrlHandlerMapping实现的方法:
- /**
- *Look up a handler instance for the given URL path.
- */
- protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
- // Direct match?
- Object handler = this.handlerMap.get(urlPath);
- if (handler != null) {
- // Bean name or resolved handler?
- if (handler instanceof String) {
- String handlerName = (String) handler;
- handler = getApplicationContext().getBean(handlerName);
- }
- validateHandler(handler, request);
- return buildPathExposingHandler(handler, urlPath, urlPath, null);
- }
- // Pattern match?
- List<String> matchingPatterns = new ArrayList<String>();
- for (String registeredPattern : this.handlerMap.keySet()) {
- if (getPathMatcher().match(registeredPattern, urlPath)) {
- matchingPatterns.add(registeredPattern);
- }
- }
- String bestPatternMatch = null;
- if (!matchingPatterns.isEmpty()) {
- Collections.sort(matchingPatterns, getPathMatcher().getPatternComparator(urlPath));
- if (logger.isDebugEnabled()) {
- logger.debug("Matching patterns for request [" + urlPath + "] are " + matchingPatterns);
- }
- bestPatternMatch = matchingPatterns.get(0);
- }
- if (bestPatternMatch != null) {
- handler = this.handlerMap.get(bestPatternMatch);
- // Bean name or resolved handler?
- if (handler instanceof String) {
- String handlerName = (String) handler;
- handler = getApplicationContext().getBean(handlerName);
- }
- validateHandler(handler, request);
- String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestPatternMatch, urlPath);
- Map<String, String> uriTemplateVariables =
- getPathMatcher().extractUriTemplateVariables(bestPatternMatch, urlPath);
- return buildPathExposingHandler(handler, bestPatternMatch, pathWithinMapping, uriTemplateVariables);
- }
- // No handler found...
- return null;
- }
果不其然,从上面的注释//直接匹配 我们可以看到该方法查找handlerMap中key为urlPath的handler value。
当查找到后,判断我们取出的handler是否是String对象,因为前面我们已经说明了lazyInit,所以这里不难理解。如果是String那么就构造bean,如果不是代码就继续向下走,请允许我先跳过buildPathExposingHandler方法,因为后面还会用到,在这里不是至关重要的,等下再说明它。
当直接匹配没有成功的时候,就进行//模式匹配,从handlerMap的keySet中查找到最符合urlPath模式的key,从而得到handler,如果没有找到,那就返回null。
回到getHandlerInternal方法中,当我们没有查找到urlPath对应的handler时,也就是返回handler==null,则进入第一个if条件中,首先要检查两种情况,因为我们在注册时将"/"set到了rootHandler中,而将"/*"set到了defaultHandler中,当从这两种情况中的一种得到handler后,也是判断是否为String,如果是则构造bean,然后调用buildPathExposingHandler方法。
细心的朋友会注意到getDefaultHandler方法在AbstractHandlerMapping的getHandler方法也被调用了一次,这是当getHandlerInternal返回null时调用的。
到这里我们就来介绍一下buildPathExposingHandler方法:
- protected Object buildPathExposingHandler(Object rawHandler,
- String bestMatchingPattern,
- String pathWithinMapping,
- Map<String, String> uriTemplateVariables) {
- HandlerExecutionChain chain = new HandlerExecutionChain(rawHandler);
- chain.addInterceptor(new PathExposingHandlerInterceptor(bestMatchingPattern, pathWithinMapping));
- if (!CollectionUtils.isEmpty(uriTemplateVariables)) {
- chain.addInterceptor(new UriTemplateVariablesHandlerInterceptor(uriTemplateVariables));
- }
- return chain;
- }
该方法中,使用handler作为参数构造了一个HandlerExecutionChain的对象,不熟悉HandlerExecutionChain的朋友可以打开该类的源码,可以看到它保存了两个最重要的对象,一个handler和一组interceptor
- private final Object handler;
- private HandlerInterceptor[] interceptors;
- private List<HandlerInterceptor> interceptorList;
然后调用addInterceptor方法,为chain添加了一个HandlerInterceptor----PathExposingHandlerInterceptor
- public void addInterceptor(HandlerInterceptor interceptor) {
- initInterceptorList();
- this.interceptorList.add(interceptor);
- }
- private void initInterceptorList() {
- if (this.interceptorList == null) {
- this.interceptorList = new ArrayList<HandlerInterceptor>();
- }
- if (this.interceptors != null) {
- this.interceptorList.addAll(Arrays.asList(this.interceptors));
- this.interceptors = null;
- }
- }
当uriTemplateVariables(这是模式匹配时传入buildPathExposingHandler的Map)不为空时,又添加一个HandlerInterceptor-----UriTemplateVariablesHandlerInterceptor
上述的两个interceptor都是AbstractUrlHandlerMapping类的内部类,这里我们先不讨论拦截器,等后面的文章会进行详细阅读。
后面直到getHandler方法完结,均是对handler和handlerinceptor的拼装,就先不介绍了。后面会着重阅读HandlerInceptor接口及其子类。
- SpringMVC源码 HandlerMapping和HandlerAdapter(2)
- SpringMVC源码 HandlerMapping和HandlerAdapter(1)
- SpringMVC源码 HandlerMapping和HandlerAdapter(3)
- SpringMVC源码 HandlerMapping和HandlerAdapter(4)
- 【推荐】SpringMVC源码总结(一)HandlerMapping和HandlerAdapter入门
- SpringMVC源码总结(一)HandlerMapping和HandlerAdapter入门
- SpringMVC源码总结(一)HandlerMapping和HandlerAdapter入门
- SpringMVC源码总结(一)HandlerMapping和HandlerAdapter入门
- SpringMVC源码总结(一)HandlerMapping和HandlerAdapter入门
- SpringMVC源码总结(一)HandlerMapping和HandlerAdapter入门
- SpringMVC源码总结(一)HandlerMapping和HandlerAdapter入门
- SpringMVC源码总结(一)HandlerMapping和HandlerAdapter入门
- SpringMVC源码分析 之 HandlerMapping和HandlerAdapter
- SpringMVC handlerMapping和handlerAdapter
- SpringMVC之HandlerMapping和HandlerAdapter
- SpringMVC之 HandlerAdapter和handlerMapping
- Spring MVC源码浅析(HandlerMapping和HandlerAdapter)
- SpringMVC学习(三)--handlerMapping和handlerAdapter
- ASP.NET相关笔记总结
- Jenkins进阶系列之——15Maven获取Jenkins的Subversion的版本号
- 309. Best Time to Buy and Sell Stock with Cooldown
- 转发一篇傅立叶变换的好文章
- 如何在mac OS 系统中用Terminal实现C语言重定向
- SpringMVC源码 HandlerMapping和HandlerAdapter(2)
- jenkins调整jdk版本不生效的解决办法
- java 图片上传
- Matlab xml读写
- hibernate 中的方言与数据库中的数据类型不对应问题
- EFI VMware Virtual SCSI Hard Drive(0,0) unsuccessful
- HTTP协议中GET、POST和HEAD的介绍
- Java源码打包成可运行JAR:Eclipse实现
- 学习记录--使用 F12 开发人员工具调试 HTML 和 CSS