2016-03-22 15:29 140人阅读 评论(0) 收藏 举报
 分类:
Spring(4) 

版权声明:本文为博主原创文章,未经博主允许不得转载。

在springmvc中,控制器(controller)是一个很重要的概念。在实际项目中,我们一般在控制器里完成具体的业务逻辑。控制器是非常重要,因此讨论控制器的产生和构建就变得很有意义(PS:我们在这里主要讨论基于注解的配置方式)。

在讨论控制器的相关问题之前,需要考虑的第一个问题是:ApplicationContext的类型是如何确定的?ApplicationContext是spring的IOC机制实现的一个核心,spring的很多功能都是通过ApplicationContext对外输出的。而springmvc的ApplicationContext则是“写死”在FrameworkServlet这个类的field中。

FrameworkServlet.Java

  1. public abstract class FrameworkServlet extends HttpServletBean {
  2. /**
  3. * Suffix for WebApplicationContext namespaces. If a servlet of this class is
  4. * given the name "test" in a context, the namespace used by the servlet will
  5. * resolve to "test-servlet".
  6. */
  7. public static final String DEFAULT_NAMESPACE_SUFFIX = "-servlet";
  8. /**
  9. * Default context class for FrameworkServlet.
  10. * @see org.springframework.web.context.support.XmlWebApplicationContext
  11. */
  12. public static final Class<?> DEFAULT_CONTEXT_CLASS = XmlWebApplicationContext.class;
  1. //some other code

而在XmlWebApplicationContext中,spring“手动”创建了一个ApplicationContextAwareProcessor,并将它注册到beanfactory的beanPostProcessor列表中,具体代码如下:

  1. beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
  1. public void addBeanPostProcessor(BeanPostProcessor beanPostProcessor) {
  2. Assert.notNull(beanPostProcessor, "BeanPostProcessor must not be null");
  3. this.beanPostProcessors.remove(beanPostProcessor);
  4. this.beanPostProcessors.add(beanPostProcessor);
  5. if (beanPostProcessor instanceof InstantiationAwareBeanPostProcessor) {
  6. this.hasInstantiationAwareBeanPostProcessors = true;
  7. }
  8. if (beanPostProcessor instanceof DestructionAwareBeanPostProcessor) {
  9. this.hasDestructionAwareBeanPostProcessors = true;
  10. }
  11. }

至此,产生和构建控制器的前置步骤就完成了。

控制器的产生:打开@Controller这一注解的源代码,你会发现该注解会被@Component这一注解所修饰。因此,所有被@Controller所修饰的类都会默认被@Component所修饰。同时这也意味着搭配<context:component-scan base-package="xxx.xxxx.xxxx" />这一标签,所有被@Controller所修饰的类都会被注册成为JavaBean。这个JavaBean与其它自定义的JavaBean没有什么区别。而重要的区别则在于被@Controller所修饰的类,能够被之前注册的BeanPostProcessor所扫描并进行处理。

控制器的构建:springmvc将构建控制器的这一工作交给了BeanPostProcessor进行处理。在<mvc:annotation-driven/>这一标签里,springmvc创建了一个JavaBean,这个bean是RequestMappingHandlerMapping。当这个JavaBean被反射出来但是还没有被初始化的时候,BeanPostProcessor的postProcessBeforeInitialization会发挥作用。当然,由于会首先对JavaBean进行过滤。具体代码如下:

  1. if (bean instanceof ApplicationContextAware) {
  2. ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
  3. }

RequestMappingHandlerMapping会使用Applicationcontext的getBeanNamesForType函数去查找所有基类是Object的JavaBean。显而易见,所有的JavaBean都会通过该方法被扫描出来。然后对其中那些被@Controller所修饰的类进行进一步探测和处理。具体代码如下:

RequestMappingHandlerMapping扫描JavaBean并处理的方法:

  1. protected void initHandlerMethods() {
  2. if (logger.isDebugEnabled()) {
  3. logger.debug("Looking for request mappings in application context: " + getApplicationContext());
  4. }
  5. String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
  6. BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
  7. getApplicationContext().getBeanNamesForType(Object.class));
  8. for (String beanName : beanNames) {
  9. if (isHandler(getApplicationContext().getType(beanName))){
  10. detectHandlerMethods(beanName);
  11. }
  12. }
  13. handlerMethodsInitialized(getHandlerMethods());
  14. }

isHandler方法(ps:判断是否为控制器):

  1. protected boolean isHandler(Class<?> beanType) {
  2. return AnnotationUtils.findAnnotation(beanType, Controller.class) != null;
  3. }

RequestMappingHandlerMapping中一个很重要的函数是detectHandlerMethods。该函数具体代码如下:

  1. protected void detectHandlerMethods(final Object handler) {
  2. Class<?> handlerType = (handler instanceof String) ?
  3. getApplicationContext().getType((String) handler) : handler.getClass();
  4. final Class<?> userType = ClassUtils.getUserClass(handlerType);
  5. Set<Method> methods = HandlerMethodSelector.selectMethods(userType, new MethodFilter() {
  6. public boolean matches(Method method) {
  7. return getMappingForMethod(method, userType) != null;
  8. }
  9. });
  10. for (Method method : methods) {
  11. T mapping = getMappingForMethod(method, userType);
  12. registerHandlerMethod(handler, method, mapping);
  13. }
  14. }

可以看到,该函数主要工作是查找handler中的所有method,并根据method生成对应的mapping,并将mapping,method和handler进行注册。而其中最重要的函数则是getMappingForMethod。该函数的主要逻辑是根据method产生一个RequestMappingInfo,然后根据handlerType产生另一个RequestMappingInfo。最后将这两个RequestMappingInfo进行合并。该函数代码如下:

  1. protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
  2. RequestMappingInfo info = null;
  3. RequestMapping methodAnnotation = AnnotationUtils.findAnnotation(method, RequestMapping.class);
  4. if (methodAnnotation != null) {
  5. RequestCondition<?> methodCondition = getCustomMethodCondition(method);
  6. info = createRequestMappingInfo(methodAnnotation, methodCondition);
  7. RequestMapping typeAnnotation = AnnotationUtils.findAnnotation(handlerType, RequestMapping.class);
  8. if (typeAnnotation != null) {
  9. RequestCondition<?> typeCondition = getCustomTypeCondition(handlerType);
  10. info = createRequestMappingInfo(typeAnnotation, typeCondition).combine(info);
  11. }
  12. }
  13. return info;
  14. }

RequestMappingHandlerMapping通过registerHandlerMethod函数对mapping,handler和method进行注册,RequestMappingHandlerMapping会生成一个LinkedHashMap,并以mapping为key,以handler和method为value。 同时在注册时会检测mapping的是否重复。至此,controller的构建所示完成了。
  既然是以mapping为可以,那么必然会涉及equals函数和hashcode函数,下面是RequestMappingInfode 的equals函数以及hashcode函数。

  1. public int hashCode() {
  2. int result = hash;
  3. if (result == 0) {
  4. result = patternsCondition.hashCode();
  5. result = 31 * result + methodsCondition.hashCode();
  6. result = 31 * result + paramsCondition.hashCode();
  7. result = 31 * result + headersCondition.hashCode();
  8. result = 31 * result + consumesCondition.hashCode();
  9. result = 31 * result + producesCondition.hashCode();
  10. result = 31 * result + customConditionHolder.hashCode();
  11. hash = result;
  12. }
  13. return result;
  14. }
  1. public boolean equals(Object obj) {
  2. if (this == obj) {
  3. return true;
  4. }
  5. if (obj != null && obj instanceof RequestMappingInfo) {
  6. RequestMappingInfo other = (RequestMappingInfo) obj;
  7. return (this.patternsCondition.equals(other.patternsCondition) &&
  8. this.methodsCondition.equals(other.methodsCondition) &&
  9. this.paramsCondition.equals(other.paramsCondition) &&
  10. this.headersCondition.equals(other.headersCondition) &&
  11. this.consumesCondition.equals(other.consumesCondition) &&
  12. this.producesCondition.equals(other.producesCondition) &&
  13. this.customConditionHolder.equals(other.customConditionHolder));
  14. }
  15. return false;
  16. }
 
0

SpringMvc学习心得(五)控制器产生与构建的更多相关文章

  1. springmvc学习(五)——处理模型数据

    Spring MVC 提供了以下几种途径输出模型数据: ModelAndView: 处理方法返回值类型为 ModelAndView 时, 方法体即可通过该对象添加模型数据Map 及 Model: 入参 ...

  2. SpringMVC学习记录(五)--表单标签

    在使用SpringMVC的时候我们能够使用Spring封装的一系列表单标签,这些标签都能够訪问到ModelMap中的内容. 以下将对这些标签一一介绍. 1.引入标签头文件 在正式介绍SpringMVC ...

  3. SpringMVC学习03(控制器Controller)

    3.控制器Controller 3.1 控制器Controller 控制器复杂提供访问应用程序的行为,通常通过接口定义或注解定义两种方法实现. 控制器负责解析用户的请求并将其转换为一个模型. 在Spr ...

  4. springMVC学习笔记(五)

    一.使用Ajax调用 1.1 Controller返回的类型为text类型的方式. @RequestMapping("/getPerson") public void getPer ...

  5. SpringMVC学习总结(五)——SpringMVC文件上传例子

    这是用的是SpringMVC-3.1.1.commons-fileupload-1.2.2和io-2.0.1 首先是web.xml <?xml version="1.0" e ...

  6. springmvc学习(五)

    这次主要是记录一下 springmvc 关于异常处理 和 拦截的回顾 关于springmvc  异常处理:springmvc 提供了 HandlerExceptionResolver  异常处理解析接 ...

  7. Maven学习(五)使用Maven构建多模块项目

    使用Maven构建多模块项目 一般的web项目构成: 建立解决方案目录parent 首先使用命令进入到我们需要建立maven项目的目录: mvn archetype:generate -DgroupI ...

  8. SpringMVC学习笔记五:使用converter进行参数数据转换

    转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/6832898.html  一:SpringMVC数据绑定机制 1:request到达SpringMVC框架时,框 ...

  9. SpringMVC学习(五)——拦截器示例

    部分内容摘自开涛的<跟我学SpringMVC.PDF> 拦截器,本质类似于AOP,主要的应用场景: 1.日志记录:记录请求信息的日志,以便进行信息监控.信息统计.计算PV等. 2.权限检查 ...

随机推荐

  1. 在virtualbox下使用vm映像文件

    virtualbox可以直接打开vmdk 创建虚拟机时先不要创建虚拟硬盘. 虚拟机创建成功后,在设置窗口,点击[存储],添加虚拟硬盘,点击选择现有的虚拟盘. 参考链接

  2. android 进程/线程管理(四)----消息机制的思考(自定义消息机制)

    关于android消息机制 已经写了3篇文章了,想要结束这个系列,总觉得少了点什么? 于是我就在想,android为什么要这个设计消息机制,使用消息机制是现在操作系统基本都会有的特点. 可是andro ...

  3. 【转载】小米2进入recovery的方法

    用过M1的朋友都多多少少的了解到~进入recovery是关机下同时 按 音量(+)+电源键. 其实M2也一样,但是我觉得是有点区别的. 在M1的时候,只要同时长按这两个键就可以的了. 但是M2呢?我发 ...

  4. 设计模式 --- 模型-视图-控制器(Model View Controller)

    模型-视图-控制器(Model-View-Controller,MVC)是Xerox PARC在20世纪80年代为编程语言Smalltalk-80发明的一种软件设计模式,至今已广泛应用于用户交互应用程 ...

  5. 使用网站websequencediagrams在线画时序图

    在线画时序图的网站:https://www.websequencediagrams.com/ 该网站提供拖拉图形和编写脚本代码2个方式来制作时序图,同时提供多种显示风格. 实例: 1.脚本代码: ti ...

  6. rails开发demo(一)搭建环境

    环境 ubuntu 14.04 为了安装rails4.2 和 ruby 2.2.2,这是目前稳定的最新版本,需要先安装rvm sudo apt-get update sudo apt-get inst ...

  7. Web Application Project is configured to use IIS. Unable to access the IIS metabase.(配置为使用IIS Web应用程序xxxx项目。无法访问IIS元数据库。)

    这几天重装系统,装了win10,居然用vs2013打开项目出现下面这个提示错误,搞了很久才知道原因: Even though I am an administrator on the machine, ...

  8. linux常用查看日志命令

    转自:http://yinfeifei.iteye.com/blog/779217 1.cat命令: 功能:1)显示整个文件. 示例: $ cat fileName 2)把文件串连接后传到基本输出,如 ...

  9. python strip() lstrip() rstrip() 使用方法

    Python中的strip用于去除字符串的首尾字符串,同理,lstrip用于去除最左边的字符,rstrip用于去除最右边的字符. 这三个函数都可传入一个参数,指定要去除的首尾字符. 需要注意的是,传入 ...

  10. 小心sae的jvm异常导致的Error 404 – Not Found.No context on this server matched or handled this request.

    本来用着sae好好的,结果第二天部署的应用突然不好使了,各种Error 404 – Not Found.No context on this server matched or handled thi ...