基于Spring MVC的Web应用开发(1) - HelloWorld

来源:互联网 发布:淘宝天天特价是什么 编辑:程序博客网 时间:2024/05/16 10:15

MVC架构

MVC是模型(model),视图(view),控制器(controller)3个单词的首字母缩写。有些应用需要处理用户请求并操纵和显示数据,MVC模式可以简化其实现。该模式由3个组件组成:

 

  • 模型表示用户期望看到的数据。通常情况下,模型由JavaBean组成。
  • 视图负责显示模型。文本编辑器中的视图组件会以恰当的格式显示一段文本,视图在Web应用中会生成客户端浏览器可以解释显示的HTML。
  • 控制器表示逻辑代码,负责处理请求和执行用户的意图。它会构建恰当的模型并将其传入视图进行显示。对Java Web应用来说,控制器多是一个servlet。当然,控制器可以使用任意语言实现,只要web容器支持就行。
Spring MVC介绍
Spring MVC对MVC的实现相当通用。模型就是一个包含数据的简单Map,视图就是一个interface,由它实现显示数据,控制器是Controller接口的实现。
Spring对Web应用下MVC模式的实现基于spring-webmvc下的org.springframework.web.servlet.DispatcherServlet。该Servlet会处理请求并调用恰当的控制元素对请求进行处理。Dispatcher会拦截请求并决定由哪个控制器处理请求。Spring控制器的handling方法会返回一个ModelAndView实例。该实例持有对视图和模型的引用。模型就是一个简单的Map实例,它里面是JavaBean,View接口会显示这些JavaBean。View接口定义了render方法。它遵循这样的原则:View的实现可以是任何东西,只要客户端能对其进行解析就行。
MVC实现
如果要用Spring创建Web应用,需要从基本的web.xml开始,我们在其中指定了DispatcherServlet并为其设定好相应的url-pattern。代码清单如下:
Xml代码  收藏代码
  1. <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"  
  2.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  3.     xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">  
  4.   
  5.     <!-- Processes application requests -->  
  6.     <servlet>  
  7.         <servlet-name>appServlet</servlet-name>  
  8.         <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>  
  9.         <init-param>  
  10.             <param-name>contextConfigLocation</param-name>  
  11.             <param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>  
  12.         </init-param>  
  13.         <load-on-startup>1</load-on-startup>  
  14.     </servlet>  
  15.       
  16.     <servlet-mapping>  
  17.         <servlet-name>appServlet</servlet-name>  
  18.         <url-pattern>/</url-pattern>  
  19.     </servlet-mapping>  
  20.       
  21.     <!-- Disables Servlet Container welcome file handling. Needed for compatibility with Servlet 3.0 and Tomcat 7.0 -->  
  22.     <welcome-file-list>  
  23.         <welcome-file></welcome-file>  
  24.     </welcome-file-list>  
  25.   
  26. </web-app>  
 上述web.xml文件定义了DispatcherServlet类作为名叫appServlet的Servlet,映射所有请求至该Servlet。
使用处理器映射
对于具体某个请求,Web应用如何知道该调用哪个servlet或者handler实现呢?Spring handler映射就在这里发挥作用了。只需要几步就可以为Spring控制器配置URL映射,因为我们使用的是Spring3.1,@Controller注解专门为此而生。
在具体展示代码前,先说明一下Spring的内部实现原理,Spring使用org.springframework.web.servlet.HandlerMappping这个接口的实现来标识调用的控制器,该接口提供了好多中实现,现在我们只要讲解hello world中需要用到的SimpleUrlHandlerMapping,凭借该handler映射,我们可以在请求中(使用全名和通配符)指定处理该请求的控制器。下列清单展示了一个handler映射的例子:
Java代码  收藏代码
  1. package org.springframework.samples.mvc.simple;  
  2.   
  3. import org.springframework.stereotype.Controller;  
  4. import org.springframework.web.bind.annotation.RequestMapping;  
  5. import org.springframework.web.bind.annotation.ResponseBody;  
  6.   
  7. @Controller  
  8. public class SimpleController {  
  9.   
  10.     @RequestMapping("/simple")  
  11.     @ResponseBody  
  12.     public String simple() {  
  13.         return "Hello world!";  
  14.     }  
  15.       
  16. }  
 Spring控制器
控制器负责处理请求:根据request构建model并将其传到view以进行显示。
DispatcherServlet有一个List类型的handlerAdapters属性,它可用来指定你想用的HandlerAdapter实现,如果这个属性值为null,就会使用默认的策略。这句话暂时不明白没关系,下文会有详细解读。
上面的代码清单其实已经继承一个控制器实现。在启动的时候我们会注意到有这样的输出日志:
Log代码  收藏代码
  1. INFO: Mapped URL path [/simple] onto handler 'simpleController'  
  2. INFO: Mapped URL path [/simple.*] onto handler 'simpleController'  
  3. INFO: Mapped URL path [/simple/] onto handler 'simpleController'  
 因为我们在web.xml中做了全局性的URL通配符,所以这里Spring“智能”的为我们的@RequestMapping("/simple")映射好三种URL(如何只保留其中一种,以后再讲)。
视图
hello world并没有视图,因为我们使用了@ResponseBody注解,该注解直接将控制器中的返回值绑定到web响应体里,如果返回的是字符串,那么该字符串就显示在浏览器上,而这也就是我们想要的。上面例子的结果是将文本hello world写入http响应流中。下面将spring官方手册中有关@ResponseBody的解释翻译一下:
翻译代码  收藏代码
  1. @ResponseBody注解和@RequestBody相似。这个注解可以放在方法上,表明返回的类型应该是可以直接写入到HTTP响应体里的(Model中不会放置该值,该值也不会被转成一个view名字)。  
 
视图的深入讲解将在以后逐步讲到。
至此,一个简单的hello world就讲完了,但是我们还是有疑问,这些只是表面现象,Spring底层到底是运作的呢?
首先我们来看
Xml代码  收藏代码
  1. <servlet>  
  2.         <servlet-name>appServlet</servlet-name>  
  3.         <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>  
  4.         <init-param>  
  5.             <param-name>contextConfigLocation</param-name>  
  6.             <param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>  
  7.         </init-param>  
  8.         <load-on-startup>1</load-on-startup>  
  9.     </servlet>  
 系统启动时会加载DispatcherServlet这个Servlet,深入看一下:
Java代码  收藏代码
  1. public class DispatcherServlet extends FrameworkServlet  
Java代码  收藏代码
  1. public abstract class FrameworkServlet extends HttpServletBean  
Java代码  收藏代码
  1. public abstract class HttpServletBean extends HttpServlet implements EnvironmentAware  

 可见DispatcherServlet是继承了javax.servlet.http.HttpServlet的一个Servlet,既然是一个Servlet,则必然有

 

Java代码  收藏代码
  1. @Override  
  2. public final void init() throws ServletException;  
  3.   
  4. @Override  
  5. protected final void doGet(HttpServletRequest request, HttpServletResponse response)  
  6.             throws ServletException, IOException;  
  7.   
  8. @Override  
  9. protected final void doPost(HttpServletRequest request, HttpServletResponse response)  
  10.             throws ServletException, IOException;  
  11.   
  12. @Override  
  13. protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception;  

 

 这几个方法的实现,我们一个一个看:

init()方法在HttpServletBean中实现,doGet和doPost方法在FrameworkServlet中实现,doService()方法在DispatcherServlet中实现。

根据Servlet规范,一个Servlet启动的时候需要执行init()方法,一个请求来了首先判断是Get还Post方式提交,Get方式提交的执行doGet()方法,Post方式提交的执行doPost()方法。然后在doGet()和doPost()方法中可调用doService()方法,SpringMVC完全参照此规范。

在init()中,HttpServletBean执行

 

Java代码  收藏代码
  1. protected void initServletBean() throws ServletException {  
  2.     }  

 

 讲具体的实现交给FrameworkServlt重载的方法完成

 

Java代码  收藏代码
  1. @Override  
  2.     protected final void initServletBean() throws ServletException {  
  3. System.out.println("使用servlet上下文记录日志");  
  4.         getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");  
  5.         if (this.logger.isInfoEnabled()) {  
  6.             this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");  
  7.         }  
  8.         long startTime = System.currentTimeMillis();  
  9. System.out.println("启动时间:[" + startTime + "]");  
  10.   
  11.         try {  
  12. System.out.println("initWebApplicationContext()");  
  13.             this.webApplicationContext = initWebApplicationContext();  
  14. System.out.println("initFrameworkServlet(),这是一个空实现");  
  15.             initFrameworkServlet();  
  16.         }  
  17.         catch (ServletException ex) {  
  18.             this.logger.error("Context initialization failed", ex);  
  19.             throw ex;  
  20.         }  
  21.         catch (RuntimeException ex) {  
  22.             this.logger.error("Context initialization failed", ex);  
  23.             throw ex;  
  24.         }  
  25.   
  26.         if (this.logger.isInfoEnabled()) {  
  27.             long elapsedTime = System.currentTimeMillis() - startTime;  
  28. System.out.println("结束时间:[" + elapsedTime + "]");  
  29.             this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +  
  30.                     elapsedTime + " ms");  
  31.         }  
  32.     }  

 

 该方法会记录启动耗费的时间,也就时我们在启动项目时在控制台看到的时间,这里还解释了,为什么输出启动耗时的日志一般是红色的。

在该方法中执行了initWebApplicationContext()方法,进入该方法

 

Java代码  收藏代码
  1. protected WebApplicationContext initWebApplicationContext() {  
  2. //System.out.println("使用WebApplicationContextUtils工具类得到WebApplicatonContext");  
  3.         WebApplicationContext rootContext =  
  4.                 WebApplicationContextUtils.getWebApplicationContext(getServletContext());  
  5.         WebApplicationContext wac = null;  
  6.   
  7. //System.out.println("判断webApplicationContext是否为null");  
  8.         if (this.webApplicationContext != null) {  
  9. //System.out.println("webApplicationContext非空!");  
  10.             // A context instance was injected at construction time -> use it  
  11.             wac = this.webApplicationContext;  
  12.             if (wac instanceof ConfigurableWebApplicationContext) {  
  13.                 ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;  
  14.                 if (!cwac.isActive()) {  
  15.                     // The context has not yet been refreshed -> provide services such as  
  16.                     // setting the parent context, setting the application context id, etc  
  17.                     if (cwac.getParent() == null) {  
  18.                         // The context instance was injected without an explicit parent -> set  
  19.                         // the root application context (if any; may be null) as the parent  
  20.                         cwac.setParent(rootContext);  
  21.                     }  
  22.                     configureAndRefreshWebApplicationContext(cwac);  
  23.                 }  
  24.             }  
  25.         }  
  26. //System.out.println("判断wac是否为null");  
  27.         if (wac == null) {  
  28. //System.out.println("was为空");  
  29.             // No context instance was injected at construction time -> see if one  
  30.             // has been registered in the servlet context. If one exists, it is assumed  
  31.             // that the parent context (if any) has already been set and that the  
  32.             // user has performed any initialization such as setting the context id  
  33.             wac = findWebApplicationContext();  
  34.         }  
  35. //System.out.println("再次判断wac是否为null");  
  36.         if (wac == null) {  
  37. System.out.println("wac还为空");  
  38. System.out.println("No context instance is defined for this servlet -> create a local one");  
  39.             // No context instance is defined for this servlet -> create a local one  
  40.             wac = createWebApplicationContext(rootContext);  
  41.         }  
  42.   
  43. System.out.println("this.refreshEventReceived:[" + this.refreshEventReceived + "]");  
  44.         if (!this.refreshEventReceived) {  
  45. System.out.println("onRefresh");  
  46.             // Either the context is not a ConfigurableApplicationContext with refresh  
  47.             // support or the context injected at construction time had already been  
  48.             // refreshed -> trigger initial onRefresh manually here.  
  49.             onRefresh(wac);  
  50.         }  
  51.   
  52. System.out.println("this.publishContext:[" + this.publishContext + "]");  
  53.         if (this.publishContext) {  
  54. System.out.println("Publish the context as a servlet context attribute.");  
  55.             // Publish the context as a servlet context attribute.  
  56.             String attrName = getServletContextAttributeName();  
  57.             getServletContext().setAttribute(attrName, wac);  
  58.             if (this.logger.isDebugEnabled()) {  
  59.                 this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +  
  60.                         "' as ServletContext attribute with name [" + attrName + "]");  
  61.             }  
  62.         }  
  63.   
  64.         return wac;  
  65.     }  

 

 发现很长,但其实有用的只有wac = createWebApplicationContext(rootContext);在进入这个方法

 

Java代码  收藏代码
  1. protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {  
  2. System.out.println("createWebApplicationContext");  
  3.         Class<?> contextClass = getContextClass();  
  4.         if (this.logger.isDebugEnabled()) {  
  5.             this.logger.debug("Servlet with name '" + getServletName() +  
  6.                     "' will try to create custom WebApplicationContext context of class '" +  
  7.                     contextClass.getName() + "'" + ", using parent context [" + parent + "]");  
  8.         }  
  9.         if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {  
  10.             throw new ApplicationContextException(  
  11.                     "Fatal initialization error in servlet with name '" + getServletName() +  
  12.                     "': custom WebApplicationContext class [" + contextClass.getName() +  
  13.                     "] is not of type ConfigurableWebApplicationContext");  
  14.         }  
  15. System.out.println("初始化Bean开始");  
  16.         ConfigurableWebApplicationContext wac =  
  17.                 (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);  
  18. System.out.println("初始化Bean结束");          
  19.   
  20.         wac.setParent(parent);  
  21.         wac.setConfigLocation(getContextConfigLocation());  
  22.   
  23. System.out.println("configureAndRefreshWebApplicationContext开始");  
  24.         configureAndRefreshWebApplicationContext(wac);  
  25. System.out.println("configureAndRefreshWebApplicationContext结束");  
  26.   
  27.         return wac;  
  28.     }  

 

 于是我们就看到了有点眼熟的

 

Java代码  收藏代码
  1. ConfigurableWebApplicationContext wac =  
  2.                 (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);  

 

 这个方法就开始初始化我们servlet上下文即spring配置文件中的bean了。

我们的配置文件中servlet-context.xml只要一行

 

Xml代码  收藏代码
  1. <beans:import resource="controllers.xml" />  

 

 而controllers.xml中

 

Xml代码  收藏代码
  1. <context:component-scan base-package="org.springframework.samples.mvc" />  

 

 含义是扫描指定包下面所有带有注解的类,并将他们初始化。上面已经列出了带有@Controller注解的SimpleController类,于是Spring默认将这个bean的名字命名为simpleController(类名首字母小写),然后发现有一个方法带有@RequestMapping,Spring便通过org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping创建URL和控制器的映射(handlerMapping),具体过程可看如下日志:

 

Log代码  收藏代码
  1. DEBUG: org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping - Looking for URL mappings in application context: WebApplicationContext for namespace 'appServlet-servlet': startup date [Fri Mar 16 05:36:03 CST 2012]; root of context hierarchy  
  2. DEBUG: org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'simpleController'  
  3. 将一个URL路径映射到一个handler上  
  4. INFO : org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping - Mapped URL path [/simple] onto handler 'simpleController'  
  5. DEBUG: org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'simpleController'  
  6. 将一个URL路径映射到一个handler上  
  7. INFO : org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping - Mapped URL path [/simple.*] onto handler 'simpleController'  
  8. DEBUG: org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'simpleController'  
  9. 将一个URL路径映射到一个handler上  
  10. INFO : org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping - Mapped URL path [/simple/] onto handler 'simpleController'  

 

 URL到控制器的映射创建完了,剩下的便是处理器代理了(handlerAdapter),前文提到了,DispatcherServlet有一个List类型的handlerAdapters,如果为null,系统将使用默认策略。这个默认策略体现在DispatcherServlet的这个方法中

 

Java代码  收藏代码
  1. /** 
  2.      * Initialize the HandlerAdapters used by this class. 
  3.      * <p>If no HandlerAdapter beans are defined in the BeanFactory for this namespace, 
  4.      * we default to SimpleControllerHandlerAdapter. 
  5.      */  
  6.     private void initHandlerAdapters(ApplicationContext context) {  
  7.         this.handlerAdapters = null;  
  8.   
  9.         if (this.detectAllHandlerAdapters) {  
  10.             // Find all HandlerAdapters in the ApplicationContext, including ancestor contexts.  
  11.             Map<String, HandlerAdapter> matchingBeans =  
  12.                     BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.classtruefalse);  
  13.             if (!matchingBeans.isEmpty()) {  
  14.                 this.handlerAdapters = new ArrayList<HandlerAdapter>(matchingBeans.values());  
  15.                 // We keep HandlerAdapters in sorted order.  
  16.                 OrderComparator.sort(this.handlerAdapters);  
  17.             }  
  18.         }  
  19.         else {  
  20.             try {  
  21.                 HandlerAdapter ha = context.getBean(HANDLER_ADAPTER_BEAN_NAME, HandlerAdapter.class);  
  22.                 this.handlerAdapters = Collections.singletonList(ha);  
  23.             }  
  24.             catch (NoSuchBeanDefinitionException ex) {  
  25.                 // Ignore, we'll add a default HandlerAdapter later.  
  26.             }  
  27.         }  
  28.   
  29.         // Ensure we have at least some HandlerAdapters, by registering  
  30.         // default HandlerAdapters if no other adapters are found.  
  31.         if (this.handlerAdapters == null) {  
  32. System.out.println("确保我们有一些handlerAdapters");  
  33.             this.handlerAdapters = getDefaultStrategies(context, HandlerAdapter.class);  
  34.             if (logger.isDebugEnabled()) {  
  35.                 logger.debug("No HandlerAdapters found in servlet '" + getServletName() + "': using default");  
  36.             }  
  37.         }  
  38.     }  

 

 这个默认策略会给我们三个HandlerAdapter,具体日志:

 

Java代码  收藏代码
  1. DEBUG: org.springframework.beans.factory.support.DefaultListableBeanFactory - Finished creating instance of bean 'org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter'  
  2. DEBUG: org.springframework.beans.factory.support.DefaultListableBeanFactory - Finished creating instance of bean 'org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter'  
  3. DEBUG: org.springframework.beans.factory.support.DefaultListableBeanFactory - Finished creating instance of bean 'org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter'  

 

至此,init()可以算是执行完了(当然为了简化,还有一些无关大局的细节略去没有讲),我们的Servlet,也就是项目就运行起来了。

下面讲解请求部分

先来看一段浏览器发出请求后,服务端的记录的日志

 

Log代码  收藏代码
  1. FrameworkServlet doGet  
  2. TRACE: org.springframework.web.servlet.DispatcherServlet - Bound request context to thread: [GET /web/simple]@902324502 org.eclipse.jetty.server.Request@35c86116  
  3. doService  
  4. DEBUG: org.springframework.web.servlet.DispatcherServlet - DispatcherServlet with name 'appServlet' processing GET request for [/web/simple]  
  5. doDispatch  
  6. Determine handler for the current request.  
  7. TRACE: org.springframework.web.servlet.DispatcherServlet - Testing handler map [org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping@20d1fa4] in DispatcherServlet with name 'appServlet'  
  8. TRACE: org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping - No handler mapping found for [/simple]  
  9. TRACE: org.springframework.web.servlet.DispatcherServlet - Testing handler map [org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping@5b33a7af] in DispatcherServlet with name 'appServlet'  
  10. DEBUG: org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping - Mapping [/simple] to HandlerExecutionChain with handler [org.springframework.samples.mvc.simple.SimpleController@573b7064] and 1 interceptor  
  11. Determine handler adapter for the current request.  
  12. TRACE: org.springframework.web.servlet.DispatcherServlet - Testing handler adapter [org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter@557ce3bb]  
  13. TRACE: org.springframework.web.servlet.DispatcherServlet - Testing handler adapter [org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter@6996a298]  
  14. TRACE: org.springframework.web.servlet.DispatcherServlet - Testing handler adapter [org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter@783b67a7]  
  15. Process last-modified header, if supported by the handler.  
  16. DEBUG: org.springframework.web.servlet.DispatcherServlet - Last-Modified value for [/web/simple] is: -1  
  17. Apply preHandle methods of registered interceptors.  
  18. Actually invoke the handler.  
  19. class org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter  
  20. invokeHandlerMethod  
  21. 真正处理  
  22. DEBUG: org.springframework.web.bind.annotation.support.HandlerMethodInvoker - Invoking request handler method: public java.lang.String org.springframework.samples.mvc.simple.SimpleController.simple()  
  23. result:[Hello world!]  
  24. getModelAndView[class java.lang.String]  
  25. 原来进入了这个分支  
  26. OK搞定  
  27. 具体输出数据  
  28. DEBUG: org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter - Written [Hello world!] as "text/html" using [org.springframework.http.converter.StringHttpMessageConverter@6b7de5ce]  
  29. 返回mv为空,假设handler已经处理完了  
  30. Do we need view name translation?  
  31. Apply postHandle methods of registered interceptors.  
  32. DEBUG: org.springframework.web.servlet.DispatcherServlet - Null ModelAndView returned to DispatcherServlet with name 'appServlet': assuming HandlerAdapter completed request handling  
  33. Trigger after-completion for successful outcome.  
  34. [1]  
  35. TRACE: org.springframework.web.servlet.DispatcherServlet - Cleared thread-bound request context: [GET /web/simple]@902324502 org.eclipse.jetty.server.Request@35c86116  
  36. DEBUG: org.springframework.web.servlet.DispatcherServlet - Successfully completed request  
  37. TRACE: org.springframework.web.context.support.XmlWebApplicationContext - Publishing event in WebApplicationContext for namespace 'appServlet-servlet': ServletRequestHandledEvent: url=[/web/simple]; client=[0:0:0:0:0:0:0:1%0]; method=[GET]; servlet=[appServlet]; session=[null]; user=[null]; time=[154ms]; status=[OK]  

 

 我们来详细分析一下,请求上来时,首先进入FrameworkServlet的doGet方法,然后调用

 

Java代码  收藏代码
  1. /** 
  2.      * Process this request, publishing an event regardless of the outcome. 
  3.      * <p>The actual event handling is performed by the abstract 
  4.      * {@link #doService} template method. 
  5.      */  
  6.     protected final void processRequest(HttpServletRequest request, HttpServletResponse response)  
  7.             throws ServletException, IOException {  
  8.   
  9.         long startTime = System.currentTimeMillis();  
  10.         Throwable failureCause = null;  
  11.   
  12.         // Expose current LocaleResolver and request as LocaleContext.  
  13.         LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();  
  14.         LocaleContextHolder.setLocaleContext(buildLocaleContext(request), this.threadContextInheritable);  
  15.   
  16.         // Expose current RequestAttributes to current thread.  
  17.         RequestAttributes previousRequestAttributes = RequestContextHolder.getRequestAttributes();  
  18.         ServletRequestAttributes requestAttributes = null;  
  19.         if (previousRequestAttributes == null || previousRequestAttributes.getClass().equals(ServletRequestAttributes.class)) {  
  20.             requestAttributes = new ServletRequestAttributes(request);  
  21.             RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable);  
  22.         }  
  23.   
  24.         if (logger.isTraceEnabled()) {  
  25.             logger.trace("Bound request context to thread: " + request);  
  26.         }  
  27.   
  28.         try {  
  29.             doService(request, response);  
  30.         }  
  31.         catch (ServletException ex) {  
  32.             failureCause = ex;  
  33.             throw ex;  
  34.         }  
  35.         catch (IOException ex) {  
  36.             failureCause = ex;  
  37.             throw ex;  
  38.         }  
  39.         catch (Throwable ex) {  
  40.             failureCause = ex;  
  41.             throw new NestedServletException("Request processing failed", ex);  
  42.         }  
  43.   
  44.         finally {  
  45.             // Clear request attributes and reset thread-bound context.  
  46.             LocaleContextHolder.setLocaleContext(previousLocaleContext, this.threadContextInheritable);  
  47.             if (requestAttributes != null) {  
  48.                 RequestContextHolder.setRequestAttributes(previousRequestAttributes, this.threadContextInheritable);  
  49.                 requestAttributes.requestCompleted();  
  50.             }  
  51.             if (logger.isTraceEnabled()) {  
  52.                 logger.trace("Cleared thread-bound request context: " + request);  
  53.             }  
  54.   
  55.             if (logger.isDebugEnabled()) {  
  56.                 if (failureCause != null) {  
  57.                     this.logger.debug("Could not complete request", failureCause);  
  58.                 }  
  59.                 else {  
  60.                     this.logger.debug("Successfully completed request");  
  61.                 }  
  62.             }  
  63.             if (this.publishEvents) {  
  64.                 // Whether or not we succeeded, publish an event.  
  65.                 long processingTime = System.currentTimeMillis() - startTime;  
  66.                 this.webApplicationContext.publishEvent(  
  67.                         new ServletRequestHandledEvent(this,  
  68.                                 request.getRequestURI(), request.getRemoteAddr(),  
  69.                                 request.getMethod(), getServletConfig().getServletName(),  
  70.                                 WebUtils.getSessionId(request), getUsernameForRequest(request),  
  71.                                 processingTime, failureCause));  
  72.             }  
  73.         }  
  74.     }  

 

 该方法很杂,其他的不要看,就看调用了doService方法即可。此doService由DispatcherServlet重写

 

Java代码  收藏代码
  1. @Override  
  2.     protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {  
  3. System.out.println("doService");  
  4.         if (logger.isDebugEnabled()) {  
  5.             String requestUri = urlPathHelper.getRequestUri(request);  
  6.             logger.debug("DispatcherServlet with name '" + getServletName() + "' processing " + request.getMethod() +  
  7.                     " request for [" + requestUri + "]");  
  8.         }  
  9.   
  10.         // Keep a snapshot of the request attributes in case of an include,  
  11.         // to be able to restore the original attributes after the include.  
  12.         Map<String, Object> attributesSnapshot = null;  
  13.         if (WebUtils.isIncludeRequest(request)) {  
  14.             logger.debug("Taking snapshot of request attributes before include");  
  15.             attributesSnapshot = new HashMap<String, Object>();  
  16.             Enumeration<?> attrNames = request.getAttributeNames();  
  17.             while (attrNames.hasMoreElements()) {  
  18.                 String attrName = (String) attrNames.nextElement();  
  19.                 if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) {  
  20.                     attributesSnapshot.put(attrName, request.getAttribute(attrName));  
  21.                 }  
  22.             }  
  23.         }  
  24.   
  25.         this.flashMapManager.requestStarted(request);  
  26.   
  27.         // Make framework objects available to handlers and view objects.  
  28.         request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());  
  29.         request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);  
  30.         request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);  
  31.         request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());  
  32.   
  33.         try {  
  34.             doDispatch(request, response);  
  35.         }  
  36.         finally {  
  37.             this.flashMapManager.requestCompleted(request);  
  38.               
  39.             // Restore the original attribute snapshot, in case of an include.  
  40.             if (attributesSnapshot != null) {  
  41.                 restoreAttributesAfterInclude(request, attributesSnapshot);  
  42.             }  
  43.         }  
  44.     }  

 

 其他不要看,就看调用了doDispatch方法

 

Java代码  收藏代码
  1. protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {  
  2. System.out.println("doDispatch");  
  3.         HttpServletRequest processedRequest = request;  
  4.         HandlerExecutionChain mappedHandler = null;  
  5.         int interceptorIndex = -1;  
  6.   
  7.         try {  
  8.             ModelAndView mv;  
  9.             boolean errorView = false;  
  10.   
  11.             try {  
  12.                 processedRequest = checkMultipart(request);  
  13.   
  14.                 // Determine handler for the current request.  
  15. System.out.println("Determine handler for the current request.");                 
  16.                 mappedHandler = getHandler(processedRequest, false);  
  17.                 if (mappedHandler == null || mappedHandler.getHandler() == null) {  
  18.                     noHandlerFound(processedRequest, response);  
  19.                     return;  
  20.                 }  
  21.   
  22.                 // Determine handler adapter for the current request.  
  23. System.out.println("Determine handler adapter for the current request.");  
  24.                 HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());  
  25.   
  26.                 // Process last-modified header, if supported by the handler.  
  27. System.out.println("Process last-modified header, if supported by the handler.");  
  28.                 String method = request.getMethod();  
  29.                 boolean isGet = "GET".equals(method);  
  30.                 if (isGet || "HEAD".equals(method)) {  
  31.                     long lastModified = ha.getLastModified(request, mappedHandler.getHandler());  
  32.                     if (logger.isDebugEnabled()) {  
  33.                         String requestUri = urlPathHelper.getRequestUri(request);  
  34.                         logger.debug("Last-Modified value for [" + requestUri + "] is: " + lastModified);  
  35.                     }  
  36.                     if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {  
  37.                         return;  
  38.                     }  
  39.                 }  
  40.   
  41.                 // Apply preHandle methods of registered interceptors.  
  42. System.out.println("Apply preHandle methods of registered interceptors.");  
  43.                 HandlerInterceptor[] interceptors = mappedHandler.getInterceptors();  
  44.                 if (interceptors != null) {  
  45.                     for (int i = 0; i < interceptors.length; i++) {  
  46.                         HandlerInterceptor interceptor = interceptors[i];  
  47.                         if (!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) {  
  48.                             triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);  
  49.                             return;  
  50.                         }  
  51.                         interceptorIndex = i;  
  52.                     }  
  53.                 }  
  54.   
  55.                 // Actually invoke the handler.  
  56. System.out.println("Actually invoke the handler.");  
  57.                 mv = ha.handle(processedRequest, response, mappedHandler.getHandler());  
  58. System.out.println("返回mv为空,假设handler已经处理完了");  
  59.   
  60.                 // Do we need view name translation?  
  61. System.out.println("Do we need view name translation?");  
  62.                 if (mv != null && !mv.hasView()) {  
  63.                     mv.setViewName(getDefaultViewName(request));  
  64.                 }  
  65.   
  66.                 // Apply postHandle methods of registered interceptors.  
  67. System.out.println("Apply postHandle methods of registered interceptors.");  
  68.                 if (interceptors != null) {  
  69.                     for (int i = interceptors.length - 1; i >= 0; i--) {  
  70.                         HandlerInterceptor interceptor = interceptors[i];  
  71.                         interceptor.postHandle(processedRequest, response, mappedHandler.getHandler(), mv);  
  72.                     }  
  73.                 }  
  74.             }  
  75.             catch (ModelAndViewDefiningException ex) {  
  76.                 logger.debug("ModelAndViewDefiningException encountered", ex);  
  77.                 mv = ex.getModelAndView();  
  78.             }  
  79.             catch (Exception ex) {  
  80.                 Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);  
  81.                 mv = processHandlerException(processedRequest, response, handler, ex);  
  82.                 errorView = (mv != null);  
  83.             }  
  84.   
  85.             // Did the handler return a view to render?  
  86.             if (mv != null && !mv.wasCleared()) {  
  87. System.out.println("执行render了");  
  88.                 render(mv, processedRequest, response);  
  89.                 if (errorView) {  
  90.                     WebUtils.clearErrorRequestAttributes(request);  
  91.                 }  
  92.             }  
  93.             else {  
  94.                 if (logger.isDebugEnabled()) {  
  95.                     logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +  
  96.                             "': assuming HandlerAdapter completed request handling");  
  97.                 }  
  98.             }  
  99.   
  100.             // Trigger after-completion for successful outcome.  
  101. System.out.println("Trigger after-completion for successful outcome.");  
  102.             triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);  
  103.         }  
  104.   
  105.         catch (Exception ex) {  
  106.             // Trigger after-completion for thrown exception.  
  107.             triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);  
  108.             throw ex;  
  109.         }  
  110.         catch (Error err) {  
  111.             ServletException ex = new NestedServletException("Handler processing failed", err);  
  112.             // Trigger after-completion for thrown exception.  
  113.             triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);  
  114.             throw ex;  
  115.         }  
  116.   
  117.         finally {  
  118.             // Clean up any resources used by a multipart request.  
  119.             if (processedRequest != request) {  
  120.                 cleanupMultipart(processedRequest);  
  121.             }  
  122.         }  
  123.     }  

 

 这里才是最精髓的,代码

 

Java代码  收藏代码
  1. System.out.println("Determine handler for the current request.");  

 

 对应日志部分

 

Log代码  收藏代码
  1. Determine handler for the current request.  
  2. TRACE: org.springframework.web.servlet.DispatcherServlet - Testing handler map [org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping@20d1fa4] in DispatcherServlet with name 'appServlet'  
  3. TRACE: org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping - No handler mapping found for [/simple]  
  4. TRACE: org.springframework.web.servlet.DispatcherServlet - Testing handler map [org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping@5b33a7af] in DispatcherServlet with name 'appServlet'  
  5. DEBUG: org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping - Mapping [/simple] to HandlerExecutionChain with handler [org.springframework.samples.mvc.simple.SimpleController@573b7064] and 1 interceptor  

 

 代码

 

Java代码  收藏代码
  1. Determine handler adapter for the current request.  

 

 对应日志部分

 

Log代码  收藏代码
  1. Determine handler adapter for the current request.  
  2. TRACE: org.springframework.web.servlet.DispatcherServlet - Testing handler adapter [org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter@557ce3bb]  
  3. TRACE: org.springframework.web.servlet.DispatcherServlet - Testing handler adapter [org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter@6996a298]  
  4. TRACE: org.springframework.web.servlet.DispatcherServlet - Testing handler adapter [org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter@783b67a7]  

 

 handlerMapping测试了BeanNameUrlHandlerMapping发现没有,接着找DefaultAmmotationHandlerMapping,有对应的映射,搞定。

handlerAdapter测试了HttpRequestHandlerAdapter不行,SimpleControllerHandlerAdapter不行,最后找到AnnotationMethodHandlerAdapter,有对应关系,搞定。

下面来到这段代码

 

Java代码  收藏代码
  1. System.out.println("Actually invoke the handler.");  
  2.                 mv = ha.handle(processedRequest, response, mappedHandler.getHandler());  
  3. System.out.println("返回mv为空,假设handler已经处理完了");  

 

 注意这里的mappedHandler.geteHandler()就是一个AnnotationMethodHandlerAdapter。

再调用

 

Java代码  收藏代码
  1. public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)  
  2.             throws Exception {  
  3. System.out.println(getClass());  
  4.   
  5.         Class<?> clazz = ClassUtils.getUserClass(handler);  
  6.         Boolean annotatedWithSessionAttributes = this.sessionAnnotatedClassesCache.get(clazz);  
  7.         if (annotatedWithSessionAttributes == null) {  
  8.             annotatedWithSessionAttributes = (AnnotationUtils.findAnnotation(clazz, SessionAttributes.class) != null);  
  9.             this.sessionAnnotatedClassesCache.put(clazz, annotatedWithSessionAttributes);  
  10.         }  
  11.   
  12.         if (annotatedWithSessionAttributes) {  
  13.             // Always prevent caching in case of session attribute management.  
  14.             checkAndPrepare(request, response, this.cacheSecondsForSessionAttributeHandlers, true);  
  15.             // Prepare cached set of session attributes names.  
  16.         }  
  17.         else {  
  18.             // Uses configured default cacheSeconds setting.  
  19.             checkAndPrepare(request, response, true);  
  20.         }  
  21.   
  22.         // Execute invokeHandlerMethod in synchronized block if required.  
  23.         if (this.synchronizeOnSession) {  
  24. System.out.println("同步");  
  25.             HttpSession session = request.getSession(false);  
  26.             if (session != null) {  
  27.                 Object mutex = WebUtils.getSessionMutex(session);  
  28.                 synchronized (mutex) {  
  29.                     return invokeHandlerMethod(request, response, handler);  
  30.                 }  
  31.             }  
  32.         }  
  33.   
  34.         return invokeHandlerMethod(request, response, handler);  
  35.     }  

 

 其他的无视,看invokeHandlerMethod方法

 

Java代码  收藏代码
  1. protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, Object handler)  
  2.             throws Exception {  
  3. System.out.println("invokeHandlerMethod");  
  4.   
  5.         ServletHandlerMethodResolver methodResolver = getMethodResolver(handler);  
  6.         Method handlerMethod = methodResolver.resolveHandlerMethod(request);  
  7.         ServletHandlerMethodInvoker methodInvoker = new ServletHandlerMethodInvoker(methodResolver);  
  8.         ServletWebRequest webRequest = new ServletWebRequest(request, response);  
  9.         ExtendedModelMap implicitModel = new BindingAwareModelMap();  
  10.   
  11.         Object result = methodInvoker.invokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel);  
  12. System.out.println("result:[" + result + "]");  
  13.         ModelAndView mav =  
  14.                 methodInvoker.getModelAndView(handlerMethod, handler.getClass(), result, implicitModel, webRequest);  
  15.         methodInvoker.updateModelAttributes(handler, (mav != null ? mav.getModel() : null), implicitModel, webRequest);  
  16.         return mav;  
  17.     }  

 

进入 getModelAndView方法

 

Java代码  收藏代码
  1. public ModelAndView getModelAndView(Method handlerMethod, Class handlerType, Object returnValue,  
  2.                 ExtendedModelMap implicitModel, ServletWebRequest webRequest) throws Exception {  
  3. System.out.println("getModelAndView[" + returnValue.getClass() + "]");  
  4.   
  5.             ResponseStatus responseStatusAnn = AnnotationUtils.findAnnotation(handlerMethod, ResponseStatus.class);  
  6.             if (responseStatusAnn != null) {  
  7.                 HttpStatus responseStatus = responseStatusAnn.value();  
  8.                 String reason = responseStatusAnn.reason();  
  9.                 if (!StringUtils.hasText(reason)) {  
  10.                     webRequest.getResponse().setStatus(responseStatus.value());  
  11.                 }  
  12.                 else {  
  13.                     webRequest.getResponse().sendError(responseStatus.value(), reason);  
  14.                 }  
  15.   
  16.                 // to be picked up by the RedirectView  
  17.                 webRequest.getRequest().setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, responseStatus);  
  18.   
  19.                 responseArgumentUsed = true;  
  20.             }  
  21.   
  22.             // Invoke custom resolvers if present...  
  23.             if (customModelAndViewResolvers != null) {  
  24. System.out.println("Invoke custom resolvers if present...");  
  25.                 for (ModelAndViewResolver mavResolver : customModelAndViewResolvers) {  
  26.                     ModelAndView mav = mavResolver.resolveModelAndView(  
  27.                             handlerMethod, handlerType, returnValue, implicitModel, webRequest);  
  28.                     if (mav != ModelAndViewResolver.UNRESOLVED) {  
  29.                         return mav;  
  30.                     }  
  31.                 }  
  32.             }  
  33.   
  34.             if (returnValue instanceof HttpEntity) {  
  35.                 handleHttpEntityResponse((HttpEntity<?>) returnValue, webRequest);  
  36.                 return null;  
  37.             }  
  38.             else if (AnnotationUtils.findAnnotation(handlerMethod, ResponseBody.class) != null) {  
  39. System.out.println("原来进入了这个分支");  
  40.                 handleResponseBody(returnValue, webRequest);  
  41.                 return null;  
  42.             }  
  43.             else if (returnValue instanceof ModelAndView) {  
  44.                 ModelAndView mav = (ModelAndView) returnValue;  
  45.                 mav.getModelMap().mergeAttributes(implicitModel);  
  46.                 return mav;  
  47.             }  
  48.             else if (returnValue instanceof Model) {  
  49.                 return new ModelAndView().addAllObjects(implicitModel).addAllObjects(((Model) returnValue).asMap());  
  50.             }  
  51.             else if (returnValue instanceof View) {  
  52.                 return new ModelAndView((View) returnValue).addAllObjects(implicitModel);  
  53.             }  
  54.             else if (AnnotationUtils.findAnnotation(handlerMethod, ModelAttribute.class) != null) {  
  55.                 addReturnValueAsModelAttribute(handlerMethod, handlerType, returnValue, implicitModel);  
  56.                 return new ModelAndView().addAllObjects(implicitModel);  
  57.             }  
  58.             else if (returnValue instanceof Map) {  
  59.                 return new ModelAndView().addAllObjects(implicitModel).addAllObjects((Map) returnValue);  
  60.             }  
  61.             else if (returnValue instanceof String) {  
  62. System.out.println("返回值为String");  
  63.                 return new ModelAndView((String) returnValue).addAllObjects(implicitModel);  
  64.             }  
  65.             else if (returnValue == null) {  
  66.                 // Either returned null or was 'void' return.  
  67.                 if (this.responseArgumentUsed || webRequest.isNotModified()) {  
  68.                     return null;  
  69.                 }  
  70.                 else {  
  71.                     // Assuming view name translation...  
  72.                     return new ModelAndView().addAllObjects(implicitModel);  
  73.                 }  
  74.             }  
  75.             else if (!BeanUtils.isSimpleProperty(returnValue.getClass())) {  
  76.                 // Assume a single model attribute...  
  77.                 addReturnValueAsModelAttribute(handlerMethod, handlerType, returnValue, implicitModel);  
  78.                 return new ModelAndView().addAllObjects(implicitModel);  
  79.             }  
  80.             else {  
  81.                 throw new IllegalArgumentException("Invalid handler method return value: " + returnValue);  
  82.             }  
  83.         }  

 

 于是我们看到了熟悉的ResponseBody.class的身影

然后handleResponseBody方法

 

Java代码  收藏代码
  1. private void handleResponseBody(Object returnValue, ServletWebRequest webRequest)  
  2.                 throws Exception {  
  3. System.out.println("OK搞定");  
  4.             if (returnValue == null) {  
  5.                 return;  
  6.             }  
  7.             HttpInputMessage inputMessage = createHttpInputMessage(webRequest);  
  8.             HttpOutputMessage outputMessage = createHttpOutputMessage(webRequest);  
  9.             writeWithMessageConverters(returnValue, inputMessage, outputMessage);  
  10.         }  

 

 可以看到就是在这个方法中直接将字符串发送到浏览器的。writeWithMessageConverters方法无关紧要就不要列出来了。

呼,我是为了完整性,才将方法整个拷贝下来了,其实我们只需要关注主要的一行调用代码即可。

总结一下?

我觉得原理知道即可,关键还是会用,会搭建。下面讲一下如何搭建这个sample。

老办法,从GitHub上将代码clone下来加载到IDE中。

 

Shell代码  收藏代码
  1. git clone git://github.com/stephansun/samples.git  

 

最后补充一下pom.xml

Xml代码  收藏代码
  1. <dependencies>  
  2.     <dependency>  
  3.         <groupId>org.springframework</groupId>  
  4.         <artifactId>spring-webmvc</artifactId>  
  5.         <version>3.1.0.RELEASE</version>  
  6.     </dependency>  
  7.   </dependencies>  

  仅仅加载了spring-webmvc,打开m2e的Dependency Hierarchy栏,观察spring模块间的依赖关系:

spring-webmvc

|- spring-asm

|- spring-beans

|- spring-context

|- spring-context-support

|- spring-core

|- spring-expression

|- spring-web

 

spring-beans

|- spring-core

 

spring-context

|- spring-aop

|- spring-beans

|- spring-core

|- spring-expression

|- spring-asm

 

spring-context-support

|- spring-beans

|- spring-context

|- spring-core

 

spring-core

|- spring-asm

|- commons-logging

 

spring-expression

|- spring-core

 

spring-web

|- aoplliance

|- spring-beans

|- spring-context

|- spring-core

 

Spring发布包

 

  • spring-aop.jar:此JAR文件包含了所有你在应用中使用Spring AOP特性时需要的类。如果应用中使用了其他涉及AOP的Spring功能时,例如声明式事务管理,你也需要将此JAR文件包含进来。
  • spring-beans.jar:此文件包含了所有Spring依赖注入需要的代码。包括bean工厂和相关支持类。大部分情况下,你会需要加入spring-context.jar文件,它包含了建立应用环境上下文需要的代码
  • spring-context.jar:此JAR文件包含了建立Spring应用环境上下文所需的代码,它将主要的ApplicationContext接口和实现、说明、JNDI、调度、主题和验证一起纳入其中
  • spring-context-support.jar:这个包文件包括了Spring的工具代码,其中包括说明、电子邮件、调度支持以及脚本语言支持
  • spring-core.jar:此文件包含了Spring框架的核心代码。它用来处理注解、枚举、任务执行、资源加载以及其他一些即便在Spring框架环境外也会有用的工具和异常类。
  • spring-jdbc.jar:此文件包含了JDBC支持类的代码,例如JdbcTemplate类和JdbcDaoSupport类
  • spring-jms.jar:此文件包含jms的代码
  • spring-orm.jar:此文件包含了对象-关系映射(ORM)工具需要的文件。把这个包加入到classpath上将会给你提供针对Hibernate3,iBatis,JDO,JPA和TopLink的Spring支持
  • spring-test.jar:此文件包含了使用Spring框架编写单元测试和集成测试的支持代码
  • spring-tx.jar:此文件提供了核心的数据访问异常和事务技术支持。这两个概念彼此关系密切,因为一般情况下事务总同某些数据访问代码一起工作的
  • spring-web.jar:此文件包含了Spring Web支持(工具类,绑定器,分段文件解析器)的代码
  • spring-webmvc.jar:此文件包含了Spring MVC的代码
0 0
原创粉丝点击