SpringMVC之构造映射关系
springmvc正是通过构造请求的模式和其对应的method,然后通过反射机制执行方法,并将结果返回。
本篇就分析下映射关系的初始化过程
一 关键类讲解
HandlerMethod
public class HandlerMethod { /** Logger that is available to subclasses */ protected final Log logger = LogFactory.getLog(getClass()); private final Object bean; private final BeanFactory beanFactory; private final Class<?> beanType; private final Method method;//实际的method private final Method bridgedMethod; private final MethodParameter[] parameters;//入参
它有一个非常重要的子类, ServletInvocableHandlerMethod
ServletInvocableHandlerMethod这个类在HandlerAdapter对每个请求处理过程中,都会实例化一个出来(上面提到的属性由HandlerAdapter进行设置),分别对请求和返回进行处理。 (RequestMappingHandlerAdapter源码,实例化ServletInvocableHandlerMethod的时候分别set了上面提到的重要属性)
MethodParameter
public class MethodParameter { private final Method method;//方法名 private final Constructor<?> constructor; private final int parameterIndex;//该参数的下标,表示第几个入参 private int nestingLevel = 1; /** Map from Integer level to Integer type index */ Map<Integer, Integer> typeIndexesPerLevel; private volatile Class<?> containingClass; private volatile Class<?> parameterType;//类型 private volatile Type genericParameterType; private volatile Annotation[] parameterAnnotations; private volatile ParameterNameDiscoverer parameterNameDiscoverer; private volatile String parameterName;//参数名 private volatile MethodParameter nestedMethodParameter;
RequestMappingInfo 是一个封装了各种请求映射条件并实现了RequestCondition接口的类。
有各种RequestCondition实现类属性,patternsCondition,methodsCondition,paramsCondition,headersCondition,consumesCondition以及producesCondition,这个请求条件看属性名也了解,分别代表http请求的路径模式、方法、参数、头部等信息。
public final class RequestMappingInfo implements RequestCondition<RequestMappingInfo> { private final String name; private final PatternsRequestCondition patternsCondition;//URL模式 private final RequestMethodsRequestCondition methodsCondition;//GET 还是POST private final ParamsRequestCondition paramsCondition; private final HeadersRequestCondition headersCondition; private final ConsumesRequestCondition consumesCondition; private final ProducesRequestCondition producesCondition;
RequestMappingHandlerMapping
处理请求与HandlerMethod映射关系的一个类
二 初始化过程
我们先从命名空间开始说起
当要使用mvc都是这么开始的 <mvc:annotation-driven content-negotiation-manager="contentNegotiationManager"/>
熟悉spring的都知道命名空间是怎么回事,直接到jar包的META-INF里去找
http\://www.springframework.org/schema/mvc=org.springframework.web.servlet.config.MvcNamespaceHandler
public class MvcNamespaceHandler extends NamespaceHandlerSupport { @Override public void init() { registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser()); registerBeanDefinitionParser("default-servlet-handler", new DefaultServletHandlerBeanDefinitionParser()); registerBeanDefinitionParser("interceptors", new InterceptorsBeanDefinitionParser()); registerBeanDefinitionParser("resources", new ResourcesBeanDefinitionParser()); registerBeanDefinitionParser("view-controller", new ViewControllerBeanDefinitionParser()); registerBeanDefinitionParser("redirect-view-controller", new ViewControllerBeanDefinitionParser()); registerBeanDefinitionParser("status-controller", new ViewControllerBeanDefinitionParser()); registerBeanDefinitionParser("view-resolvers", new ViewResolversBeanDefinitionParser()); registerBeanDefinitionParser("tiles-configurer", new TilesConfigurerBeanDefinitionParser()); registerBeanDefinitionParser("freemarker-configurer", new FreeMarkerConfigurerBeanDefinitionParser()); registerBeanDefinitionParser("velocity-configurer", new VelocityConfigurerBeanDefinitionParser()); registerBeanDefinitionParser("groovy-configurer", new GroovyMarkupConfigurerBeanDefinitionParser()); registerBeanDefinitionParser("script-template-configurer", new ScriptTemplateConfigurerBeanDefinitionParser()); registerBeanDefinitionParser("cors", new CorsBeanDefinitionParser()); }
class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser { public static final String HANDLER_MAPPING_BEAN_NAME = RequestMappingHandlerMapping.class.getName();//默认的bean public static final String HANDLER_ADAPTER_BEAN_NAME = RequestMappingHandlerAdapter.class.getName();//默认的bean
@Override public BeanDefinition parse(Element element, ParserContext parserContext) { Object source = parserContext.extractSource(element); XmlReaderContext readerContext = parserContext.getReaderContext(); CompositeComponentDefinition compDefinition = new CompositeComponentDefinition(element.getTagName(), source); parserContext.pushContainingComponent(compDefinition); RuntimeBeanReference contentNegotiationManager = getContentNegotiationManager(element, source, parserContext); RootBeanDefinition handlerMappingDef = new RootBeanDefinition(RequestMappingHandlerMapping.class); handlerMappingDef.setSource(source); handlerMappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); handlerMappingDef.getPropertyValues().add("order", 0); handlerMappingDef.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager); if (element.hasAttribute("enable-matrix-variables")) { Boolean enableMatrixVariables = Boolean.valueOf(element.getAttribute("enable-matrix-variables")); handlerMappingDef.getPropertyValues().add("removeSemicolonContent", !enableMatrixVariables); } else if (element.hasAttribute("enableMatrixVariables")) { Boolean enableMatrixVariables = Boolean.valueOf(element.getAttribute("enableMatrixVariables")); handlerMappingDef.getPropertyValues().add("removeSemicolonContent", !enableMatrixVariables); } configurePathMatchingProperties(handlerMappingDef, element, parserContext); readerContext.getRegistry().registerBeanDefinition(HANDLER_MAPPING_BEAN_NAME , handlerMappingDef);
上面的代码说明了,在解析 <mvc:annotation-driven content-negotiation-manager="contentNegotiationManager"/>
系统自动给容器中注册了两个BeanDefinition,分别是 RequestMappingHandlerMapping ,RequestMappingHandlerAdapter
接下来我们就分析 RequestMappingHandlerMapping 的初始化过程
三 RequestMappingHandlerMapping 初始化
初始化的工作在 AbstractHandlerMethodMapping
@Override public void afterPropertiesSet() { initHandlerMethods(); }
protected void initHandlerMethods() { if (logger.isDebugEnabled()) { logger.debug("Looking for request mappings in application context: " + getApplicationContext()); } String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ? BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) : getApplicationContext().getBeanNamesForType(Object.class)); for (String beanName : beanNames) { if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) { Class<?> beanType = null; try { beanType = getApplicationContext().getType(beanName); } catch (Throwable ex) { // An unresolvable bean type, probably from a lazy bean - let's ignore it. if (logger.isDebugEnabled()) { logger.debug("Could not resolve target class for bean with name '" + beanName + "'", ex); } } if (beanType != null && isHandler(beanType)) {//拿到当前容器的所有的bean,判断是不是controller detectHandlerMethods(beanName); } } } handlerMethodsInitialized(getHandlerMethods()); }
protected void detectHandlerMethods(final Object handler) {//handler就是实际的controller实现类 Class<?> handlerType = (handler instanceof String ? getApplicationContext().getType((String) handler) : handler.getClass()); final Class<?> userType = ClassUtils.getUserClass(handlerType); Map<Method, T> methods = MethodIntrospector.selectMethods(userType, //这里的methods的结构是key是Method,而Value是一个RequestMappingInfo new MethodIntrospector.MetadataLookup<T>() { @Override public T inspect(Method method) { try { return getMappingForMethod(method, userType); } catch (Throwable ex) { throw new IllegalStateException("Invalid mapping on handler class [" + userType.getName() + "]: " + method, ex); } } });//这部分的逻辑并不复杂,就是遍历这个对象里的所有方法,并记录下方法上的路径 if (logger.isDebugEnabled()) { logger.debug(methods.size() + " request handler methods found on " + userType + ": " + methods); } for (Map.Entry<Method, T> entry : methods.entrySet()) { Method invocableMethod = AopUtils.selectInvocableMethod(entry.getKey(), userType); T mapping = entry.getValue(); registerHandlerMethod(handler, invocableMethod, mapping); } }
我们看一下 RequestMappingHandlerMapping 的
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) { RequestMappingInfo info = createRequestMappingInfo(method); if (info != null) { RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType); if (typeInfo != null) { info = typeInfo.combine(info); } } return info; }
private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) { RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class); RequestCondition<?> condition = (element instanceof Class ? getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element)); return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null); }
入参 AnnotatedElement 是 public com.suning.rdrs.admin.controller.ResponseResult com.suning.rdrs.admin.controller.audit.RdrsAuditCallbackController.manualAudit(java.lang.String) ,类型就是java反射的Method。
AnnotatedElementUtils.findMergedAnnotation 的作用就是解析Method带有的RequestMapping注解
requestMapping的返回值是 @org.springframework.web.bind.annotation.RequestMapping(name=, value=[/alarm/summary], path=[/alarm/summary], method=[GET], params=[], headers=[], produces=[], consumes=[])
我这里的condition是null,因为我没有配特殊模式
然后就是构造RequestMappingInfo
protected RequestMappingInfo createRequestMappingInfo( RequestMapping requestMapping, RequestCondition<?> customCondition) { return RequestMappingInfo .paths(resolveEmbeddedValuesInPatterns(requestMapping.path())) .methods(requestMapping.method()) .params(requestMapping.params()) .headers(requestMapping.headers()) .consumes(requestMapping.consumes()) .produces(requestMapping.produces()) .mappingName(requestMapping.name()) .customCondition(customCondition) .options(this.config) .build(); }
整个过程最后我们得到 Map<Method, T> methods key是Method,Value是RequestMappingInfo
让我们回到 AbstractHandlerMethodMapping.detectHandlerMethods 看看剩下的部分
for (Map.Entry<Method, T> entry : methods.entrySet()) { Method invocableMethod = AopUtils.selectInvocableMethod(entry.getKey(), userType); T mapping = entry.getValue(); registerHandlerMethod(handler, invocableMethod, mapping); }
AbstractHandlerMethodMapping 中的方法
protected void registerHandlerMethod(Object handler, Method method, T mapping) { this.mappingRegistry.register(mapping, handler, method); }
public void register(T mapping, Object handler, Method method) { this.readWriteLock.writeLock().lock(); try { HandlerMethod handlerMethod = createHandlerMethod(handler, method); assertUniqueMethodMapping(handlerMethod, mapping); if (logger.isInfoEnabled()) { logger.info("Mapped \"" + mapping + "\" onto " + handlerMethod); } this.mappingLookup.put(mapping, handlerMethod); //key是RequestMappingInfo对象,Value是实际的方法 List<String> directUrls = getDirectUrls(mapping); for (String url : directUrls) { this.urlLookup.add(url, mapping); //保存url和mapping关系注意 mapping是一个RequestMappingInfo对象 } String name = null; if (getNamingStrategy() != null) { name = getNamingStrategy().getName(handlerMethod, mapping); addMappingName(name, handlerMethod); } CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping); if (corsConfig != null) { this.corsLookup.put(handlerMethod, corsConfig); } this.registry.put(mapping, new MappingRegistration<T>(mapping, handlerMethod, directUrls, name)); } finally { this.readWriteLock.writeLock().unlock(); } }
以上就是分析怎么把请求url和对应的处理方法关联起来的,这里面有很多细节比如RequestMappingInfo在构建中,有些用户会写很复杂的url模式,比如带通配符的,通配符还有很多钟模式等等。本文要讨论的是原理,哪些字符串解析就不管他了。