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 ...
随机推荐
- 拥抱.NET Core,如何开发跨平台的应用并部署至Ubuntu运行
之前写了一篇博文宣布Rabbit Rpc跨平台了"拥抱.NET Core,跨平台的轻量级RPC:Rabbit.Rpc",在过程中尝试了如何编写支持跨平台的类库与应用程序,也尝试了在 ...
- [Android] 怎么在应用中实现密码隐藏?
[Android] 怎么在应用中实现密码隐藏? 在安卓应用中,用户注册或者登录时,需要把密码隐藏,实现一定的保密效果.在安卓中,可以通过设置EditText组件的TransformationMetho ...
- python列表模拟堆栈和队列
对列特点:先进先出.后进后出 用列表insert.pop模拟进队出队: >>> l = [] >>> l.insert(0,'p1') >>> l ...
- 跳入linux的第一个坑-因为安装Ubuntu导致的硬盘被误格的恢复.(记TestDisk使用记录)
不看废话,直接跳到操作说明 前几日心血来潮想把家中的旧笔记本换成Linux操作系统,算是在业余生活中正式投入Linux的怀抱.说干就干,发行版选择了Ubuntu,下载了Ubuntu16.04的ISO, ...
- 禁止chrome记住密码
谷歌浏览器保存密码后输入框背景色变成黄色,会影响原来的输入框样式,css样式input:-webkit-autofill可以改变输入框样式,background-color,background-im ...
- SpringMVC入门案例及请求流程图(关于处理器或视图解析器或处理器映射器等的初步配置)
SpringMVC简介:SpringMVC也叫Spring Web mvc,属于表现层的框架.Spring MVC是Spring框架的一部分,是在Spring3.0后发布的 Spring结构图 Spr ...
- C#中ToString()格式详解
以下内容均摘自博客园,仅供资料查询. ToString格式化 在很多对象显示为字符串的时候都会使用到ToString中的格式化,由于以前没怎么注意到这个问题,想总结一下各个基础结构对象的格式化,以便后 ...
- js禁止页面刷新禁止用F5键刷新,禁止右键
<script language="javascript"> //禁止用F5键 function document.onkeydown() { if ...
- phabricator-zh_CN汉化包
https://github.com/wanthings/phabricator-zh_CN 之前公司用这个做任务管理.可以将一些安排计划等指派给某个人跟进进度. 这个系统都是英文的,这是个缺憾.我找 ...
- [LeetCode] Longest Repeating Character Replacement 最长重复字符置换
Given a string that consists of only uppercase English letters, you can replace any letter in the st ...