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 ...
随机推荐
- OpenSceneGraph 编译 error LNK2019:unresolved external symbol 错误
在编译 OpenSceneGraph 的一个简单示例时, #include <osgViewer/Viewer> #include <osgDB/ReadFile> void ...
- Java文件中出现这样的提示错误与解决方法:Cannot return from outside a function or method?
1.打开 >> Myeclipse或Eclipse.(我使用的是Myeclipse) 2.打开 >> Window >> Preferences 如图 ...
- 高性能Mysql主从架构的复制原理及配置详解
温习<高性能MySQL>的复制篇. 1 复制概述 Mysql内建的复制功能是构建大型,高性能应用程序的基础.将Mysql的数据分布到多个系统上去,这种分布的机制,是通过将Mysql的某一台 ...
- Mac新建文件夹、txt文件、无格式文件
新建文件夹: mkdir test 新建txt touch test.txt 新建无后缀格式文件 touch test 如果要删除文件夹 rm -r -f test
- css权重计算方法浅谈
在这之前只知道css权重的皮毛,比如说:行内权重比头部权重高,头部比外部样式权重高----工作中才知道真正理解css权重重要性.理解权重了才能写出来最优css选择器来.对后面学习less,scss有很 ...
- how2heap分析系列:2_fastbin_dup
源码 #include <stdio.h> #include <stdlib.h> int main() { printf("This file demonstrat ...
- python爬虫学习(10) —— 专利检索DEMO
这是一个稍微复杂的demo,它的功能如下: 输入专利号,下载对应的专利文档 输入关键词,下载所有相关的专利文档 0. 模块准备 首先是requests,这个就不说了,爬虫利器 其次是安装tessera ...
- Linux下的C Socket编程 -- 获取对方IP地址
Linux下的C Socket编程(二) 获取域名对应的IP地址 经过上面的讨论,如果我们想要连接到远程的服务器,我们需要知道对方的IP地址,系统函数gethostbyname便能够实现这个目的.它能 ...
- Sql 2008 的常用函数
1.LEN 函数:返回数据的长度 ') 返回:8 2.ASCII函数:返回字符串最左边的ascii值 SELECT ASCII('abc') 返回:97 3.LEFT函数:从左边开始截取指定长度的字符 ...
- Oracle XE手动建立数据库实例
参考资料为: [Oracle XE系列之三]使用OMF方式手工创建Oracle XE数据库 - 王立夫 - 博客园http://www.cnblogs.com/opfo/p/5056122.html ...