Spring源代码解析4Spring MVC.docx
- 文档编号:29830355
- 上传时间:2023-07-27
- 格式:DOCX
- 页数:27
- 大小:26.24KB
Spring源代码解析4Spring MVC.docx
《Spring源代码解析4Spring MVC.docx》由会员分享,可在线阅读,更多相关《Spring源代码解析4Spring MVC.docx(27页珍藏版)》请在冰豆网上搜索。
Spring源代码解析4SpringMVC
下面我们对SpringMVC框架代码进行分析,对于webApplicationContext的相关分析可以参见以前的文档,我们这里着重分析SpringWebMVC框架的实现.我们从分析DispatcherServlet入手:
代码
1.//这里是对DispatcherServlet的初始化方法,根据名字我们很方面的看到对各个Spring MVC主要元素的初始化
2.protected void initFrameworkServlet() throws ServletException, BeansException {
3. initMultipartResolver();
4. initLocaleResolver();
5. initThemeResolver();
6. initHandlerMappings();
7. initHandlerAdapters();
8. initHandlerExceptionResolvers();
9. initRequestToViewNameTranslator();
10. initViewResolvers();
11.}
看到注解我们知道,这是DispatcherSerlvet的初始化过程,它是在WebApplicationContext已经存在的情况下进行的,也就意味着在初始化它的时候,IOC容器应该已经工作了,这也是我们在web.xml中配置Spring的时候,需要把DispatcherServlet的load-on-startup的属性配置为2的原因。
对于具体的初始化过程,很容易理解,我们拿initHandlerMappings()来看看:
代码
1.private void initHandlerMappings() throws BeansException {
2. if (this.detectAllHandlerMappings) {
3. // 这里找到所有在上下文中定义的HandlerMapping,同时把他们排序
4. // 因为在同一个上下文中可以有不止一个handlerMapping,所以我们把他们都载入到一个链里进行维护和管理
5. Map matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(
6. getWebApplicationContext(), HandlerMapping.class, true, false);
7. if (!
matchingBeans.isEmpty()) {
8. this.handlerMappings = new ArrayList(matchingBeans.values());
9. // 这里通过order属性来对handlerMapping来在list中排序
10. Collections.sort(this.handlerMappings, new OrderComparator());
11. }
12. }
13. else {
14. try {
15. Object hm = getWebApplicationContext().getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
16. this.handlerMappings = Collections.singletonList(hm);
17. }
18. catch (NoSuchBeanDefinitionException ex) {
19. // Ignore, we'll add a default HandlerMapping later.
20. }
21. }
22.
23. //如果在上下文中没有定义的话,那么我们使用默认的BeanNameUrlHandlerMapping
24. if (this.handlerMappings == null) {
25. this.handlerMappings = getDefaultStrategies(HandlerMapping.class);
26. ........
27. }
28.}
怎样获得上下文环境,可以参见我们前面的对IOC容器在web环境中加载的分析。
DispatcherServlet把定义了的所有HandlerMapping都加载了放在一个List里待以后进行使用,这个链的每一个元素都是一个handlerMapping的配置,而一般每一个handlerMapping可以持有一系列从URL请求到SpringController的映射,比如SimpleUrl
HandlerMaaping中就定义了一个map来持有这一系列的映射关系。
DisptcherServlet通过HandlerMapping使得Web应用程序确定一个执行路径,就像我们在HanderMapping中看到的那样,HandlerMapping只是一个借口:
代码
1.public interface HandlerMapping {
2. public static final String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE =
3. Conventions.getQualifiedAttributeName(HandlerMapping.class, "pathWithinHandlerMapping");
4. //实际上维护一个HandlerExecutionChain,这是典型的Command的模式的使用,这个执行链里面维护handler和拦截器
5. HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
6.}
他的具体实现只需要实现一个接口方法,而这个接口方法返回的是一个HandlerExecutionChain,实际上就是一个执行链,就像在Command模式描述的那样,这个类很简单,就是一个持有一个Interceptor链和一个Controller:
代码
1.public class HandlerExecutionChain {
2.
3. private Object handler;
4.
5. private HandlerInterceptor[] interceptors;
6.
7. ........
8.}
而这些Handler和Interceptor需要我们定义HandlerMapping的时候配置好,比如对具体的SimpleURLHandlerMapping,他要做的就是根据URL映射的方式注册Handler和Interceptor,自己维护一个放映映射的handlerMap,当需要匹配Http请求的时候需要使用这个表里的信息来得到执行链。
这个注册的过程在IOC容器初始化SimpleUrlHandlerMapping的时候就被完成了,这样以后的解析才可以用到map里的映射信息,这里的信息和bean文件的信息是等价的,下面是具体的注册过程:
代码
1.protected void registerHandlers(Map urlMap) throws BeansException {
2. if (urlMap.isEmpty()) {
3. logger.warn("Neither 'urlMap' nor 'mappings' set on SimpleUrlHandlerMapping");
4. }
5. else {
6. //这里迭代在SimpleUrlHandlerMapping中定义的所有映射元素
7. Iterator it = urlMap.keySet().iterator();
8. while (it.hasNext()) {
9. //这里取得配置的url
10. String url = (String) it.next();
11. //这里根据url在bean定义中取得对应的handler
12. Object handler = urlMap.get(url);
13. // Prepend with slash if not already present.
14. if (!
url.startsWith("/")) {
15. url = "/" + url;
16. }
17. //这里调用AbstractHandlerMapping中的注册过程
18. registerHandler(url, handler);
19. }
20. }
21.}
在AbstractMappingHandler中的注册代码:
代码
1.protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {
2. //试图从handlerMap中取handler,看看是否已经存在同样的Url映射关系
3. Object mappedHandler = this.handlerMap.get(urlPath);
4. if (mappedHandler !
= null) {
5. ........
6. }
7.
8. //如果是直接用bean名做映射那就直接从容器中取handler
9. if (!
this.lazyInitHandlers && handler instanceof String) {
10. String handlerName = (String) handler;
11. if (getApplicationContext().isSingleton(handlerName)) {
12. handler = getApplicationContext().getBean(handlerName);
13. }
14. }
15. //或者使用默认的handler.
16. if (urlPath.equals("/*")) {
17. setDefaultHandler(handler);
18. }
19. else {
20. //把url和handler的对应关系放到handlerMap中去
21. this.handlerMap.put(urlPath, handler);
22. ........
23. }
24.}
handlerMap是持有的一个HashMap,里面就保存了具体的映射信息:
代码
1.private final Map handlerMap = new HashMap();
而SimpleUrlHandlerMapping对接口HandlerMapping的实现是这样的,这个getHandler根据在初始化的时候就得到的映射表来生成DispatcherServlet需要的执行链
代码
1.public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
2. //这里根据request中的参数得到其对应的handler,具体处理在AbstractUrlHandlerMapping中
3. Object handler = getHandlerInternal(request);
4. //如果找不到对应的,就使用缺省的handler
5. if (handler == null) {
6. handler = this.defaultHandler;
7. }
8. //如果缺省的也没有,那就没办法了
9. if (handler == null) {
10. return null;
11. }
12. // 如果handler不是一个具体的handler,那我们还要到上下文中取
13. if (handler instanceof String) {
14. String handlerName = (String) handler;
15. handler = getApplicationContext().getBean(handlerName);
16. }
17. //生成一个HandlerExecutionChain,其中放了我们匹配上的handler和定义好的拦截器,就像我们在HandlerExecutionChain中看到的那样,它持有一个handler和一个拦截器组。
18. return new HandlerExecutionChain(handler, this.adaptedInterceptors);
19.}
我们看看具体的handler查找过程:
代码
1.protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
2. //这里的HTTP Request传进来的参数进行分析,得到具体的路径信息。
3. String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
4. .......//下面是根据请求信息的查找
5. return lookupHandler(lookupPath, request);
6.}
7.
8.protected Object lookupHandler(String urlPath, HttpServletRequest request) {
9. // 如果能够直接能在SimpleUrlHandlerMapping的映射表中找到,那最好。
10. Object handler = this.handlerMap.get(urlPath);
11. if (handler == null) {
12. // 这里使用模式来对map中的所有handler进行匹配,调用了Jre中的Matcher类来完成匹配处理。
13. String bestPathMatch = null;
14. for (Iterator it = this.handlerMap.keySet().iterator(); it.hasNext();) {
15. String registeredPath = (String) it.next();
16. if (this.pathMatcher.match(registeredPath, urlPath) &&
17. (bestPathMatch == null || bestPathMatch.length() <= registeredPath.length())) {
18. //这里根据匹配路径找到最象的一个
19. handler = this.handlerMap.get(registeredPath);
20. bestPathMatch = registeredPath;
21. }
22. }
23.
24. if (handler !
= null) {
25. exposePathWithinMapping(this.pathMatcher.extractPathWithinPattern(bestPathMatch, urlPath), request);
26. }
27. }
28. else {
29. exposePathWithinMapping(urlPath, request);
30. }
31. //
32. return handler;
33.}
我们可以看到,总是在handlerMap这个HashMap中找,当然如果直接找到最好,如果找不到,就看看是不是能通过MatchPattern的模式找,我们一定还记得在配置HnaderMapping的时候是可以通过ANT语法进行配置的,其中的处理就在这里。
这样可以清楚地看到整个HandlerMapping的初始化过程-同时,我们也看到了一个具体的handler映射是怎样被存储和查找的-这里生成一个ExecutionChain来储存我们找到的handler和在定义bean的时候定义的Interceptors.
让我们回到DispatcherServlet,初始化完成以后,实际的对web请求是在doService()方法中处理的,我们知道DispatcherServlet只是一个普通的Servlet:
代码
1.protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
2. .......
3. //这里把属性信息进行保存
4. Map attributesSnapshot = null;
5. if (WebUtils.isIncludeRequest(request)) {
6. logger.debug("Taking snapshot of request attributes before include");
7. attributesSnapshot = new HashMap();
8. Enumeration attrNames = request.getAttributeNames();
9. while (attrNames.hasMoreElements()) {
10. String attrName = (String) attrNames.nextElement();
11. if (this.cleanupAfterInclude || attrName.startsWith(DispatcherServlet.class.getName())) {
12. attributesSnapshot.put(attrName, request.getAttribute(attrName));
13. }
14. }
15. }
16.
17. // Make framework objects available to handlers and view objects.
18. request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
19. request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
20. request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
21. request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
22.
23. try {
24. //这里使实际的处理入口
25. doDispatch(request, response);
26. }
27. finally {
28. // Restore the origin
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Spring源代码解析4Spring MVC Spring 源代码 解析