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. Effective Java 44 Write doc comments for all exposed API elements

    Principle You must precede every exported class, interface, constructor, method, and field declarati ...

  2. Outlook 2013 在邮件里面点击超链接时弹出“组织策略阻止我们为您完成此操作”

    现象描叙:     在Outlook在邮件里面点击超链接时,打不开超链接页面,弹出如下提示: 这个是因为之前安装了其它浏览器(例如,我安装了360的浏览器),并且设置为了默认浏览器,后来卸载了该浏览器 ...

  3. 【SQL查询】集合查询之INTERSECT

    [SQL查询]集合查询之INTERSECT 1  BLOG文档结构图 2  前言部分 2.1  导读和注意事项 各位技术爱好者,看完本文后,你可以掌握如下的技能,也可以学到一些其它你所不知道的知识,~ ...

  4. 读书摘要:第七章 闩Suan锁和自旋锁

    摘要: 1.闩锁就像是内存上的锁,随着越来越多的线程参与进来,他们争相访问同一块内存,导致堵塞.2.自旋锁就是闩锁,不同之处是如果访问的内存不可用,它将继续检查轮询一段时间.3.拴锁和自旋锁是我们无法 ...

  5. Sublime Text3 C++及Java开发环境配置

    一.C++开发环境配置 1. 下载MingW 2. 环境变量配置,系统属性->高级设置->环境变量,如果Mingw装在c盘更目录,其它自己思考 (1)PATH  变量值中加入 C:\Min ...

  6. 虚拟机ping不通主机,但是主机可以ping通虚拟机(转载)

    我在Windows7系统安装了虚拟机,通过虚拟机安装了Ubuntu13.04,我设置的主机与虚拟机的连接方式是桥接,安装好后,发现虚拟机ping不通主机,但是主机可以ping通虚拟机. 我的操作是:关 ...

  7. Error: Could not access the Package Manager. Is the system running?

    最近在搭建cordova,android 开发环境,安装android studio之后创建一个demo之后,运行想看一下效果,在运行过程中创建一个虚拟机(arm)的,等了有1分钟左右,再次运行程序, ...

  8. 虚拟机Linux----Ubuntu1204----安装jdk1.8

    1.介绍 这里主要讲一下,如何在Ubuntu1204下通过压缩包的方式安装jdk1.8,rpm的直接运行就行了. 2.步骤 2.1 下载 地址:http://www.oracle.com/techne ...

  9. python 标准库和第3方库的介绍

    忘了从哪里来的了~~~~ Tkinter———— Python默认的图形界面接口.Tkinter是一个和Tk接口的模块,Tkinter库提供了对Tk API的接口,它属于Tcl/Tk的GUI工具组.T ...

  10. [转] KVM Internals, code and more

    KVM Kernel-based Virtual Machine Internals, code and more http://slides.com/braoru/kvm#/ What behind ...