SpringMvc学习心得(五)控制器产生与构建

版权声明:本文为博主原创文章,未经博主允许不得转载。
在springmvc中,控制器(controller)是一个很重要的概念。在实际项目中,我们一般在控制器里完成具体的业务逻辑。控制器是非常重要,因此讨论控制器的产生和构建就变得很有意义(PS:我们在这里主要讨论基于注解的配置方式)。
在讨论控制器的相关问题之前,需要考虑的第一个问题是:ApplicationContext的类型是如何确定的?ApplicationContext是spring的IOC机制实现的一个核心,spring的很多功能都是通过ApplicationContext对外输出的。而springmvc的ApplicationContext则是“写死”在FrameworkServlet这个类的field中。
FrameworkServlet.Java
- public abstract class FrameworkServlet extends HttpServletBean {
- /**
- * Suffix for WebApplicationContext namespaces. If a servlet of this class is
- * given the name "test" in a context, the namespace used by the servlet will
- * resolve to "test-servlet".
- */
- public static final String DEFAULT_NAMESPACE_SUFFIX = "-servlet";
- /**
- * Default context class for FrameworkServlet.
- * @see org.springframework.web.context.support.XmlWebApplicationContext
- */
- public static final Class<?> DEFAULT_CONTEXT_CLASS = XmlWebApplicationContext.class;
- //some other code
而在XmlWebApplicationContext中,spring“手动”创建了一个ApplicationContextAwareProcessor,并将它注册到beanfactory的beanPostProcessor列表中,具体代码如下:
- beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
- public void addBeanPostProcessor(BeanPostProcessor beanPostProcessor) {
- Assert.notNull(beanPostProcessor, "BeanPostProcessor must not be null");
- this.beanPostProcessors.remove(beanPostProcessor);
- this.beanPostProcessors.add(beanPostProcessor);
- if (beanPostProcessor instanceof InstantiationAwareBeanPostProcessor) {
- this.hasInstantiationAwareBeanPostProcessors = true;
- }
- if (beanPostProcessor instanceof DestructionAwareBeanPostProcessor) {
- this.hasDestructionAwareBeanPostProcessors = true;
- }
- }
至此,产生和构建控制器的前置步骤就完成了。
控制器的产生:打开@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进行过滤。具体代码如下:
- if (bean instanceof ApplicationContextAware) {
- ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
- }
RequestMappingHandlerMapping会使用Applicationcontext的getBeanNamesForType函数去查找所有基类是Object的JavaBean。显而易见,所有的JavaBean都会通过该方法被扫描出来。然后对其中那些被@Controller所修饰的类进行进一步探测和处理。具体代码如下:
RequestMappingHandlerMapping扫描JavaBean并处理的方法:
- 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 (isHandler(getApplicationContext().getType(beanName))){
- detectHandlerMethods(beanName);
- }
- }
- handlerMethodsInitialized(getHandlerMethods());
- }
isHandler方法(ps:判断是否为控制器):
- protected boolean isHandler(Class<?> beanType) {
- return AnnotationUtils.findAnnotation(beanType, Controller.class) != null;
- }
RequestMappingHandlerMapping中一个很重要的函数是detectHandlerMethods。该函数具体代码如下:
- 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) {
- return getMappingForMethod(method, userType) != null;
- }
- });
- for (Method method : methods) {
- T mapping = getMappingForMethod(method, userType);
- registerHandlerMethod(handler, method, mapping);
- }
- }
可以看到,该函数主要工作是查找handler中的所有method,并根据method生成对应的mapping,并将mapping,method和handler进行注册。而其中最重要的函数则是getMappingForMethod。该函数的主要逻辑是根据method产生一个RequestMappingInfo,然后根据handlerType产生另一个RequestMappingInfo。最后将这两个RequestMappingInfo进行合并。该函数代码如下:
- protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
- RequestMappingInfo info = null;
- RequestMapping methodAnnotation = AnnotationUtils.findAnnotation(method, RequestMapping.class);
- if (methodAnnotation != null) {
- RequestCondition<?> methodCondition = getCustomMethodCondition(method);
- info = createRequestMappingInfo(methodAnnotation, methodCondition);
- RequestMapping typeAnnotation = AnnotationUtils.findAnnotation(handlerType, RequestMapping.class);
- if (typeAnnotation != null) {
- RequestCondition<?> typeCondition = getCustomTypeCondition(handlerType);
- info = createRequestMappingInfo(typeAnnotation, typeCondition).combine(info);
- }
- }
- return info;
- }
RequestMappingHandlerMapping通过registerHandlerMethod函数对mapping,handler和method进行注册,RequestMappingHandlerMapping会生成一个LinkedHashMap,并以mapping为key,以handler和method为value。 同时在注册时会检测mapping的是否重复。至此,controller的构建所示完成了。
既然是以mapping为可以,那么必然会涉及equals函数和hashcode函数,下面是RequestMappingInfode 的equals函数以及hashcode函数。
- public int hashCode() {
- int result = hash;
- if (result == 0) {
- result = patternsCondition.hashCode();
- result = 31 * result + methodsCondition.hashCode();
- result = 31 * result + paramsCondition.hashCode();
- result = 31 * result + headersCondition.hashCode();
- result = 31 * result + consumesCondition.hashCode();
- result = 31 * result + producesCondition.hashCode();
- result = 31 * result + customConditionHolder.hashCode();
- hash = result;
- }
- return result;
- }
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
- if (obj != null && obj instanceof RequestMappingInfo) {
- RequestMappingInfo other = (RequestMappingInfo) obj;
- return (this.patternsCondition.equals(other.patternsCondition) &&
- this.methodsCondition.equals(other.methodsCondition) &&
- this.paramsCondition.equals(other.paramsCondition) &&
- this.headersCondition.equals(other.headersCondition) &&
- this.consumesCondition.equals(other.consumesCondition) &&
- this.producesCondition.equals(other.producesCondition) &&
- this.customConditionHolder.equals(other.customConditionHolder));
- }
- return false;
- }

- 顶
- 0
- 踩
SpringMvc学习心得(五)控制器产生与构建的更多相关文章
- springmvc学习(五)——处理模型数据
Spring MVC 提供了以下几种途径输出模型数据: ModelAndView: 处理方法返回值类型为 ModelAndView 时, 方法体即可通过该对象添加模型数据Map 及 Model: 入参 ...
- SpringMVC学习记录(五)--表单标签
在使用SpringMVC的时候我们能够使用Spring封装的一系列表单标签,这些标签都能够訪问到ModelMap中的内容. 以下将对这些标签一一介绍. 1.引入标签头文件 在正式介绍SpringMVC ...
- SpringMVC学习03(控制器Controller)
3.控制器Controller 3.1 控制器Controller 控制器复杂提供访问应用程序的行为,通常通过接口定义或注解定义两种方法实现. 控制器负责解析用户的请求并将其转换为一个模型. 在Spr ...
- springMVC学习笔记(五)
一.使用Ajax调用 1.1 Controller返回的类型为text类型的方式. @RequestMapping("/getPerson") public void getPer ...
- SpringMVC学习总结(五)——SpringMVC文件上传例子
这是用的是SpringMVC-3.1.1.commons-fileupload-1.2.2和io-2.0.1 首先是web.xml <?xml version="1.0" e ...
- springmvc学习(五)
这次主要是记录一下 springmvc 关于异常处理 和 拦截的回顾 关于springmvc 异常处理:springmvc 提供了 HandlerExceptionResolver 异常处理解析接 ...
- Maven学习(五)使用Maven构建多模块项目
使用Maven构建多模块项目 一般的web项目构成: 建立解决方案目录parent 首先使用命令进入到我们需要建立maven项目的目录: mvn archetype:generate -DgroupI ...
- SpringMVC学习笔记五:使用converter进行参数数据转换
转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/6832898.html 一:SpringMVC数据绑定机制 1:request到达SpringMVC框架时,框 ...
- SpringMVC学习(五)——拦截器示例
部分内容摘自开涛的<跟我学SpringMVC.PDF> 拦截器,本质类似于AOP,主要的应用场景: 1.日志记录:记录请求信息的日志,以便进行信息监控.信息统计.计算PV等. 2.权限检查 ...
随机推荐
- Effective Java 44 Write doc comments for all exposed API elements
Principle You must precede every exported class, interface, constructor, method, and field declarati ...
- Outlook 2013 在邮件里面点击超链接时弹出“组织策略阻止我们为您完成此操作”
现象描叙: 在Outlook在邮件里面点击超链接时,打不开超链接页面,弹出如下提示: 这个是因为之前安装了其它浏览器(例如,我安装了360的浏览器),并且设置为了默认浏览器,后来卸载了该浏览器 ...
- 【SQL查询】集合查询之INTERSECT
[SQL查询]集合查询之INTERSECT 1 BLOG文档结构图 2 前言部分 2.1 导读和注意事项 各位技术爱好者,看完本文后,你可以掌握如下的技能,也可以学到一些其它你所不知道的知识,~ ...
- 读书摘要:第七章 闩Suan锁和自旋锁
摘要: 1.闩锁就像是内存上的锁,随着越来越多的线程参与进来,他们争相访问同一块内存,导致堵塞.2.自旋锁就是闩锁,不同之处是如果访问的内存不可用,它将继续检查轮询一段时间.3.拴锁和自旋锁是我们无法 ...
- Sublime Text3 C++及Java开发环境配置
一.C++开发环境配置 1. 下载MingW 2. 环境变量配置,系统属性->高级设置->环境变量,如果Mingw装在c盘更目录,其它自己思考 (1)PATH 变量值中加入 C:\Min ...
- 虚拟机ping不通主机,但是主机可以ping通虚拟机(转载)
我在Windows7系统安装了虚拟机,通过虚拟机安装了Ubuntu13.04,我设置的主机与虚拟机的连接方式是桥接,安装好后,发现虚拟机ping不通主机,但是主机可以ping通虚拟机. 我的操作是:关 ...
- Error: Could not access the Package Manager. Is the system running?
最近在搭建cordova,android 开发环境,安装android studio之后创建一个demo之后,运行想看一下效果,在运行过程中创建一个虚拟机(arm)的,等了有1分钟左右,再次运行程序, ...
- 虚拟机Linux----Ubuntu1204----安装jdk1.8
1.介绍 这里主要讲一下,如何在Ubuntu1204下通过压缩包的方式安装jdk1.8,rpm的直接运行就行了. 2.步骤 2.1 下载 地址:http://www.oracle.com/techne ...
- python 标准库和第3方库的介绍
忘了从哪里来的了~~~~ Tkinter———— Python默认的图形界面接口.Tkinter是一个和Tk接口的模块,Tkinter库提供了对Tk API的接口,它属于Tcl/Tk的GUI工具组.T ...
- [转] KVM Internals, code and more
KVM Kernel-based Virtual Machine Internals, code and more http://slides.com/braoru/kvm#/ What behind ...