SpringMVC深度探险

来源:互联网 发布:五子棋c语言源代码 编辑:程序博客网 时间:2024/04/26 16:59

SpringMVC深度探险(一) —— SpringMVC前传http://blog.csdn.net/goalstar/article/details/7627715
SpringMVC深度探险(二) —— SpringMVC概览http://blog.csdn.net/goalstar/article/details/7627720
SpringMVC深度探险(三) —— DispatcherServlet与初始化主线 http://blog.csdn.net/goalstar/article/details/7627740
SpringMVC深度探险(四) —— SpringMVC核心配置文件详解 http://blog.csdn.net/goalstar/article/details/7627725


核心Servlet应该能够根据一定的规则对不同的Http请求分发到不同的Servlet对象上去进行处理。

核心Servlet应该能够建立起一整套完整的对所有Http请求进行规范化处理的流程。


这一被提炼出来的Servlet,通常被我们称之为:核心分发器。在SpringMVC中,核心分发器就是org.springframework.web.servlet.DispatcherServlet。


DispatcherServlet的引入是我们通过加入新的编程元素来对基本的Servlet规范进行抽象概括所迈出的第一步。不过接下来,有关DispatcherServlet的设计问题又一次摆到了我们的面前。 


如果仔细分析一下上一节末尾所提出的两个问题,我们可以发现这两个问题实际上都涉及到了DispatcherServlet的处理过程,这一处理过程首先必须是一剂万能药,能够处理所有的Http请求;同时,DispatcherServlet还需要完成不同协议之间的转化工作(从Http协议到Java世界的转化)。 


对此,SpringMVC所提出的方案是:将整个处理流程规范化,并把每一个处理步骤分派到不同的组件中进行处理。 


这个方案实际上涉及到两个方面: 
处理流程规范化 —— 将处理流程划分为若干个步骤(任务),并使用一条明确的逻辑主线将所有的步骤串联起来
处理流程组件化 —— 将处理流程中的每一个步骤(任务)都定义为接口,并为每个接口赋予不同的实现模式
在SpringMVC的设计中,这两个方面的内容总是在一个不断交叉、互为补充的过程中逐步完善的。 


处理流程规范化是目的,对于处理过程的步骤划分和流程定义则是手段。因而处理流程规范化的首要内容就是考虑一个通用的Servlet响应程序大致应该包含的逻辑步骤: 


步骤1 —— 对Http请求进行初步处理,查找与之对应的Controller处理类(方法)
步骤2 —— 调用相应的Controller处理类(方法)完成业务逻辑
步骤3 —— 对Controller处理类(方法)调用时可能发生的异常进行处理
步骤4 —— 根据Controller处理类(方法)的调用结果,进行Http响应处理


步骤1 —— HandlerMapping
步骤2 —— HandlerAdapter
步骤3 —— HandlerExceptionResolver
步骤4 —— ViewResolver


http://docs.spring.io/spring/docs/3.1.x/spring-framework-reference/html/mvc.html


Table 16.1. Special bean types in the WebApplicationContext


Bean type Explanation
HandlerMapping Maps incoming requests to handlers and a list of pre- and post-processors (handler interceptors) based on some criteria the details of which vary by HandlerMapping implementation. The most popular implementation supports annotated controllers but other implementations exists as well.
HandlerAdapter Helps the DispatcherServlet to invoke a handler mapped to a request regardless of the handler is actually invoked. For example, invoking an annotated controller requires resolving various annotations. Thus the main purpose of a HandlerAdapter is to shield the DispatcherServlet from such details.
HandlerExceptionResolver Maps exceptions to views also allowing for more complex exception handling code.
ViewResolver Resolves logical String-based view names to actual View types.
LocaleResolver Resolves the locale a client is using, in order to be able to offer internationalized views
ThemeResolver Resolves themes your web application can use, for example, to offer personalized layouts
MultipartResolver Parses multi-part requests for example to support processing file uploads from HTML forms.
FlashMapManager Stores and retrieves the "input" and the "output" FlashMap that can be used to pass attributes from one request to another, usually across a redirect.


这些组件是如何串联在一起的?这个过程,是在DispatcherServlet中完成的。


// 获取HandlerMapping组件返回的执行链
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}


// 获取HandlerAdapter组件
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

if (exception != null) {
if (exception instanceof ModelAndViewDefiningException) {
logger.debug("ModelAndViewDefiningException encountered", exception);
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
}
else {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
}






注:SpringMVC在进行组件管理时,会单独为SpringMVC相关的组件构建一个容器环境,这一容器环境可以独立于应用程序自身所创建的Spring容器。有关这一点,我们在之后的讨论中将详细给出分析。


SpringMVC就是通过DispatcherServlet将一堆组件串联起来的Web框架!!


在引入组件这个概念的时候,我们所强调的是处理流程的抽象化,因而所有组件的外在表现形式是接口。接口最重要意义是定义操作规范,所以接口用来表达每一个处理单元的逻辑语义是最合适不过的。


SpringMVC中比较具有典型意义的行为模式。我们可以截取其中的几个实现来加以说明: 


BeanNameUrlHandlerMapping —— 根据Spring容器中的bean的定义来指定请求映射关系
SimpleUrlHandlerMapping —— 直接指定URL与Controller的映射关系,其中的URL支持Ant风格
DefaultAnnotationHandlerMapping —— 支持通过直接扫描Controller类中的Annotation来确定请求映射关系
RequestMappingHandlerMapping —— 通过扫描Controller类中的Annotation来确定请求映射关系的另外一个实现类


SpringMVC各种不同的组件实现体系成为了SpringMVC行为模式扩展的有效途径!!


SpringMVC的设计原则 
A key design principle in Spring Web MVC and in Spring in general is the “Open for extension, closed for modification” principle.


SpringMVC在整个官方reference的起始就强调这一原则,可见其对于整个框架的重要性。那么我们又如何来理解这段话的含义呢?笔者在这里从源码的角度归纳了四个方面:
1. 使用final关键字来限定核心组件中的核心方法 
2. 大量地在核心组件中使用private方法 
3. 限定某些类对外部程序不可见 ResourcesBeanDefinitionParser、InterceptorsBeanDefinitionParser等未看到public修饰符。也就是说,这些类只能在SpringMVC的内部被调用,对于框架以外的应用程序是不可见的。
4. 提供自定义扩展接口,却不提供完整覆盖默认行为的方式 


SpringMVC提供的扩展切入点无法改变框架默认的行为方式!!
SpringMVC的逻辑主线始终不变,而行为模式却可以多种多样!!


从Spring2.5这个版本之后才不断显现出来的。这条设计原则可以用2个字来概括:简化。 
Annotation —— 简化各类配置定义
Schema Based XML —— 简化组件定义


Annotation是JDK5.0带来的一种全新的Java语法。这种语法的设计初衷众说纷纭,并没有一个标准的答案。


Annotation的原型是注释。作为一种对注释的扩展而被引入成为一个语法要素,其本身就是为了对所标注的编程元素进行补充说明,从而进一步完善编程元素的逻辑语义!!


从这个结论中,我们可以看到一层潜在的意思:在Annotation出现之前,Java自身语法所定义的编程元素已经不足以表达足够多的信息或者逻辑语义。在这种情况下,过去经常使用的方法是引入新的编程元素(例如使用最多的就是XML形式的结构化配置文件)来对Java程序进行补充说明。而在Annotation出现之后,可以在一定程度上有效解决这一问题。因此Annotation在很长一段时间都被当作是XML配置文件的替代品。 


这也就是Annotation经常被用来和XML进行比较的原因。孰优孰劣其实还是要视具体情况而定,并没有什么标准答案。不过我们在这里想强调的是Annotation在整个SpringMVC中所起到的作用,并非仅仅是代替XML那么简单。我们归纳了有三个不同的方面: 


1. 简化请求映射的定义 
在Spring2.5之前,所有的Http请求与Controller核心处理器之间的映射关系都是在XML文件中定义的。作为XML配置文件的有效替代品,Annotation接过了定义映射关系的重任。我们可以将@RequestMapping加在Controller的class-level和method-level进行Http请求的抽象。 


2. 消除Controller对接口的依赖
在Spring2.5之前,SpringMVC规定所有的Controller都必须实现Controller接口: 


3. 成为框架进行逻辑处理的标识 
之前已经谈到,Annotation主要被用于对编程元素进行补充说明。因而Spring就利用这一特性,使得那些被加入了特殊Annotation的编程元素可以得到特殊的处理。例如,SpringMVC引入的@SessionAttribute、@RequestBody、@ModelAttribute等等,可以说既是对Controller的一种逻辑声明,也成为了框架本身对相关元素进行处理的一个标识符。 


再谈谈Schema Based XML。Schema Based XML并不是一个陌生的概念,早在Spring2.0时代就被用于进行XML配置的简化。SpringMVC在进入到3.0版本之后,正式将其引入并作为SpringMVC组件定义的一个重要手段。 


在XML中引入Schema,只需要在XML文件的开头加入相关的定义。例如: 
<beans xmlns="http://www.springframework.org/schema/beans"  
       xmlns:mvc="http://www.springframework.org/schema/mvc"  
       xmlns:context="http://www.springframework.org/schema/context"  
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
       xsi:schemaLocation="  
            http://www.springframework.org/schema/beans  
            http://www.springframework.org/schema/beans/spring-beans-3.1.xsd  
            http://www.springframework.org/schema/context   
            http://www.springframework.org/schema/context/spring-context-3.1.xsd  
            http://www.springframework.org/schema/mvc  
            http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd">  
</beans>  


而Schema的具体处理,则位于Spring的JAR中的/META-INF/spring.handlers文件中进行定义: 
http\://www.springframework.org/schema/mvc=org.springframework.web.servlet.config.MvcNamespaceHandler  


==============================================================================================
==============================================================================================
==============================================================================================


SpringMVC将Http处理流程抽象为一个又一个处理单元
SpringMVC定义了一系列组件(接口)与所有的处理单元对应起来
SpringMVC由DispatcherServlet贯穿始终,并将所有的组件串联起来


---DispatcherServlet的体系结构---


通过不同的角度来观察DispatcherServlet会得到不同的结论。我们在这里选取了三个不同的角度:运行特性、继承结构和数据结构。 
从DispatcherServlet所实现的接口来看,DispatcherServlet的核心本质:是一个Servlet。这个结论似乎很幼稚,不过这个幼稚的结论却蕴含了一个对整个框架都至关重要的内在原则:Servlet可以根据其特性进行运行主线的划分。 


根据Servlet规范的定义,Servlet中的两大核心方法init方法和service方法,它们的运行时间和触发条件都截然不同: 


1. init方法 
在整个系统启动时运行,且只运行一次。因此,在init方法中我们往往会对整个应用程序进行初始化操作。这些初始化操作可能包括对容器(WebApplicationContext)的初始化、组件和外部资源的初始化等等。 


2. service方法 
在整个系统运行的过程中处于侦听模式,侦听并处理所有的Web请求。因此,在service及其相关方法中,我们看到的则是对Http请求的处理流程。 


因而在这里,Servlet的这一特性就被SpringMVC用于对不同的逻辑职责加以划分,从而形成两条互不相关的逻辑运行主线: 
初始化主线 —— 负责对SpringMVC的运行要素进行初始化
Http请求处理主线 —— 负责对SpringMVC中的组件进行逻辑调度完成对Http请求的处理


HttpServletBean源码:
try {
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
initBeanWrapper(bw);
bw.setPropertyValues(pvs, true);
}
catch (BeansException ex) {
logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
throw ex;
}


// Let subclasses do whatever initialization they like.
initServletBean();  //initServletBean();是个回调,也应用了模板方法

HttpServletBean从源码中,我们可以看到HttpServletBean利用了Servlet的init方法的执行特性,将一个普通的Servlet与Spring的容器联系在了一起。在这其中起到核心作用的代码是:BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);将当前的这个Servlet类转化为一个BeanWrapper,从而能够以Spring的方式来对init-param的值进行注入。BeanWrapper的相关知识属于Spring Framework的内容,我们在这里不做详细展开,读者可以具体参考HttpServletBean的注释获得更多的信息。 


FrameworkServlet则是在HttpServletBean的基础之上的进一步抽象。通过FrameworkServlet真正初始化了一个Spring的容器(WebApplicationContext),并引入到Servlet对象之中: 
protected final void initServletBean() throws ServletException {
getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
if (this.logger.isInfoEnabled()) {
this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
}
long startTime = System.currentTimeMillis();


try {
this.webApplicationContext = initWebApplicationContext();
initFrameworkServlet();
}
catch (ServletException ex) {
this.logger.error("Context initialization failed", ex);
throw ex;
}
catch (RuntimeException ex) {
this.logger.error("Context initialization failed", ex);
throw ex;
}


if (this.logger.isInfoEnabled()) {
long elapsedTime = System.currentTimeMillis() - startTime;
this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +
elapsedTime + " ms");
}
}


上面的这段代码就是FrameworkServlet初始化的核心代码。从中我们可以看到这个FrameworkServlet将调用其内部的方法initWebApplicationContext()对Spring的容器(WebApplicationContext)进行初始化。同时,FrameworkServlet还暴露了与之通讯的结构可供子类调用:
 
public abstract class FrameworkServlet extends HttpServletBean {  
  
    /** WebApplicationContext for this servlet */  
    private WebApplicationContext webApplicationContext;  
  
        // 这里省略了其他所有的代码  
  
    /** 
     * Return this servlet's WebApplicationContext. 
     */  
    public final WebApplicationContext getWebApplicationContext() {  
        return this.webApplicationContext;  
    }  
}  


我们在这里暂且不对Spring容器(WebApplicationContext)的初始化过程详加探查,稍后我们会讨论一些WebApplicationContext初始化过程中的配置选项。不过读者可以在这里体会到:FrameworkServlet在其内部初始化了一个Spring的容器(WebApplicationContext)并暴露了相关的操作接口,因而继承自FrameworkServlet的DispatcherServlet,也就直接拥有了与WebApplicationContext进行通信的能力。


DispatcherServlet的继承体系架起了DispatcherServlet与Spring容器进行沟通的桥梁!!


组件是整个DispatcherServlet的灵魂所在:它不仅是初始化主线中的初始化对象,同样也是Http请求处理主线中的逻辑调度载体!!




---SpringMVC的运行体系---


DispatcherServlet继承结构和数据结构,实际上表述的是DispatcherServlet与另外两大要素之间的关系: 
继承结构 —— DispatcherServlet与Spring容器(WebApplicationContext)之间的关系
数据结构 —— DispatcherServlet与组件之间的关系


SpringMVC的整个运行体系,是由DispatcherServlet、组件和容器这三者共同构成的!!!!


DispatcherServlet - 容器 —— DispatcherServlet对容器进行初始化
容器 - 组件 —— 容器对组件进行全局管理
DispatcherServlet - 组件 —— DispatcherServlet对组件进行逻辑调用




----DispatcherServlet的初始化主线 


对于DispatcherServlet的初始化主线,我们首先应该明确几个基本观点: 


初始化主线的驱动要素 —— servlet中的init方法
初始化主线的执行次序 —— HttpServletBean -> FrameworkServlet -> DispatcherServlet
初始化主线的操作对象 —— Spring容器(WebApplicationContext)和组件


在默认情况下,web.xml配置节点中<servlet-name>的值就是建立起核心分发器DispatcherServlet与核心配置文件之间联系的桥梁。DispatcherServlet在初始化时会加载位置在/WEB-INF/[servlet-name]-servlet.xml的配置文件作为SpringMVC的核心配置!!


SpringMVC在这里采用了一个“命名约定”的方法进行关系映射,这种方法很廉价也很管用。以上面的配置为例,我们就必须在/WEB-INF/目录下,放一个名为dispatcher-servlet.xml的Spring配置文件作为SpringMVC的核心配置用以指定SpringMVC的基本组件声明定义。 


<servlet>  
    <servlet-name>dispatcher</servlet-name>  
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>  
    <init-param>  
        <param-name>contextConfigLocation</param-name>  
        <param-value>classpath:web/applicationContext-dispatcherServlet.xml</param-value>  
    </init-param>  
    <load-on-startup>1</load-on-startup>  
</servlet>  
          
<servlet-mapping>  
    <servlet-name>dispatcher</servlet-name>  
    <url-pattern>/</url-pattern>  
</servlet-mapping>  


结论 SpringMVC核心配置文件中所有的bean定义,就是SpringMVC的组件定义,也是DispatcherServlet在初始化容器(WebApplicationContext)时,所要进行初始化的组件!!


在上一篇文章我们谈到组件的时候就曾经提到,SpringMVC自身对于组件并未实现一套完整的管理机制,而是借用了Spring Framework核心框架中容器的概念,将所有的组件纳入到容器中进行管理。所以,SpringMVC的核心配置文件使用与传统Spring Framework相同的配置形式,而整个管理的体系也是一脉相承的。 


SpringMVC的组件是一个个的接口定义,当我们在SpringMVC的核心配置文件中定义一个组件时,使用的却是组件的实现类:


<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">  
    <property name="prefix" value="/" />  
    <property name="suffix" value=".jsp" />  
</bean>  


这也就是Spring管理组件的模式:用具体的实现类来指定接口的行为方式。不同的实现类,代表着不同的组件行为模式,它们在Spring容器中是可以共存的:


<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">  
    <property name="prefix" value="/" />  
    <property name="suffix" value=".jsp" />  
</bean>  
  
<bean class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">  
    <property name="prefix" value="/" />  
    <property name="suffix" value=".ftl" />  
</bean>  


所以,Spring的容器就像是一个聚宝盆,它只负责承载对象,管理好对象的生命周期,而并不关心一个组件接口到底有多少种实现类或者行为模式。这也就是我们在上面那幅图中,画了多个HandlerMappings、HandlerAdapters和ViewResolvers的原因:一个组件的多种行为模式可以在容器中共存,容器将负责对这些实现类进行管理。而具体如何使用这些对象,则由应用程序自身来决定。 


我们可以大致概括一下WebApplicationContext初始化的两个逻辑层次: 
DispatcherServlet负责对容器(WebApplicationContext)进行初始化。
容器(WebApplicationContext)将读取SpringMVC的核心配置文件进行组件的实例化。


---独立的WebApplicationContext体系---


在DispatcherServlet初始化的过程中所构建的WebApplicationContext独立于Spring自身的所构建的其他WebApplicationContext体系而存在。


稍有一些Spring编程经验的程序员,对于下面的配置应该非常熟悉:
<context-param>  
    <param-name>contextConfigLocation</param-name>  
    <param-value>classpath:context/applicationContext-*.xml</param-value>  
</context-param>  
      
<listener>  
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>  
</listener>  


在上面的代码中,我们定义了一个Listener,它会在整个Web应用程序启动的时候运行一次,并初始化传统意义上的Spring的容器。这也是一般情况下,当并不使用SpringMVC作为我们的表示层解决方案,却希望在我们的Web应用程序中使用Spring相关功能时所采取的一种配置方式。 


如果我们要在这里引入SpringMVC,整个配置看上去就像这样
<context-param>  
    <param-name>contextConfigLocation</param-name>  
    <param-value>classpath:context/applicationContext-*.xml</param-value>  
</context-param>  
      
<listener>  
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>  
</listener>  
      
<!-- Processes application requests -->  
<servlet>  
    <servlet-name>dispatcher</servlet-name>  
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>  
    <init-param>  
        <param-name>contextConfigLocation</param-name>  
        <param-value>classpath:web/applicationContext-dispatcherServlet.xml</param-value>  
    </init-param>  
    <load-on-startup>1</load-on-startup>  
</servlet>  
          
<servlet-mapping>  
    <servlet-name>dispatcher</servlet-name>  
    <url-pattern>/</url-pattern>  
</servlet-mapping>  


在这种情况下,DispatcherServlet和ContextLoaderListener会分别构建不同作用范围的容器(WebApplicationContext)。我们可以引入两个不同的概念来对其进行表述:ContextLoaderListener所初始化的容器,我们称之为Root WebApplicationContext;而DispatcherServlet所初始化的容器,是SpringMVC WebApplicationContext。 


整个启动日志被我们分为了2段。第一段的过程初始化的是Root WebApplicationContext;而第二段的过程初始化的是SpringMVC的WebApplicationContext。


Spring正是使用这种Parent-Child的容器关系来对不同的编程层次进行划分。这种我们俗称的父子关系实际上不仅仅是一种从属关系,更是一种引用关系。从刚才的日志分析中,我们可以看出:SpringMVC中所定义的一切组件能够无缝地与Root WebApplicationContext中的组件整合。 


DispatcherServlet的初始化主线的执行体系是顺着其继承结构依次进行的,我们在之前曾经讨论过它的执行次序。所以,只有在FrameworkServlet完成了对于WebApplicationContext和组件的初始化之后,执行权才被正式转移到DispatcherServlet中。我们可以来看看DispatcherServlet此时究竟干了哪些事: 


/** 
 * This implementation calls {@link #initStrategies}. 
 */  
@Override  
protected void onRefresh(ApplicationContext context) {  
    initStrategies(context);  
}  
  
/** 
 * Initialize the strategy objects that this servlet uses. 
 * <p>May be overridden in subclasses in order to initialize further strategy objects. 
 */  
protected void initStrategies(ApplicationContext context) {  
    initMultipartResolver(context);  
    initLocaleResolver(context);  
    initThemeResolver(context);  
    initHandlerMappings(context);  
    initHandlerAdapters(context);  
    initHandlerExceptionResolvers(context);  
    initRequestToViewNameTranslator(context);  
    initViewResolvers(context);  
    initFlashMapManager(context);  
}  


很容易就能明白DispatcherServlet到底在这里干些什么了:初始化组件。 




DispatcherServlet中对于组件的初始化过程实际上是应用程序在WebApplicationContext中选择和查找组件实现类的过程,也是指定组件在SpringMVC中的默认行为方式的过程!!




除了根据特定名称进行查找的策略以外,我们还对DispatcherServlet中指定SpringMVC默认行为方式的其他的策略进行的总结: 


名称查找 —— 根据bean的名字在容器中查找相应的实现类
自动搜索 —— 自动搜索容器中所有某个特定组件(接口)的所有实现类
默认配置 —— 根据一个默认的配置文件指定进行实现类加载




Schema-based XML可以代替Traditional的XML配置形式,在Spring容器中进行组件的定义。


除了头部声明部分的其他配置部分,就是真正的组件定义部分。在这个部分中,我们可以看到两种不同类型的配置定义模式: 


1. 基于Schema-based XML的配置定义模式 
<mvc:annotation-driven />  




2. 基于Traditional XML的配置定义模式 
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">    
       <property name="prefix" value="/" />    
       <property name="suffix" value=".jsp" />    
/bean>  


我们引入Schema-based XML是为了对Traditional的XML配置形式进行简化。通过Schema的定义,把一些原本需要通过几个bean的定义或者复杂的bean的组合定义的配置形式,用另外一种简单而可读的配置形式呈现出来。 


Schema-based XML可以代替Traditional的XML配置形式,在Spring容器中进行组件的定义。
Schema-based XML可以极大改善配置文件的可读性并且缩小配置文件的规模。


Schema-based XML的引入,实际上是把原本静态的配置动态化、过程化。


从形式上看,Schema-based XML相比较Traditional XML至少有三个方面的优势: 
namespace —— 拥有很明确的逻辑分类
element —— 拥有很明确的过程语义
attributes —— 拥有很简明的配置








选项


在Spring中,一个Schema-based XML有两大构成要素:过程实现和配置定义。 
先谈谈过程实现。所谓过程实现,其实就是我们刚才所举的那个例子中,实现实际背后逻辑的过程。这个过程由两个Java接口来进行表述: 
NamespaceHandler —— 对Schema定义中namespace的逻辑处理接口
BeanDefinitionParser —— 对Schema定义中element的逻辑处理接口


NamespaceHandler是入口程序,它包含了所有的属于该namespace定义下所有element的处理调用,所以BeanDefinitionParser的实现就成为了NamespaceHandler的调用对象了。这一点,我们可以通过NamesapceHandler的MVC实现类来加以证明: 
public void init() {  
    registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());  
    registerBeanDefinitionParser("default-servlet-handler", new DefaultServletHandlerBeanDefinitionParser());  
    registerBeanDefinitionParser("interceptors", new InterceptorsBeanDefinitionParser());         
    registerBeanDefinitionParser("resources", new ResourcesBeanDefinitionParser());  
    registerBeanDefinitionParser("view-controller", new ViewControllerBeanDefinitionParser());  
}  










0 0
原创粉丝点击