SpringMVC学习记录1
起因
以前大三暑假实习的时候看到公司用SpringMVC而不是Struts2,老司机告诉我SpringMVC各种方便,各种解耦.
然后我自己试了试..好像是蛮方便的....
基本上在Spring的基础上配置一小段就可以了....
但是我一直搞不明白...像我用Struts2的时候需要继承框架的Action父类,所以请求可以委派过来...但是SpringMVC自己写的Controller完全就是POJO.那为什么请求还可以委派过来涅?
最近有点空...稍微研究了一下SpringMVC....算是对它有了点新的理解...
mvc:annotation-driven到底干了啥?
SpringMVC插入的一小段配置中有一段就是mvc:annotation-driven......这个神奇的配置到底干了啥?为什么加了就可以用@Controller来接受请求了呢?why????
查阅资料发现.这个配置会加载2个bean
<mvc:annotation-driven /> 会自动注册DefaultAnnotationHandlerMapping与AnnotationMethodHandlerAdapter 两个bean,是spring MVC为@Controllers分发请求所必须的。
但是实际上我去看了下SpringMVC的代码..发现这2个类以及过期了(我的SPringMVC版本是4.1.7)...现在实际上现在注册的是RequestMappingHandlerMapping和RequestMappingHandlerAdapter..如下图
这里之所以@Controller能起作用主要是靠RequestMappingHandlerMapping.....所以我看了一下RequestMappingHandlerMapping的代码...
RequestMappingHandlerMapping
RequestMappingHandlerMapping的是实现了InitializingBean接口的
所以当各种bean初始化以后,属性被设置以后RequestMappingHandlerMapping的afterPropertiesSet方法会被调用.(请参考bean的生命周期http://www.cnblogs.com/zrtqsk/p/3735273.html).
/**
* Detects handler methods at initialization.
*/
@Override
public void afterPropertiesSet() {
initHandlerMethods();
} /**
* Scan beans in the ApplicationContext, detect and register handler methods.
* @see #isHandler(Class)
* @see #getMappingForMethod(Method, Class)
* @see #handlerMethodsInitialized(Map)
*/
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) &&
isHandler(getApplicationContext().getType(beanName))){
detectHandlerMethods(beanName);
}
}
handlerMethodsInitialized(getHandlerMethods());
}
afterPropertiesSet会调用initHandlerMethods
initHandlerMethods里22行会把所有的bean都取出来.然后26行一个一个去做isHandler方法
@Override
protected boolean isHandler(Class<?> beanType) {
return ((AnnotationUtils.findAnnotation(beanType, Controller.class) != null) ||
(AnnotationUtils.findAnnotation(beanType, RequestMapping.class) != null));
}
从isHandler方法里可以看到因为我们自己写的Controller上面有annotation @Controller所以isHandler返回是true...
从代码中我们额外还可以发现...如果我们的Controller上面有注解@RequestMapping....那我们不写@Controller也是可以的....(⊙﹏⊙)b
有@Controller注解的类会接下去做detectHandlerMethods方法(27行).
/**
* Look for handler methods in a handler.
* @param handler the bean name of a handler or a handler instance
*/
protected void detectHandlerMethods(final Object handler) {
Class<?> handlerType =
(handler instanceof String ? getApplicationContext().getType((String) handler) : handler.getClass()); // Avoid repeated calls to getMappingForMethod which would rebuild RequestMappingInfo instances
final Map<Method, T> mappings = new IdentityHashMap<Method, T>();
final Class<?> userType = ClassUtils.getUserClass(handlerType); Set<Method> methods = HandlerMethodSelector.selectMethods(userType, new MethodFilter() {
@Override
public boolean matches(Method method) {
T mapping = getMappingForMethod(method, userType);
if (mapping != null) {
mappings.put(method, mapping);
return true;
}
else {
return false;
}
}
}); for (Method method : methods) {
registerHandlerMethod(handler, method, mappings.get(method));
}
}
看这个方法的名字我们大概也能知道它是干啥的...第16行getMappingForMethod会去把这个Controller里标注了@RequestMapping的方法全部找出来..如下面代码片段
@Override
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;
}
全部找出来以后再做27行-29行
for (Method method : methods) {
registerHandlerMethod(handler, method, mappings.get(method));
}
把这个标注为@RequestMapping的方法注册一下.....
总结
1.这样就能解释为了我们标注了@Controller的方法里面的标注了@RequestMapping的方法能够接受到请求...即使我们没有继承SpringMVC的任何类...
这全靠RequestMappingHandlerMapping.它参与了Spring bean的生命周期...会将所有bean一个一个检测...只要这个bean被标注了Controller注解.就会将@RequestMapping标注的方法取出来并注册..
2.至此以前另外一个疑问也有了解答..为什么@Controller标注的bean与<mvc:annotation-driven>标签都需要配置在SpringMVC的配置里而不是Spring里.(全部配置在Spring里也可以.必须都在一起.)
首先, Spring不需要注入Controller的bean,Controller的bean只有SpringMVC需要用到.但这个不是重点.
重点是其次, mvc:annotation-driven会注入参与Spring bean生命周期的特殊的bean,他们会检测Controller的bean.
如果情况1:这些特殊的bean是由Spring加载的,而Controller bean是由SpringMVC加载的.那这些特殊的bean是检测不到Controller bean的.因为Spring父环境是由ServletContextListener加载的,必定先于DispatcherServlet加载的子环境.所以创建Spring的XMLWebApplicationContext的时候Controller bean都还未加载.
情况2:Controller bean由Spring加载,<mvc:annotation-driven>写在SpringMVC里.
AbstractHandlerMethodMapping.class 1 String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
getApplicationContext().getBeanNamesForType(Object.class));
detectHandlerMethodsInAncestorContexts默认是false.所以SpringMVC的xmlWebApplicationContext不会去父容器找bean.只会在自身这里找Object.class.所以也不会包括Controller bean.那结果可想而知. 解决办法是可以通过设置detectHandlerMethodsInAncestorContexts 为true来解决.
但是最简单的办法就是@Controller标注的bean与<mvc:annotation-driven>标签都配置在SpringMVC的配置文件里.
SpringMVC学习记录1的更多相关文章
- springMVC学习记录1-使用XML进行配置
SpringMVC是整个spring中的一个很小的组成,准确的说他是spring WEB这个模块的下一个子模块,Spring WEB中除了有springMVC还有struts2,webWork等MVC ...
- SpringMVC学习记录5
Springmvc流程中的扩展点有很多,可以在很多地方插入自己的代码逻辑达到控制流程的目的. 如果要对Controller的handler方法做统一的处理.我想应该会有很多选择,比如:@ModelAt ...
- SpringMVC学习记录4
主题 SpringMVC有很多很多的注解.其中有2个注解@SessionAttributes @ModelAttribute我平时一般不用,因为实在是太灵活了.但是又有一定限制,用不好容易错.. 最近 ...
- SpringMVC学习记录3
这次的主题 最近一直在学习SpringMVC..(这句话我已经至少写了3,4遍了....).这次的研究主要是RequestMappingHandlerAdapter中的各种ArgumentsResol ...
- SpringMVC学习记录2
废话 最近在看SpringMVC...里面东西好多...反正东看一点西看一点吧... 分享一下最近的一些心得..是关于DispatcherServlet的 DispatcherServlet与Cont ...
- SpringMVC学习记录
1E)Spring MVC框架 ①Jar包结构: docs+libs+schema. 版本区别:核心包,源码包. SpringMVC文档学习: 学习三步骤: 1)是什么? 开源框架 2)做什么? IO ...
- springMVC学习记录2-使用注解配置
前面说了一下使用xml配置springmvc,下面再说说注解配置.项目如下: 业务很简单,主页和输入用户名和密码进行登陆的页面. 看一下springmvc的配置文件: <?xml version ...
- springMVC学习记录3-拦截器和文件上传
拦截器和文件上传算是springmvc中比较高级一点的内容了吧,让我们一起看一下. 下面先说说拦截器.拦截器和过滤器有点像,都可以在请求被处理之前和请求被处理之到做一些额外的操作. 1. 实现Hand ...
- SpringMVC学习记录七——sjon数据交互和拦截器
21 json数据交互 21.1 为什么要进行json数据交互 json数据格式在接口调用中.html页面中较常用,json格式比较简单,解析还比较方便. 比如:webservi ...
随机推荐
- python安装后推荐的安装两款文本编辑器
Notepad++ 7.2.2和 Sublime Text --道心 Notepad++ 7.2.2 Notepad++ 是一款非常有特色的编辑器,是开源软件,可以免费使用.支持的语言: C, C++ ...
- 在Ubuntu Server 14.04中搭建FTP服务器(VMWare)
自己搭建ftp服务器,方便主机与虚拟机中的Ubuntu传输文件. 选用的ftp软件为vsftpd. 1.命令行: sudo apt-get install vsftpd 2.安装完配置: vsftpd ...
- TCP协议学习
一.TCP参考模型 VS OSI参考模型 二.TCP/IP分层模型的四个协议层分别完成以下的功能 第一层 网络接口层 网络接口层包括用于协作IP数据在已有网络介质上传输的协议.实际上TCP/IP标准 ...
- Vim Using
1 2 set nu 3 set backup 4 set bg=light 5 " transform tab to space 6 set expandtab 7 " auto ...
- JQuery 滚动轮播
css: *{margin: 0;padding: 0;}body{font-size: 12px;line-height: 24px;text-algin: center; }a{color: #f ...
- hibernate关联映射
首先,建立user表,news表 建立User,News类 package com.example.hibernate; import java.util.Set; public class User ...
- CentOS 配置MySQL允许远程登录
Mysql为了安全性,在默认情况下用户只允许在本地登录,可是在有此情况下,还是需要使用用户进行远程连接,因此为了使其可以远程需要进行如下操作: 一.允许root用户在任何地方进行远程登录,并具有所有库 ...
- angular js 在ie11 下的get请求缓存问题的解决办法
使用angularjs 1.x开发的应用在ie11 下回碰到各种怪异的情况:一般表现在:卡,慢,丑. 还有另外一种情况,就是:get请求被缓存,导致页面数据没有刷新.今天我们就来解决这个问题. 几行代 ...
- C#组件系列——又一款Excel处理神器Spire.XLS,你值得拥有
前言:最近项目里面有一些对Excel操作的需求,博主想都没想,NPOI呗,简单.开源.免费,大家都喜欢!确实,对于一些简单的Excel导入.导出.合并单元格等,它都没啥太大的问题,但是这次的需求有两点 ...
- 日货EmEditor的使用小技巧
1.查看->大纲向导,可层级显示HTML 2.工具->插件->资源管理器,可在左侧显示资源管理器 3.工具->插件->单词自动完成,可实现单词智能提示功能