2009年10月12日星期一

[转]spring的国际化

标题准确来说应该是“使用Spring中的IoC功能来实现我们所开发项目系统的国际化”,国际化不是针对IoC的,而是针对你开发的整个系统。

如果你使用过Eclipse的国际化,或者用过Eclipse的“外部化字符串”向导(Eclipse主菜单:源代码->外部化字符串),那么对Spring提供的国际化功能应该是非常容易理解,两者基本一样,或者说各种Java程序的国际化方式都基本一样。

先谈谈Eclipse国际化的两个组成部分:*.properties的资源文件、获取资源文件内容的Message类。

而Spring则和Eclipse的处理类似:资源文件两者是一样的,不同语言的翻译放在不同的资源文件里,连起名规则都一样;Eclipse的Message类要自己写(代码通用,复制以前项目的即可,或用Eclipse的向导生成一个也行),Spring则已经有写好的Message类,我们在IoC的xml文件里注册一下即可使用(也可以实现Spring的MessageSource接口,自己来写一个Message类,代码并不复杂,不过这没什么必要,用Spring提供的就行了)。

无论是Eclipse的Message类,还是Spring的自带的Message类,或是我们自己写一个Message类,都是使用JDK的java.util.ResourceBundle类来实现*.properties文件的读取。

3.2  简单实例

假设我们有如下程序,程序的作用是打印出一个字符串

package cn.com.chengang.spring;

public class MessageTest {

    public static void main(String[] args) {

        String str = "ChenGang";

        System.out.println(str);

    }

}

现在,我们要让这个程序能够根据使用者的语言情况输出不同的字符,比如:对英文使用者输出“ChenGang”,对中文使用者输出“陈刚”,对台湾使用输出“��”等等。这个需求的实现方法如下:

1、创建一系列的资源文件

在cn.com.chengang.spring包下创建以下文件:

(1)messages.properties(默认:英文),内容仅一句,如下

chengang=Giles

“chengang”是键值,Giles是要输出的英文字符串

(2)messages_zh_CN.properties(简体中文)

chengang=\u9648\u521A

“\u9648\u521A”是UNICODE码,对应的中文是“陈刚”

(3)messages_ zh_TW.properties(繁体中文)

chengang=\u9673\u525B

“\u9673\u525B”对应的中文是“��”

 

附注:由于中文是要转换成UNICODE码,在编辑和阅读上有诸多不便,如果是用Eclipse做IDE,则有一个编辑资源文件的插件jinto,下载网址是http://www.guh-software.de/,用它打开的资源文件如下图所示,可以看到三个资源在一个界面反映了出来。

如果你不用Eclipse,而是用Editplugs+JDK的方式来编程(现在还有这样的原始人吗?),你也可以用JDK自带的native2ascii.exe程序来将中文字串转成UNICODE码。Ant中还提供了一个相应的任务:<native2ascii encoding="GBK" src="${src}" dest="${build}"/>,其中GBK是一个中国的字符集。

 

2、修改bean.xml

将Spring自带的org.springframework.context.support.ResourceBundleMessageSource类注册到bean.xml中,这个类的作用是获取资源文件的内容,注册到IoC的bean.xml文件中是为了自动获得此类的对象(Spring做了一些简化编程的处理)。

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">

<beans>

       <bean id="Chinese" class="cn.com.chengang.spring.Chinese"/>

       <bean id="American" class="cn.com.chengang.spring.American"/>

       <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">

              <property name="basenames">

                     <list>

                            <value>cn.com.chengang.spring.messages</value>

                     </list>

              </property>

       </bean>

</beans>

代码说明:

l           id="messageSource" 的设置是不变的、必须的。

l           ResourceBundleMessageSource是Spring的一个Message类。这里还有一个选择,用ReloadableResourceBundleMessageSource类,此类可以提供不用重启即可重新加载资源文件的特性(前者对资源文件只加载一次)。对于那种有热修改资源文件的需求,后者比较合适,只是后者在效率上有可能有损耗,因为至少要多一些检查资源文件是否改变的代码(这只是我的猜测,我没有仔佃去读这段的源码)。

l           “basenames”是不变的、必须的。它是ResourceBundleMessageSource的一个属性,在源代码中的定义是“private String[] basenames;”,可见它是一个字符串数组。

l           “cn.com.chengang.spring.messages”是把资源文件的位置传入到basenames属性中。注意:三个资源文件只需要将共同的主名(红色字体)传入:messages.properties、messages_zh_CN.properties、messages_zh_TW.properties。

 

3、使用。修改MessageTest类,如下

package cn.com.chengang.spring;

import org.springframework.context.ApplicationContext;

import org.springframework.context.support.FileSystemXmlApplicationContext;

public class MessageTest {

    public static void main(String[] args) {

        ApplicationContext ctx = new FileSystemXmlApplicationContext("bean.xml");

        String str = ctx.getMessage("chengang", null, null);

        System.out.println(str);

    }

}

代码说明:

(1)main方法里

l           第一句取得bean.xml文件的配置信息。

l           第二句从资源文件里得到键值chengang对应的字符串。

l           第三句将字符串打印出来,结果是打印的是“陈刚”,说明读取的是messages_zh_CN.properties资源文件。

(2)ctx.getMessage("chengang", null, null);有三个参数:

l           第一个是资源文件的键值;

l           第二个是资源文件字符串的参数,由于本字符串没有参数,所以用一个null(后面给出了一个用到字符串参数的实例);

l           第三个是一个java.util. Locale类型的参数。参数为null,则表示根据使用者的语言环境来选择Locale,因为我用的是中文版的windows,所以在取字符串时它自动选择了messages_zh_CN.properties资源文件。
   
这其中还有一个控制点在JVM,JVM会根据当前操作系统的语言环境进行相应处理,我们可以通过在JVM启动参数中追加“-Duser.language=zh_TW”来设定当前JVM语言类型,通过JVM级的设定,也可以实现自动切换所使用的资源文件类型。
   
所以这里面的控制语言的方式有三种:从最低层的操作系统的Locale设定,到更上一层的JVM的Locale设定,再到程序一级的Locale设定。

3.3  资源文件的其他使用方式:

package cn.com.chengang.spring;

import java.util.Locale;

import org.springframework.context.ApplicationContext;

import org.springframework.context.support.FileSystemXmlApplicationContext;

import org.springframework.context.support.ResourceBundleMessageSource;

public class MessageTest {

    public static void main(String[] args) {

        ApplicationContext ctx = new FileSystemXmlApplicationContext("bean.xml");

        String str = ctx.getMessage("chengang", null, null);

        System.out.println(str); //输出“陈刚”

        /*

         * 使用了messages.properties

         */

        str = ctx.getMessage("chengang", null, new Locale(""));

        System.out.println(str);//输出“Giles”

        /*

         * 使用了messages_zh_CN.properties

         */

        str = ctx.getMessage("chengang", null, new Locale("zh", "CN"));

        System.out.println(str);//输出“陈刚”

        /*

         * 使用了messages_zh_TW.properties

         */

        str = ctx.getMessage("chengang", null, new Locale("zh", "TW"));

        System.out.println(str);//输出“��”

        /*

         * 使用了messages_zh_TW.properties,从这里可见资源文件的起名可以很随意,

         * 比如我们建立一个messages_123.properties,在传参数时候就可以这样:

         * new Locale("123"),一样也可以取出messages_123.properties中的值

         */

        str = ctx.getMessage("chengang", null, new Locale("zh_TW"));

        System.out.println(str);//输出“��”

        /*

         * 当找不到相应的资源文件时,使用了messages_zh_CN.properties

         */

        str = ctx.getMessage("chengang", null, new Locale("abcd"));

        System.out.println(str);//输出“陈刚”

         /**

         * 不通过IoC注册,直接使用ResourceBundleMessageSource类的写法。

         */

        ResourceBundleMessageSource s = new ResourceBundleMessageSource();

        s.setBasename("cn.com.chengang.spring.messages");

        str = s.getMessage("chengang", null, null);

        System.out.println(str);//输出“陈刚”

    }

}

代码说明:

前面说过控制语言的方式有三种:从最低层的操作系统的Locale设定,到更上一层的JVM的Locale设定,再到程序一级的Locale设定。我认为最佳的方法是在程序一级进行控制:定义一个统一的Locale静态变量,然后整个系统中只使用这一个变量,以后就可以通过界面操作设置此Locale变量的值,让用户来选择他所需的软件语言。而且我们也可以将此静态变量设成null值,来自动选择资源文件。

另外,Locale里也定义了一些常量,我们可以直接使用而不必去new一个Locale对象,如:“Locale.ENGLISH”。

 

3.4  再一个实例

这个实例演示了如何使用多个资源文件,以及如何使用字符串参数

(1)在cn.com.chengang.spring包下再创建一个资源文件messagesOther_zh_CN.properties

chengang.info=\u9648\u521A\uFF0C\u7F51\u540D\uFF1A{0}\uFF0C\u82F1\u6587\u540D\uFF1A{1}\uFF0CBlog\uFF1A{2}

其中UNICODE字符串对应的中文是:“陈刚,网名:{0},英文名:{1},Blog:{2}”,这个字符串一共有三个参数。

(2)修改 bean.xml文件

因为basenames属性是一个数组,当然也就可以接收多个资源文件设定。具体修改如下面的红字部份

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">

<beans>

       <bean id="Chinese" class="cn.com.chengang.spring.Chinese"/>

       <bean id="American" class="cn.com.chengang.spring.American"/>

       <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">

              <property name="basenames">

                     <list>

                            <value>cn.com.chengang.spring.messages</value>

                            <value>cn.com.chengang.spring.messagesOther</value>

                     </list>

              </property>

       </bean>

</beans>

 

(3)修改MessageTest类,加入几行使用的代码

        String[] strArgs = new String[3];

        strArgs[0]="混北民工";

        strArgs[1]="Giles";

        strArgs[2]="http://blog.csdn.net/glchengang";

        str = ctx.getMessage("chengang.info", strArgs, null);

        System.out.println(str);

打印出来的结果就是:“陈刚,网名:混北民工,英文名:Giles,Blog:http://blog.csdn.net/glchengang”

 

3.5  国际化的实践建议

l           建议一个包对应一个资源文件。不要整个系统都使用一个资源文件来翻译,这样单个文档的体积就太大了,不利于维护;当然,也不必一个类对应一个资源文件,这样资源文件又太多了。

l           建议资源文件和其翻译类/包在同一目录下。不过,如果是要将软件打成一外JAR包或WAR包,建议把资源文件分离出来,这样可以修改资源文件,而不必再次打包。

l           建议字符串项的键值上加上其所在的类名。比如:上面的chengang和chengang.info最好是取名成MessageTest.chengang和MessageTest.chengang.info。这样查找使用此键值的类会方便很多。

[note]Spring注解入门两篇

使用 Spring 2.5 注释驱动的 IoC 功能

使用 Spring 2.5 基于注解驱动的 Spring MVC

2009年10月11日星期日

[note]生活计划

前几日看Heroes的最新一季,克莱尔的新室友总是说:"Everyone has a plan!"。刚想起来自己也应该有个计划,这样生活学习比较有动力。

下一步关注的一些库和语言,年底之前要完成的目标:

required:
Spring库的熟练掌握
Rhino库的学习
Linux C的初步学习

optional:
写自己的JS引擎
学习五线谱

[note]Java的两个关键字:synchronize和volatile

Volatile修饰的成员变量在每次被线程访问时,都强迫从共享内存中重读该成员变量的值。而且,当成员变量发生变化时,强迫线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。
Java语言规范中指出:为了获得最佳速度,允许线程保存共享成员变量的私有拷贝,而且只当线程进入或者离开同步代码块时才与共享成员变量的原始值对比。
这样当多个线程同时与某个对象交互时,就必须要注意到要让线程及时的得到共享成员变量的变化。
而volatile关键字就是提示VM:对于这个成员变量不能保存它的私有拷贝,而应直接与共享成员变量交互。
由于使用volatile屏蔽掉了VM中必要的代码优化,所以在效率上比较低,因此一定在必要时才使用此关键字。
Synchronize修饰的变量或者方法在每次被线程访问时,都会先锁定此资源,然后操作,操作完成后解锁。与Volatile最大的不同在于锁定和解锁,保证了操作的原子性,而Volatile只能保证read-write等
原子操作的原子性,当三者混合执行时就会出错,如:
  1. public class TestRaceCondition {  
  2.     private volatile int i = 0;  
  3.   
  4.     public void increase() {  
  5.        i++;  
  6.     }  
  7.   
  8.     public int getValue() {  
  9.        return i;  
  10.     }  
  11. } 
i++这一步等于:i = i + 1;先read i,然后执行+操作,最后赋值更新。当多线程执行时,这些一个个操作之间夹杂着其他线程的操作,就会出现错误。

[转] Spring中的WebAppRootListener

这个listenr的作用就是监听web.xml中的配置para-name为webAppRootKey的值,比如我的web应用为tsts,那么我配置

这样一个

  1. <context-param>
  2.         <param-name>webAppRootKey</param-name>
  3.         <param-value>tsts.root</param-value>
  4.     </context-param>

,然后再配置这样一个监听器:

  1. <listener>
  2.         <listener-class> 
  3.             org.springframework.web.util.WebAppRootListener
  4.         </listener-class>
  5.     </listener>

。这个监听器就会在web上下文初始化的时候,调用webUtil的对应方法,首先获取到param-name对应的param-value ,然后,根据传递进去的ServletContext对象得到web的物理路径:String root = servletContext.getRealPath("/");

接着把这个param-value作为key,root作为value放到system中System.setProperty(key, root);

然后再web中可以用 System.get.....就可以得到web的跟目录的物理路径了。

之前我的做法是用一个filter,在filter的init中调用String root = servletContext.getRealPath("/");,然后再去设置对应一个常量类文件的属性。做法差不多,但是spring的做法更可以借鉴!

[转]spring学习笔记之DispatcherServlet源码解读

spring web MVC 框架,就像其他web MVC框架一样,是请求驱动,围绕一个核心的servlet设计,这个servlet把接收的请求(request)分别分发到不同的控制器并提供其他功能促进web应用的开发。而spring的DispatcherServlet做的事情更多。它完全整合了spring Ioc容器方便你使用spring所拥有的功能。

这幅图解释了spring web MVC DispatcherServlet对请求的处理流程。其实DispatcherServlet是采用了前端控制器(Front Controller)的设计模式,这种设计模式也被其他流行的web框架所采用。

DispatcherServlet层次类图

根据类图,知道DispatcherServlet实际上是一个Servlet,因为它继承于HttpServlet,所以它需要在web.xml定义,并且在web.xml定义需要通过DispatherServlet处理的请求的URL映射。

这是标准的J2EE servlet 配置方式。下面是一个例子:


  1. <web-app>  
  2.     <servlet>  
  3.         <servlet-name>example</servlet-name>  
  4.         <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>  
  5.         <load-on-startup>1</load-on-startup>  
  6.     </servlet>  
  7.     <servlet-mapping>  
  8.         <servlet-name>example</servlet-name>  
  9.         <url-pattern>*.form</url-pattern>  
  10.     </servlet-mapping>  
  11. </web-app>  


       上面这个例子,所有以.form结尾的请求都会被名为example的DispaterServlet处理。这是使用spring MVC的第一步,一系列beans需要配置。

       在spring web MVC框架,每一个DispatcherServlet都有它自己的WebApplicationContext,这些 WebApplicationContext继承了一个根WebApplicationContextde定义的所有bean。这些被继承的bean可以 在具体的servlet中改写作用域,也可以在一个servlet实例定义新的局部的作用域bean。

       框架在启动初始化DispatcherServlet时,在WEB应用下的WEB-INF目录下寻找名为[servlet- name]-servlet.xml的文件,并创建定义的bean,这些bean如果同样在全局作用域中定义(名字相同),则覆盖全局bean.如在上一 例中,则会找WEB-INF/example-servlet.xml文件。当然,配置文件的路径可以通过servlet的参数配置。

       WebApplicationContext是普通的ApplicationContext的扩展,它增加了一些web应用需要的特性。比如,它解析 themes与ApplicationContext是不同的,因为它会关联到ServletContext。 WebApplicationContext绑定到了ServletContext,在你需要访问它时,只需要用RequestContextUtils 类的静态方法就可以找到它。

       DispatcherServlet有几个特殊的bean用来处理request请求和渲染适合的视图。这些bean包含在spring框架中,并可在WebApplicationContext像其他普通bean一样配置。在下面有更具体的介绍。

Bean类型

说明

Controllers

MVC架构的'C'

Handler mappings

主要负责预处理,后置处理等一系列执行处理和满足特定条件下的控制器的执行,比如URL匹配等

View resolvers

负责根据视图名称解析视图

Locale resolver

负责语言本地化

Theme resolver

负责根据应用使用解析主题,比如可以定制个性化版面

multipart file


resolver


负责在html页面提供文件上传功能

Handler exception resolver(s)

负责提供处理异常功能

DispatcherServlet服务启动,一个请求到特定的DispatcherServlet,这时Dispatcher开始处理请求。下面是DispatcherServlet处理请求的完整过程。

1.寻找WebApplicationContext并且在request作为一个属性绑定方便控制器和其他成员使用。默认的绑定KEY是  DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE.

2.本地化解析器绑定到request中。如果没有使用该解析器,则不作任何事情。

3.主题解析器绑定到request中。如果没有使用该解析器,则不做任何事情。

4.如果multipart resolver被指定,则request会检测它。如果找到,request被包装成MultipartHttpServletRequest方便之后的其他成员处理。

5.寻找合适的的处理模块。如果找到,该处理模块相关的执行链如预处理,后置处理和控制器等都会被执行为模块的渲染作准备。

6.如果一个模块返回,则会渲染一个视图。如果没有模块返回,就没有视图被渲染,因为请求可能已经处理完成。

处理请求过程中抛出异常,请求会启用在WebApplicationContext声明的异常处理解析器。在发生异常时,可以使用这些解析器定义用户的行为。

DispatcherServlet也支持返回last-modification-date。DispatcherServlet首先寻找一个合适的处理映射并检测这处理模块是否实现了LastModified接口。如果是,这个接口的方法long getLastModified(request)返回值给客户端。

你也可以定制DispatcherServlet,通过在web.xml或者servlet初始参数上添加上下文容器的参数。具体如下:

参数

说明

contextClass

实现了WebApplicationContext接口的类,用来初始上下文环境。如果没有指定,则默认XmlWebApplicationContext

contextConfigLocation

传递给context实例的字符串,说明哪里可以找到context。这个字符串可被逗号拆分从而支持多个contexts。

namespace

WebApplicationContext命名空间。默认是 [servlet-name]-servlet



现在再查看源码加深理解。

首先服务器如tomcat启动时,就会将web.xml定义的context的bean都会实例化。入口当然是

org.springframework.web.servlet.DispatcherServlet。 它是一个HttpServlet,所以必然会执行init()方法。我们查看根据上述的DispatcherServlet类图查看,发现 HttpServletBean才有init()方法。


  1. public final void init() throws ServletException {  
  2.         if (logger.isDebugEnabled()) {  
  3.             logger.debug("Initializing servlet '" + getServletName() + "'");  
  4.         }  
  5.         // Set bean properties from init parameters.  
  6.         try {  
  7.             PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);  
  8.             BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);  
  9.             ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());  
  10.             bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader));  
  11.             initBeanWrapper(bw);  
  12.             bw.setPropertyValues(pvs, true);  
  13.         }  
  14.         catch (BeansException ex) {  
  15.             logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);  
  16.             throw ex;  
  17.         }  
  18.         // Let subclasses do whatever initialization they like.  
  19.         initServletBean();  
  20.         if (logger.isDebugEnabled()) {  
  21.             logger.debug("Servlet '" + getServletName() + "' configured successfully");  
  22.         }  
  23.     }  

在这个方法里最重要的是initServletBean()。它会调用FrameworkServlet下的initServletBean(),这个方法改写了HttpServletBean的initServletBean()。代码如下:

 
  1. protected final void initServletBean() throws ServletException, BeansException {  
  2.         getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");  
  3.         if (this.logger.isInfoEnabled()) {  
  4.             this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");  
  5.         }  
  6.         long startTime = System.currentTimeMillis();  
  7.         try {  
  8.             this.webApplicationContext = initWebApplicationContext();  
  9.             initFrameworkServlet();  
  10.         }  
  11.         catch (ServletException ex) {  
  12.             this.logger.error("Context initialization failed", ex);  
  13.             throw ex;  
  14.         }  
  15.         catch (BeansException ex) {  
  16.             this.logger.error("Context initialization failed", ex);  
  17.             throw ex;  
  18.         }  
  19.         if (this.logger.isInfoEnabled()) {  
  20.             long elapsedTime = System.currentTimeMillis() - startTime;  
  21.             this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +  
  22.                     elapsedTime + " ms");  
  23.         }  
  24.     }  

我们应该注意到这个方法有修饰符final,是不能子类重写的,这就体现了 “开-闭原则(open for extension,close for modification)”我们再看initWebApplicationContext() 。


  1. protected WebApplicationContext initWebApplicationContext() throws BeansException {  
  2.         WebApplicationContext wac = findWebApplicationContext();  
  3.         if (wac == null) {  
  4.             // No fixed context defined for this servlet - create a local one.  
  5.             WebApplicationContext parent =  
  6.                     WebApplicationContextUtils.getWebApplicationContext(getServletContext());  
  7.             wac = createWebApplicationContext(parent);  
  8.         }  
  9.         if (!this.refreshEventReceived) {  
  10.             // Apparently not a ConfigurableApplicationContext with refresh support:  
  11.             // triggering initial onRefresh manually here.  
  12.             onRefresh(wac);  
  13.         }  
  14.         if (this.publishContext) {  
  15.             // Publish the context as a servlet context attribute.  
  16.             String attrName = getServletContextAttributeName();  
  17.             getServletContext().setAttribute(attrName, wac);  
  18.             if (this.logger.isDebugEnabled()) {  
  19.                 this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +  
  20.                         "' as ServletContext attribute with name [" + attrName + "]");  
  21.             }  
  22.         }  
  23.         return wac;  
  24.     }  

FindWebApplicationContext() 方法是从 ServletContext 属性取 WebApplicationContext ,如果没有配置 ServletContext 属性的话,则返回 null 。这时,先取回根 WebApplicationContext,key 是 WebApplicationContext. ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE 。

 

而 createWebApplicationContext(parent) 方法是真正得到我们 DispatcherServlet 对应的 WebApplicationContext 。

 
  1. protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent)  
  2.             throws BeansException {  
  3.         if (this.logger.isDebugEnabled()) {  
  4.             this.logger.debug("Servlet with name '" + getServletName() +  
  5.                     "' will try to create custom WebApplicationContext context of class '" +  
  6.                     getContextClass().getName() + "'" + ", using parent context [" + parent + "]");  
  7.         }  
  8.         if (!ConfigurableWebApplicationContext.class.isAssignableFrom(getContextClass())) {  
  9.             throw new ApplicationContextException(  
  10.                     "Fatal initialization error in servlet with name '" + getServletName() +  
  11.                     "': custom WebApplicationContext class [" + getContextClass().getName() +  
  12.                     "] is not of type ConfigurableWebApplicationContext");  
  13.         }  
  14.         ConfigurableWebApplicationContext wac =  
  15.                 (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(getContextClass());  
  16.         wac.setParent(parent);  
  17.         wac.setServletContext(getServletContext());  
  18.         wac.setServletConfig(getServletConfig());  
  19.         wac.setNamespace(getNamespace());  
  20.         wac.setConfigLocation(getContextConfigLocation());  
  21.         wac.addApplicationListener(new SourceFilteringListener(wac, this));  
  22.         postProcessWebApplicationContext(wac);  
  23.         wac.refresh();  
  24.         return wac;  
  25.     }  

 

在这个方法里,首先调用 getContextClass() 获取默认的 ContextClass, 就是 public static final Class DEFAULT_CONTEXT_CLASS = XmlWebApplicationContext. class 。

 
  1. ConfigurableWebApplicationContext  wac  =  
  2. (ConfigurableWebApplicationContext) BeanUtils. instantiateClass (getContextClass());   

这行代码就是利用 XmlWebApplicationContext 的无参数构造函数实例化 XmlWebApplicationContext , 因为 XmlWebApplicationContext 实现了 ConfigurableWebApplicationContext 接口。接下来就是填充 XmlWebApplicationContext 的属性,这些属性部分在上面有提及过。这样在这个 WebApplicationContext 就可以使用了。