天下之事常成于困约,而败于奢靡。这篇文章主要讲述Spring MVC — @RequestMapping原理讲解-1相关的知识,希望能为你提供帮助。
转载地址 :http://blog.csdn.net/j080624/article/details/56278461
为了降低文章篇幅,使得文章更目标化,简洁化,我们就不例举各种@RequestMapping的用法等内容了.
具体请点击查看@RequestMapping的用法
文章主要说明以下问题:
- Spring怎样处理@RequestMapping(怎样将请求路径映射到控制器类或方法)
- Spring怎样将请求分派给正确的控制器类或方法
- Spring如何实现灵活的控制器方法的
- Spring怎样处理@RequestMapping(怎样将请求路径映射到控制器类或方法)
一、概念解析
在开始之前我们先了解下新的API中引入的新接口或者类,这会有助于后面的处理过程的理解。不得不说新的API提供了更多漂亮的抽象,你能感受到面向对象的魅力。
- RequestMappingInfo
这个类是对请求映射的一个抽象,它包含了请求路径,请求方法,请求头等信息。其实可以看做是@RequestMapping的一个对应类。
- HandlerMethod这个类封装了处理器实例(Controller
Bean)和
处理方法实例(Method)以及方法参数数组(MethodParameter[])
- MethodParameter
这个类从2.0就有了,它封装了方法某个参数的相关信息及行为,如该参数的索引,该参数所属方法实例或构造器实例,该参数的类型等。
- HandlerMapping
该接口的实现类用来定义请求和处理器之前的映射关系,其中只定义了一个方法getHandler。
- AbstractHandlerMethodMapping
这是HandlerMapping的一个基本实现类,该类定义了请求与HandlerMethod实例的映射关系。
- RequestMappingInfoHandlerMapping这个是AbstractHandlerMethodMapping的实现类,他维护了一个RequestMappingInfo和HandlerMethod的Map属性。
- RequestMappingHandlerMapping
这个是RequestMappingInfoHandlerMapping的子类,它将@RequestMapping注解转化为RequestMappingInfo实例,并为父类使用。也就是我们处理@RequestMapping的终点。
- InitializingBean
这个接口定义了其实现Bean在容器完成属性设置后可以执行自定义初始化操作,我们的AbstractHandlerMethodMapping便实现了这个接口,并且定义了一组自定义操作,就是用来检测处理我们的@RequestMapping注解。
二、InitialiZingBean.afterPropertySet()
【Spring MVC — @RequestMapping原理讲解-1】 我们从头开始,看看到底Spring是怎样检测并处理我们@RequestMapping注解的。不知大家还记不记的这段代码:
[java] view plain copy
- Object exposedObject = bean;
- try {
- populateBean(beanName, mbd, instanceWrapper);
- if (exposedObject != null) {
- exposedObject = initializeBean(beanName, exposedObject, mbd);
- }
- }
上面概念中我们讲到InitiaizingBean接口,它的实现Bean会在容器完成属性注入后执行一个自定义操作,这不就满足initializeBean方法的执行唤醒嘛,我们来看它的实现:
[java] view plain copy
- protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {
- if (System.getSecurityManager() != null) {
- AccessController.doPrivileged(new PrivilegedAction< Object> () {
- public Object run() {
- invokeAwareMethods(beanName, bean);
- return null;
- }
- }, getAccessControlContext());
- }
- else {//这里检测当前Bean是否实现一些列Aware接口,并调用相关方法,我们不关心。
- invokeAwareMethods(beanName, bean);
- }
- Object wrappedBean = bean;
- if (mbd == null || !mbd.isSynthetic()) {//BeanPostProcessor 的回调,不关心
- wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
- }
- try {
- invokeInitMethods(beanName, wrappedBean, mbd); //这是我们需要关心的,下面看下它的实现
- }
- if (mbd == null || !mbd.isSynthetic()) {//BeanPostProcessor 的回调,不关心
- wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
- }
- return wrappedBean;
- }
[java] view plain copy
- protected void invokeInitMethods(String beanName, final Object bean, RootBeanDefinition mbd)
- throws Throwable {
- //是否是InitializingBean的实例
- boolean isInitializingBean = (bean instanceof InitializingBean);
- if (isInitializingBean & &
- (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
- if (System.getSecurityManager() != null) {
- try {
- AccessController.doPrivileged(new PrivilegedExceptionAction< Object> () {
- public Object run() throws Exception {//利用系统安全管理器调用
- ((InitializingBean) bean).afterPropertiesSet();
- return null;
- }
- }, getAccessControlContext());
- }
- }
- else {//调用InitializingBean的afterPropertiesSet方法。
- ((InitializingBean) bean).afterPropertiesSet();
- }
- }
- //调用自定义初始化方法。。。省略,不关心
- }
RequestMappingHandlerMapping
等Bean定义。而RequestMappingHandlerMapping
实现了InitializingBean接口,因此,在初始化并装配该Bean实例时,执行到上述代码是,便会执行他的afterPropertySet方法。我们接下来看看他的afterPropertySet方法:
[java] view plain copy
- public void afterPropertiesSet() {
- initHandlerMethods();
- }
- //Scan beans in the ApplicationContext, detect and register handler methods.
- protected void initHandlerMethods() {
- //扫描所有注册的Bean
- String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
- BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(),
- Object.class) : getApplicationContext().getBeanNamesForType(Object.class));
- //遍历这些Bean,依次判断是否是处理器,并检测其HandlerMethod
- for (String beanName : beanNames) {
- if (isHandler(getApplicationContext().getType(beanName))){
- detectHandlerMethods(beanName);
- }
- }
- //这个方法是个空实现,不管他
- handlerMethodsInitialized(getHandlerMethods());
- }
三、检测@RequestMapping
我们再看它是怎样判断是否是处理器的,以及怎么detect Handler Methods 的:
[java] view plain copy
- @Override
- protected boolean isHandler(Class< ?> beanType) {
- return ((AnnotationUtils.findAnnotation(beanType, Controller.class) != null) ||
- (AnnotationUtils.findAnnotation(beanType, RequestMapping.class) != null));
- }
[java] view plain copy
- protected void detectHandlerMethods(final Object handler) {
- Class< ?> handlerType = (handler instanceof String) ?
- getApplicationContext().getType((String) handler) : handler.getClass();
- final Class< ?> userType = ClassUtils.getUserClass(handlerType);
- Set< Method> methods = HandlerMethodSelector.selectMethods(userType, new MethodFilter(){
- public boolean matches(Method method) {//只选择被@RequestMapping标记的方法
- return getMappingForMethod(method, userType) != null;
- }
- });
- for (Method method : methods) {
- //根据方法上的@RequestMapping来创建RequestMappingInfo实例。
- T mapping = getMappingForMethod(method, userType);
- //注册请求映射
- registerHandlerMethod(handler, method, mapping);
- }
- }
下面我们看看细节:
[java] view plain copy
- @Override
- protected RequestMappingInfo getMappingForMethod(Method method, Class< ?> handlerType) {
- RequestMappingInfo info = null;
- //获取方法method上的@RequestMapping实例。
- RequestMapping methodAnnotation =
- AnnotationUtils.findAnnotation(method, RequestMapping.class);
- if (methodAnnotation != null) {//方法被注解了
- RequestCondition< ?> methodCondition = getCustomMethodCondition(method); //始终返回null
- info = createRequestMappingInfo(methodAnnotation, methodCondition); //创建MappingInfo
- //检查方法所属的类有没有@RequestMapping注解
- RequestMapping typeAnnotation = AnnotationUtils.findAnnotation(handlerType,
- RequestMapping.class);
- if (typeAnnotation != null) {//有类层次的@RequestMapping注解
- RequestCondition< ?> typeCondition = getCustomTypeCondition(handlerType); //null
- //将类层次的RequestMapping和方法级别的RequestMapping结合
- info = createRequestMappingInfo(typeAnnotation, typeCondition).combine(info);
- }
- }
- return info;
- }
[java] view plain copy
- private RequestMappingInfo createRequestMappingInfo(RequestMapping annotation,
- RequestCondition< ?> customCondition) {
- return new RequestMappingInfo(
- new PatternsRequestCondition(annotation.value(), getUrlPathHelper(), getPathMatcher(),
- this.useSuffixPatternMatch, this.useTrailingSlashMatch, this.fileExtensions),
- new RequestMethodsRequestCondition(annotation.method()),
- new ParamsRequestCondition(annotation.params()),
- new HeadersRequestCondition(annotation.headers()),
- new ConsumesRequestCondition(annotation.consumes(), annotation.headers()),
- new ProducesRequestCondition(annotation.produces(), annotation.headers(),
- getContentNegotiationManager()),
- customCondition
- );
- }
- PatternRequestCondition
它其实就是URL模式的封装,它包含了一个URL模式的Set集合。其实就是@RequestMapping注解中的value值得封装。
- RequestMethodRequestCondition
它是@RequestMapping 注解中method属性的封装
- ParamsRequestCondition
它是@RequestMapping注解中params属性的封装
下面我们再看看怎样进行Combine的:
[java] view plain copy
- public RequestMappingInfo combine(RequestMappingInfo other) {
- PatternsRequestCondition patterns = this.patternsCondition.combine(other.patternsCondition);
- RequestMethodsRequestCondition methods = this.methodsCondition.combine(other.methodsCondition);
- ParamsRequestCondition params = this.paramsCondition.combine(other.paramsCondition);
- HeadersRequestCondition headers = this.headersCondition.combine(other.headersCondition);
- ConsumesRequestCondition consumes = this.consumesCondition.combine(other.consumesCondition);
- ProducesRequestCondition produces = this.producesCondition.combine(other.producesCondition);
- RequestConditionHolder custom = this.customConditionHolder.combine(other.customConditionHolder);
- return new RequestMappingInfo(patterns, methods, params, headers, consumes,
- produces, custom.getCondition());
- }< span style="white-space:pre; "> < /span>
[java] view plain copy
- public PatternsRequestCondition combine(PatternsRequestCondition other) {
- Set< String> result = new LinkedHashSet< String> ();
- if (!this.patterns.isEmpty() & & !other.patterns.isEmpty()) {
- for (String pattern1 : this.patterns) {
- for (String pattern2 : other.patterns) {
- result.add(this.pathMatcher.combine(pattern1, pattern2));
- }
- }
- }
- else if (!this.patterns.isEmpty()) {
- result.addAll(this.patterns);
- }
- else if (!other.patterns.isEmpty()) {
- result.addAll(other.patterns);
- }
- else {
- result.add("");
- }
- return new PatternsRequestCondition(result, this.urlPathHelper, this.pathMatcher,
- this.useSuffixPatternMatch,this.useTrailingSlashMatch, this.fileExtensions);
- }
2)只有一个有时,使用这个。
3)两个都没有时,为空“”。
现在真正的url拼接是由PathMatcher来完成的了。我们就不看他的代码了就是一串if else的组合,重点是考虑进各种情况,我们来看下方法的注释吧:
文章图片
清晰,全面吧,有兴趣的可以看一下代码,这里不讲了。
四、注册请求映射
上面我们已经讲了@RequestMapping的检测和处理,并且根据@RequestMapping生成了RequestMappingInfo实例,那Spring必定需要将这些信息保存起来,以处理我们的请求。
第三节中我们提到一个方法还没有分析,就是registerHandlerMethod 方法:
[java] view plain copy
- protected void registerHandlerMethod(Object handler, Method method, T mapping) {
- HandlerMethod handlerMethod;
- if (handler instanceof String) {
- String beanName = (String) handler;
- handlerMethod = new HandlerMethod(beanName, getApplicationContext(), method);
- }
- else {
- handlerMethod = new HandlerMethod(handler, method);
- }
- //上面几行是根据新的处理器实例,方法实例,RequestMappingInfo来生成新的HandlerMethod实例
- //下面是从缓存中查看是否有存在的HandlerMethod实例,如果有并且不相等则抛出异常
- HandlerMethod oldHandlerMethod = handlerMethods.get(mapping);
- if (oldHandlerMethod != null & & !oldHandlerMethod.equals(handlerMethod)) {
- throw new IllegalStateException();
- }
- //handlerMethods 是一个Map键是RequestMappingInfo对象,值是HandlerMethod实例
- //因此一个HandlerMethod实例可能处理多个mapping,而一个mapping实例只能由一个method处理
- this.handlerMethods.put(mapping, handlerMethod);
- //这里获取mapping实例中的所有url。
- Set< String> patterns = getMappingPathPatterns(mapping);
- for (String pattern : patterns) {
- if (!getPathMatcher().isPattern(pattern)) {
- //urlMap也是Map,键是url 模式,值是RequestMappingInfo实例
- //因此一个mapping实例可能对应多个pattern,但是一个pattern只能对应一个mapping实例
- this.urlMap.add(pattern, mapping);
- }
- }
- }
这里可能稍微有点绕,其实道理很简单,当请求到达时,去urlMap中需找匹配的url,以及获取对应mapping实例,然后去handlerMethods中获取匹配HandlerMethod实例。
五、承上启下
篇幅有些长了,超出字数限制了,只能分成两篇了..........................
这章只分析了我们前面三个问题中的第一个,但是已经相当接近了。下一篇我们来讲,Spring怎样处理客户发来的请求,以及方法调用的。
推荐阅读
- Android.mk函数进阶教程 - 分支循环子程序
- Android.mk 依赖(目标编程的模式)
- Android 解决qq分享后返回程序出现的Bug
- 本文教您神州笔记本bios设置u盘打开的办法
- 本文教您华硕笔记本设置u盘打开办法
- 本文教您解除u盘加密的办法
- 本文教您u盘显示0字节怎样办
- 本文教您u盘读写速度多少是正常的
- 本文教您怎样用u盘装系统