`
中华国锋
  • 浏览: 41150 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论
收藏列表
标题 标签 来源
Struts1&& Struts2
Struts2与Struts1的对比 

来源:struts2开发组  翻译:tianxinet(胖猴) 


Action 类: 
• Struts1要求Action类继承一个抽象基类。Struts1的一个普遍问题是使用抽象类编程而不是接口。 
• Struts 2 Action类可以实现一个Action接口,也可实现其他接口,使可选和定制的服务成为可能。Struts2提供一个ActionSupport基类去实现 常用的接口。Action接口不是必须的,任何有execute标识的POJO对象都可以用作Struts2的Action对象。 

线程模式: 
• Struts1 Action是单例模式并且必须是线程安全的,因为仅有Action的一个实例来处理所有的请求。单例策略限制了Struts1 Action能作的事,并且要在开发时特别小心。Action资源必须是线程安全的或同步的。 
• Struts2 Action对象为每一个请求产生一个实例,因此没有线程安全问题。(实际上,servlet容器给每个请求产生许多可丢弃的对象,并且不会导致性能和垃圾回收问题) 

Servlet 依赖: 
• Struts1 Action 依赖于Servlet API ,因为当一个Action被调用时HttpServletRequest 和 HttpServletResponse 被传递给execute方法。 
• Struts 2 Action不依赖于容器,允许Action脱离容器单独被测试。如果需要,Struts2 Action仍然可以访问初始的request和response。但是,其他的元素减少或者消除了直接访问HttpServetRequest 和 HttpServletResponse的必要性。 

可测性: 
• 测试Struts1 Action的一个主要问题是execute方法暴露了servlet API(这使得测试要依赖于容器)。一个第三方扩展--Struts TestCase--提供了一套Struts1的模拟对象(来进行测试)。 
• Struts 2 Action可以通过初始化、设置属性、调用方法来测试,“依赖注入”支持也使测试更容易。 

捕获输入: 
• Struts1 使用ActionForm对象捕获输入。所有的ActionForm必须继承一个基类。因为其他JavaBean不能用作ActionForm,开发者经常创建多余的类捕获输入。动态Bean(DynaBeans)可以作为创建传统ActionForm的选择,但是,开发者可能是在重新描述(创建)已经存在的JavaBean(仍然会导致有冗余的javabean)。 
• Struts 2直接使用Action属性作为输入属性,消除了对第二个输入对象的需求。输入属性可能是有自己(子)属性的rich对象类型。Action属性能够通过web页面上的taglibs访问。Struts2也支持ActionForm模式。rich对象类型,包括业务对象,能够用作输入/输出对象。这种ModelDriven 特性简化了taglib对POJO输入对象的引用。 

表达式语言: 
• Struts1 整合了JSTL,因此使用JSTL EL。这种EL有基本对象图遍历,但是对集合和索引属性的支持很弱。 
• Struts2可以使用JSTL,但是也支持一个更强大和灵活的表达式语言--"Object Graph Notation Language" (OGNL).  

绑定值到页面(view): 
• Struts 1使用标准JSP机制把对象绑定到页面中来访问。 
• Struts 2 使用 "ValueStack"技术,使taglib能够访问值而不需要把你的页面(view)和对象绑定起来。ValueStack策略允许通过一系列名称相同但类型不同的属性重用页面(view)。 
  
类型转换: 
• Struts 1 ActionForm 属性通常都是String类型。Struts1使用Commons-Beanutils进行类型转换。每个类一个转换器,对每一个实例来说是不可配置的。 
• Struts2 使用OGNL进行类型转换。提供基本和常用对象的转换器。 

校验:  
• Struts 1支持在ActionForm的validate方法中手动校验,或者通过Commons Validator的扩展来校验。同一个类可以有不同的校验内容,但不能校验子对象。 
• Struts2支持通过validate方法和XWork校验框架来进行校验。XWork校验框架使用为属性类类型定义的校验和内容校验,来支持chain校验子属性  

Action执行的控制: 
• Struts1支持每一个模块有单独的Request Processors(生命周期),但是模块中的所有Action必须共享相同的生命周期。 
• Struts2支持通过拦截器堆栈(Interceptor Stacks)为每一个Action创建不同的生命周期。堆栈能够根据需要和不同的Action一起使用。 
Struts1、Struts2、Spring总结 struts1、struts2、spring总结
一: struts1.x 
A配置: 
1 导入struts1.x的jar包到web-inf/lib下面 
2 在web-inf目录下新建struts-config.xml文件,配置struts相关内容 
3 web.xml配置加入: 
<servlet> 
  <servlet-name>action</servlet-name> 
  <servlet-class>org.apache.struts.action.ActionServlet</servlet-class> 
- <init-param> 
  <param-name>config</param-name> 
  <param-value>/WEB-INF/struts-config.xml</param-value> 
  </init-param> 
- <init-param> 
  <param-name>debug</param-name> 
  <param-value>3</param-value> 
  </init-param> 
- <init-param> 
  <param-name>detail</param-name> 
  <param-value>3</param-value> 
  </init-param> 
  <load-on-startup>0</load-on-startup> 
  </servlet> 
- <servlet-mapping> 
  <servlet-name>action</servlet-name> 
  <url-pattern>*.do</url-pattern> 
  </servlet-mapping> 
配置随容器启动的servlet,并初始化struts的配置文件.设置由struts处理的请求标识 
4 编写action控制类,继承Action[复杂情况继承DispactureAction],同时在struts-config.xml中配置这个控制类action.例如: 
<action path="/stock" parameter="method" name="stocklogForm"   type="com.xuanmu.struts.action.stock.StockAction" scope="request"> 
  <forward name="LIST" path="/productsList.jsp" /> 
</action> 
当截取到请求为stock时就由StockAaction处理.[继承DispactureAction的话分发到parameter值对应的方法中去],该方法返回由字符串封装[LIST]的ActionForward对象,然后再struts-config.xml中的匹配 ,决定返回呈现的页面 

B流程: 
1预备工作:服务器启动,Web应用启动时就会加载web.xml初始化actionServlet和记载struts配置文件(struts-config.xml),读配置信息到内存中,供以后action调用 
2(由请求定位action控制类) 
用户通过客户端向服务器发出一个请求,http://localhost:8080/struts_login/login.do 
我们已经在web.xml配置了所有符合某特定格式的请求都将由struts指定的Servlet来处理。比如:只要是以.do结尾的请求(*.do)都由org.apache.struts.action.ActionServlet来对其进行处理.ActionServlet会拿到用户的请求,并且去分析这个URL,它会截下 /login. 截下来之后,它是为了去struts-config.xml这个配置文件里面找<action>标签path属性的值等于所截部分的那个Action. 
3(页面数据注入) 
找到对应的action之后,ActionServlet会把表单提交的数据给存放(生成对应调用set/get方法)到struts-config中相应的action标签的name属性值指定的actionform类中(若有,[actionform的子类,并且在form-bean标签中配置了,若要进行数据验证可以在actionform中覆盖validate方法,推荐使用js,减轻服务器负担]).同时把actionform和当前HttpServletrequest 对象注入到代调用的action方法中. 
4(调用action的处理方法) 
[默认是execute方法可以通过dispactureAction分发请求给自定义的方法]可以在方法中取得请求附属的数据(若需要),编写业务逻辑代码处理请求. 
5(返回到view层的页面) 
  Action的业务逻辑方法会返回一个ActionForward对象 ,ActionForward对象根据此action配置的<forward>匹配name进而调转到对应path的jsp页面上. 
C特性分析: 
1 线程模式:单例模式,每个action只维护一个实例服务于对应的请求,效率较好,但多线程环境下需要考虑数据同步等安全问题 
2 JSTL标签和struts标签处理页面展示,页面相对整洁 

二:struts2.x 
A配置: 
1 导入struts2.x的相关jar包到WEB-INF/lib下 
2 在项目的src目录下新建struts.properties或struts.xml 
struts.properties和struts.xml具有相同的配置作用,配置项目相关 
3 web.xml加入: 
    <!-- 延长action中属性的生命周期,以便在 jsp页面中进行访问,让actionContextcleanup过滤器来清除属性,不让action自己清除。--> 
    <filter> 
        <filter-name>ActionContextCleanUp</filter-name> 
        <filter-class>com.opensymphony.webwork.dispatcher.ActionContextCleanUp</filter-class> 
    </filter> 

<!-- 若有其他相关的filter 需要加在这个地方--> 

<!--配置struts2过滤器--> 
    <filter> 
        <filter-name>webwork</filter-name> 
        <filter-class>com.opensymphony.webwork.dispatcher.FilterDispatcher</filter-class> 
    </filter> 

    <filter-mapping> 
        <filter-name>ActionContextCleanUp</filter-name> 
        <url-pattern>/*</url-pattern> 
    </filter-mapping> 

    <filter-mapping> 
        <filter-name>webwork</filter-name> 
        <url-pattern>/*</url-pattern> 
    </filter-mapping> 

4 编写action请求处理类.可以继承struts2的ActionSupport也可以不继承,(不过一般为了方便获取request,response以及session 最好自己编写一个类继承ActionSupport,并在类中获取request等,让请求处理类继承这个类),然后根据页面和struts.xml配置决定请求的处理方法. 
例1: 
------页面 
#  <s:form action="login.action" method="post">  
#         <s:textfield key="username" name="user.username"/>  
#         <s:password key="password" name="user.password" />  
#         <s:submit key="submit" />  
#   </s:form> 
------struts.xml 
#     <action name="login" class="cc.dynasoft.action.LoginAction">  
#             <result name="success">/welcome.jspresult>  
#             <result name="error">/login.jspresult>  
#     </action>  
这时请求会交由LoginAction的默认方法execute来处理,在LoginAaction中有user对象及其set方法,页面属性会自动注入到此user对象中,返回字符串[success],匹配struts.xml此action内的result值确定调转的展示页面 
例2: 
-----页面 
<s:form action="submit.action"> 
<s:textfield name="msg"label="输入内容"/>  
<s:submit name="save"value="保存"align="left"method="save"/> 
<s:submit name="print"value="打印"align="left"method="print"/>  
</s:form> 
-----struts.xml: 
<action name="submit" class="action.MoreSubmitAction"> 
<result name="save">/result.jsp</result> 
<result name="print">/result.jsp</result> 
</action> 
由页面submit的method方法来指定对应的处理方法,方法返回字符串.继而确定调转展示页面 

例3: 
-----设置自定义的url后缀,有struts2处理例如struts.action.extension=php.默认为action,即处理所有*.action的请求 
-----页面 
<form  method="post" action="Kinder_add.php" 
  <input type="text" name="kinder.name"> 
  <input  type="text"name="kinder.zip"> 
  <input type="submit" value="提交"> 
</form> 
-----strust.xml 
- <action name="*_*" method="{2}" class="ostar.lg.action.{1}Action"> 
  <result name="success">/default.jsp</result> 
  </action> 
这样的意思是:所有由'_'间隔的请求都会有'_'前面的字符串同名的action类处理,并交给这个类的和'_'后面字符串同名的方法处理[简化了配置],方法返回字符串继而确定调转展示的页面.(*_*只是一种自定义格式,可任意配置) 
B 流程: 
1、客户端初始化一个对服务器的请求(tomcat); 
2、这个请求经过一系列的过滤器(Filter)(这些过滤器中有一个叫做ActionContextCleanUp的可选过滤器,这个过滤器对于 Struts2和其他框架的集成很有帮助,例如:SiteMesh Plugin); 
3、接着FilterDispatcher被调用,FilterDispatcher询问ActionMapper来决定这个请求是否需要调用某个 Action; 
4、如果ActionMapper决定需要调用某个Action,FilterDispatcher把请求的处理交给ActionProxy; 
5、ActionProxy通过Configuration Manager询问框架的配置文件,找到需要调用的Action类; 
6、ActionProxy创建一个ActionInvocation的实例。 
7、ActionInvocation实例使用命名模式来调用,在调用Action的过程前后,涉及到相关拦截器(Intercepter)的调用。 
8、一旦Action执行完毕,ActionInvocation负责根据struts.xml中的配置找到对应的返回结果。返回结果通常是(但不总是,也可能是另外的一个Action链)一个需要被表示的JSP或者FreeMarker的模版。 
C特性分析: 
1 线程模式,每一次请求都会产生一个新的实例处理请求. 多线程环境没有数据同步问题. 
2 引入数据的依赖注入 
  [页面表单数据到action的注入,实例对象的注入,都是通过set注入,需要提供set方法] 
3 基于AOP的拦截器,可以在每次请求前后灵活控制 
4 配置文件支持表达式,基于约定优于配置的话可简化配置文件 
4 内置以插件形式支持ajax如dojo,支持多种模板展示jsp、freemarker,Velocity等 

三 springMvc: 
A(2.5版+spring注解)配置: 
1 复制spring相关jar包到web-inf/lib里面 
2 web.xml新增如下: 
<!--   Spring 容器启动监听器 --> 
<listener> 
  <listener-class> 
    org.springframework.web.context.ContextLoaderListener 
  </listener-class> 
</listener> 

<!--   Spring MVC 的 Servlet ,它将加载 WEB-INF/annomvc-servlet.xml 的配置文件,以启动 Spring MVC 模块 
  --> 
- <servlet> 
  <servlet-name>annomvc</servlet-name> 
  <servlet-class> 
     org.springframework.web.servlet.DispatcherServlet 
  </servlet-class> 
  <load-on-startup>1</load-on-startup> 
  </servlet> 
- <servlet-mapping> 
  <servlet-name>annomvc</servlet-name> 
  <url-pattern>*.do</url-pattern> 
  </servlet-mapping> 
[这里spring回根据servlet-name的值(annomvc)去加载(annomvc).servlet.xml] 

注解1: 
在web-inf/新建annomvc-servlet.xml里面新增如下: 

<!--:对 web 包中的所有类进行扫描,以完成 Bean 创建和自动依赖注入的功能 --> 
  <context:component-scan base-package="com.controller" /> 
这样就会自动扫描controller包下面的所有类 

<!--:对模型视图名称的解析,即在模型视图名称添加前后缀  --> 
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" p:prefix="/WEB-INF/test/" p:suffix=".jsp" /> 

3 这样你在com.controller下面新建一类Test类 
@Controller 
@RequestMapping("/test.do") 
public class Test{ 
    @RequestMapping(params="method=show") 
public String show(User user,ModelMap model){ 
System.out.println(“calling...”); 

} 
} 
这样当页面有请求,例如action="test.do?method=show"时就会调用Test类中的show方法. 是不是简化了很多.. 

注意:需要使用spring注解时还需要在配置文件中加上相应的xml的schema验证 

B流程: 
spring mvc请所有的请求都提交给在web.xml中配置的DispatcherServlet,由它查询一个或多个HandlerMapping,找到处理请求的Controller,并且将请求提交给这个类。 
Controller进行业务逻辑处理后,会返回一个ModelAndView 
Dispathcher查询一个或多个 ViewResolver视图解析器,找到ModelAndView对象指定的视图对象 ,视图对象负责渲染返回给客户端。 
C 特性: 
1 线程模式:单例模式,效率较好. 
2 基于方法级别的拦截,而不是基于类。更细粒度的灵活控制 
3 通过注解简化了很多配置,但同时在代码中增加了和业务不相干的代码. 
[并不是所有项目都适合通过注解,虽然可以简化繁琐的配置,但是对于项目经常需要改动时,有可能需要更改代码并重新编译后才能使之生效,而配置文件的更改可以自动服务器发现,及时更新] 

总结: 
这里可看出这几个框架大致有哪些异同,可以根据项目来适当选择.个人感觉springmvc 这的不错,struts2 回归从单例模式回归.每次都是新的实例.+ 项目拦截器存在过多等问题,感觉效率不是很好. springmvc 则提供的最简化的请求处理映射,能极大提高项目效率. 
【曾看到过一个做测试的哥们说,做并发压力测试,struts2每秒到达300左右,springmvc到达了2000/s.】 

Strust2工作机制及代码解析 strust2工作机制及代码解析
 1.请求首先通过Filter chain,Filter主要包括ActionContextCleanUp,它主要清理当前线程的ActionContext和Dispatcher;FilterDispatcher主要通过AcionMapper来决定需要调用哪个Action。 
        ActionMapper取得了ActionMapping后,在Dispatcher的serviceAction方法里创建ActionProxy,ActionProxy创建ActionInvocation,然后ActionInvocation调用Interceptors,执行Action本身,创建Result并返回,当然,如果要在返回之前做些什么,可以实现PreResultListener。 

2. Struts2部分类介绍 
这部分从Struts2参考文档中翻译就可以了。 
ActionMapper 
        ActionMapper其实是HttpServletRequest和Action调用请求的一个映射,它屏蔽了Action对于Request等java Servlet类的依赖。Struts2中它的默认实现类是DefaultActionMapper,ActionMapper很大的用处可以根据自己的需要来设计url格式,它自己也有Restful的实现,具体可以参考文档的docs\actionmapper.html。 
ActionProxy&ActionInvocation 
        Action的一个代理,由ActionProxyFactory创建,它本身不包括Action实例,默认实现DefaultActionProxy是由ActionInvocation持有Action实例。ActionProxy作用是如何取得Action,无论是本地还是远程。而ActionInvocation的作用是如何执行Action,拦截器的功能就是在ActionInvocation中实现的。 
ConfigurationProvider&Configuration 
        ConfigurationProvider就是Struts2中配置文件的解析器,Struts2中的配置文件主要是尤其实现类XmlConfigurationProvider及其子类StrutsXmlConfigurationProvider来解析。 

3. Struts2请求流程 
1、客户端发送请求 
2、请求先通过ActionContextCleanUp-->FilterDispatcher 
3、FilterDispatcher通过ActionMapper来决定这个Request需要调用哪个Action 
4、如果ActionMapper决定调用某个Action,FilterDispatcher把请求的处理交给ActionProxy,这儿已经转到它的Delegate--Dispatcher来执行 
5、ActionProxy根据ActionMapping和ConfigurationManager找到需要调用的Action类 
6、ActionProxy创建一个ActionInvocation的实例 
7、ActionInvocation调用真正的Action,当然这涉及到相关拦截器的调用 
8、Action执行完毕,ActionInvocation创建Result并返回,当然,如果要在返回之前做些什么,可以实现PreResultListener。添加PreResultListener可以在Interceptor中实现,不知道其它还有什么方式? 



   发表时间:2009-08-16   最后修改:2009-08-16 
收藏  
< > 猎头职位: 上海: 上海:天会皓闻诚聘资深Java架构师 
相关文章:   
•	Xwork2 源码阅读(三)
•	探究Struts2运行机制:StrutsPrepareAndExecuteFilter 源码剖析
•	Xwork2 源码阅读(二)

推荐圈子: struts2 
更多相关推荐 
1. Struts2架构图 
 
        请求首先通过Filter chain,Filter主要包括ActionContextCleanUp,它主要清理当前线程的ActionContext和Dispatcher;FilterDispatcher主要通过AcionMapper来决定需要调用哪个Action。 
        ActionMapper取得了ActionMapping后,在Dispatcher的serviceAction方法里创建ActionProxy,ActionProxy创建ActionInvocation,然后ActionInvocation调用Interceptors,执行Action本身,创建Result并返回,当然,如果要在返回之前做些什么,可以实现PreResultListener。 

2. Struts2部分类介绍 
这部分从Struts2参考文档中翻译就可以了。 
ActionMapper 
        ActionMapper其实是HttpServletRequest和Action调用请求的一个映射,它屏蔽了Action对于Request等java Servlet类的依赖。Struts2中它的默认实现类是DefaultActionMapper,ActionMapper很大的用处可以根据自己的需要来设计url格式,它自己也有Restful的实现,具体可以参考文档的docs\actionmapper.html。 
ActionProxy&ActionInvocation 
        Action的一个代理,由ActionProxyFactory创建,它本身不包括Action实例,默认实现DefaultActionProxy是由ActionInvocation持有Action实例。ActionProxy作用是如何取得Action,无论是本地还是远程。而ActionInvocation的作用是如何执行Action,拦截器的功能就是在ActionInvocation中实现的。 
ConfigurationProvider&Configuration 
        ConfigurationProvider就是Struts2中配置文件的解析器,Struts2中的配置文件主要是尤其实现类XmlConfigurationProvider及其子类StrutsXmlConfigurationProvider来解析。 

3. Struts2请求流程 
1、客户端发送请求 
2、请求先通过ActionContextCleanUp-->FilterDispatcher 
3、FilterDispatcher通过ActionMapper来决定这个Request需要调用哪个Action 
4、如果ActionMapper决定调用某个Action,FilterDispatcher把请求的处理交给ActionProxy,这儿已经转到它的Delegate--Dispatcher来执行 
5、ActionProxy根据ActionMapping和ConfigurationManager找到需要调用的Action类 
6、ActionProxy创建一个ActionInvocation的实例 
7、ActionInvocation调用真正的Action,当然这涉及到相关拦截器的调用 
8、Action执行完毕,ActionInvocation创建Result并返回,当然,如果要在返回之前做些什么,可以实现PreResultListener。添加PreResultListener可以在Interceptor中实现,不知道其它还有什么方式? 

4. Struts2(2.1.2)部分源码阅读 
    从org.apache.struts2.dispatcher.FilterDispatcher开始 
Java代码     
1.	//创建Dispatcher,此类是一个Delegate,它是真正完成根据url解析,读取对应Action的地方   
2.	public void init(FilterConfig filterConfig) throws ServletException {   
3.	    try {   
4.	        this.filterConfig = filterConfig;   
5.	  
6.	        initLogging();   
7.	  
8.	        dispatcher = createDispatcher(filterConfig);   
9.	        dispatcher.init();   
10.	        dispatcher.getContainer().inject(this);   
11.	        //读取初始参数pakages,调用parse(),解析成类似/org/apache/struts2/static,/template的数组   
12.	        String param = filterConfig.getInitParameter("packages");   
13.	        String packages = "org.apache.struts2.static template org.apache.struts2.interceptor.debugging";   
14.	        if (param != null) {   
15.	            packages = param + " " + packages;   
16.	        }   
17.	        this.pathPrefixes = parse(packages);   
18.	    } finally {   
19.	        ActionContext.setContext(null);   
20.	    }   
21.	}  

     顺着流程我们看Disptcher的init方法。init方法里就是初始读取一些配置文件等,先看init_DefaultProperties,主要是读取properties配置文件。 
Java代码     
1.	private void init_DefaultProperties() {   
2.	    configurationManager.addConfigurationProvider(new DefaultPropertiesProvider());   
3.	}  

    打开DefaultPropertiesProvider 
Java代码     
1.	public void register(ContainerBuilder builder, LocatableProperties props)   
2.	         throws ConfigurationException {   
3.	        
4.	     Settings defaultSettings = null;   
5.	     try {   
6.	         defaultSettings = new PropertiesSettings("org/apache/struts2/default");   
7.	     } catch (Exception e) {   
8.	         throw new ConfigurationException("Could not find or error in org/apache/struts2/default.properties", e);   
9.	     }   
10.	        
11.	     loadSettings(props, defaultSettings);   
12.	 }   
13.	  
14.	 //PropertiesSettings   
15.	 //读取org/apache/struts2/default.properties的配置信息,如果项目中需要覆盖,可以在classpath里的struts.properties里覆写   
16.	 public PropertiesSettings(String name) {   
17.	        
18.	     URL settingsUrl = ClassLoaderUtils.getResource(name + ".properties", getClass());   
19.	        
20.	     if (settingsUrl == null) {   
21.	         LOG.debug(name + ".properties missing");   
22.	         settings = new LocatableProperties();   
23.	         return;   
24.	     }   
25.	        
26.	     settings = new LocatableProperties(new LocationImpl(null, settingsUrl.toString()));   
27.	  
28.	     // Load settings   
29.	     InputStream in = null;   
30.	     try {   
31.	         in = settingsUrl.openStream();   
32.	         settings.load(in);   
33.	     } catch (IOException e) {   
34.	         throw new StrutsException("Could not load " + name + ".properties:" + e, e);   
35.	     } finally {   
36.	         if(in != null) {   
37.	             try {   
38.	                 in.close();   
39.	             } catch(IOException io) {   
40.	                 LOG.warn("Unable to close input stream", io);   
41.	             }   
42.	         }   
43.	     }   
44.	 }  

    再来看init_TraditionalXmlConfigurations方法,这个是读取struts-default.xml和Struts.xml的方法。 
Java代码     
1.	private void init_TraditionalXmlConfigurations() {   
2.	    //首先读取web.xml中的config初始参数值   
3.	    //如果没有配置就使用默认的"struts-default.xml,struts-plugin.xml,struts.xml",   
4.	    //这儿就可以看出为什么默认的配置文件必须取名为这三个名称了   
5.	    //如果不想使用默认的名称,直接在web.xml中配置config初始参数即可   
6.	    String configPaths = initParams.get("config");   
7.	    if (configPaths == null) {   
8.	        configPaths = DEFAULT_CONFIGURATION_PATHS;   
9.	    }   
10.	    String[] files = configPaths.split("\\s*[,]\\s*");   
11.	    //依次解析配置文件,xwork.xml单独解析   
12.	    for (String file : files) {   
13.	        if (file.endsWith(".xml")) {   
14.	            if ("xwork.xml".equals(file)) {   
15.	                configurationManager.addConfigurationProvider(new XmlConfigurationProvider(file, false));   
16.	            } else {   
17.	                configurationManager.addConfigurationProvider(new StrutsXmlConfigurationProvider(file, false, servletContext));   
18.	            }   
19.	        } else {   
20.	            throw new IllegalArgumentException("Invalid configuration file name");   
21.	        }   
22.	    }   
23.	}  

    对于其它配置文件只用StrutsXmlConfigurationProvider,此类继承XmlConfigurationProvider,而XmlConfigurationProvider又实现ConfigurationProvider接口。类XmlConfigurationProvider负责配置文件的读取和解析,addAction()方法负责读取<action>标签,并将数据保存在ActionConfig中;addResultTypes()方法负责将<result-type>标签转化为ResultTypeConfig对象;loadInterceptors()方法负责将<interceptor>标签转化为InterceptorConfi对象;loadInterceptorStack()方法负责将<interceptor-ref>标签转化为InterceptorStackConfig对象;loadInterceptorStacks()方法负责将<interceptor-stack>标签转化成InterceptorStackConfig对象。而上面的方法最终会被addPackage()方法调用,将所读取到的数据汇集到PackageConfig对象中。来看XmlConfigurationProvider的源代码,详细的我自己也就大体浏览了一下,各位可以自己研读。 
Java代码     
1.	protected PackageConfig addPackage(Element packageElement) throws ConfigurationException {   
2.	    PackageConfig.Builder newPackage = buildPackageContext(packageElement);   
3.	  
4.	    if (newPackage.isNeedsRefresh()) {   
5.	        return newPackage.build();   
6.	    }   
7.	    .   
8.	  
9.	    addResultTypes(newPackage, packageElement);   
10.	    loadInterceptors(newPackage, packageElement);   
11.	    loadDefaultInterceptorRef(newPackage, packageElement);   
12.	    loadDefaultClassRef(newPackage, packageElement);   
13.	    loadGlobalResults(newPackage, packageElement);   
14.	    loadGobalExceptionMappings(newPackage, packageElement);   
15.	    NodeList actionList = packageElement.getElementsByTagName("action");   
16.	  
17.	    for (int i = 0; i < actionList.getLength(); i++) {   
18.	        Element actionElement = (Element) actionList.item(i);   
19.	        addAction(actionElement, newPackage);   
20.	    }   
21.	    loadDefaultActionRef(newPackage, packageElement);   
22.	    PackageConfig cfg = newPackage.build();   
23.	    configuration.addPackageConfig(cfg.getName(), cfg);   
24.	    return cfg;   
25.	}  

    这儿发现一个配置上的小技巧,我的xwork2.0.*是没有的,但是看源码是看到xwork2.1.*是可以的。继续看XmlConfigurationProvider的源代码: 
Java代码     
1.	private List loadConfigurationFiles(String fileName, Element includeElement) {   
2.	    List<Document> docs = new ArrayList<Document>();   
3.	    if (!includedFileNames.contains(fileName)) {   
4.	               
5.	            Element rootElement = doc.getDocumentElement();   
6.	            NodeList children = rootElement.getChildNodes();   
7.	            int childSize = children.getLength();   
8.	  
9.	            for (int i = 0; i < childSize; i++) {   
10.	                Node childNode = children.item(i);   
11.	  
12.	                if (childNode instanceof Element) {   
13.	                    Element child = (Element) childNode;   
14.	  
15.	                    final String nodeName = child.getNodeName();   
16.	                    //解析每个action配置是,对于include文件可以使用通配符*来进行配置   
17.	                    //如Struts.xml中可配置成<include file="actions_*.xml"/>   
18.	                    if (nodeName.equals("include")) {   
19.	                        String includeFileName = child.getAttribute("file");   
20.	                        if(includeFileName.indexOf('*') != -1 ) {   
21.	                            ClassPathFinder wildcardFinder = new ClassPathFinder();   
22.	                            wildcardFinder.setPattern(includeFileName);   
23.	                            Vector<String> wildcardMatches = wildcardFinder.findMatches();   
24.	                            for (String match : wildcardMatches) {   
25.	                                docs.addAll(loadConfigurationFiles(match, child));   
26.	                            }   
27.	                        }   
28.	                        else {   
29.	                               
30.	                            docs.addAll(loadConfigurationFiles(includeFileName, child));       
31.	                        }       
32.	                }   
33.	            }   
34.	            }   
35.	            docs.add(doc);   
36.	            loadedFileUrls.add(url.toString());   
37.	        }   
38.	    }   
39.	    return docs;   
40.	}  
声明:JavaEye文章版权属于作者,受法律保护。没有作者书面许可不得转载。 
推荐链接 
•	【推荐】年薪15-20万急聘java讲师"
•	创新工场第二期“助跑计划”首批名单出炉

          


   发表时间:2009-08-16   
收藏  
    init_CustomConfigurationProviders方式初始自定义的Provider,配置类全名和实现ConfigurationProvider接口,用逗号隔开即可。 
Java代码     
1.	private void init_CustomConfigurationProviders() {   
2.	    String configProvs = initParams.get("configProviders");   
3.	    if (configProvs != null) {   
4.	        String[] classes = configProvs.split("\\s*[,]\\s*");   
5.	        for (String cname : classes) {   
6.	            try {   
7.	                Class cls = ClassLoaderUtils.loadClass(cname, this.getClass());   
8.	                ConfigurationProvider prov = (ConfigurationProvider)cls.newInstance();   
9.	                configurationManager.addConfigurationProvider(prov);   
10.	            }   
11.	               
12.	        }   
13.	    }   
14.	}  

   好了,现在再回到FilterDispatcher,每次发送一个Request,FilterDispatcher都会调用doFilter方法。 
Java代码     
1.	public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {   
2.	  
3.	    HttpServletRequest request = (HttpServletRequest) req;   
4.	    HttpServletResponse response = (HttpServletResponse) res;   
5.	    ServletContext servletContext = getServletContext();   
6.	  
7.	    String timerKey = "FilterDispatcher_doFilter: ";   
8.	    try {   
9.	        ValueStack stack = dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack();   
10.	        ActionContext ctx = new ActionContext(stack.getContext());   
11.	        ActionContext.setContext(ctx);   
12.	           
13.	        UtilTimerStack.push(timerKey);   
14.	        //根据content type来使用不同的Request封装,可以参见Dispatcher的wrapRequest   
15.	        request = prepareDispatcherAndWrapRequest(request, response);   
16.	        ActionMapping mapping;   
17.	        try {   
18.	            //根据url取得对应的Action的配置信息--ActionMapping,actionMapper是通过Container的inject注入的   
19.	            mapping = actionMapper.getMapping(request, dispatcher.getConfigurationManager());   
20.	        } catch (Exception ex) {   
21.	            log.error("error getting ActionMapping", ex);   
22.	            dispatcher.sendError(request, response, servletContext, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex);   
23.	            return;   
24.	        }   
25.	        //如果找不到对应的action配置,则直接返回。比如你输入***.jsp等等   
26.	        //这儿有个例外,就是如果path是以“/struts”开头,则到初始参数packages配置的包路径去查找对应的静态资源并输出到页面流中,当然.class文件除外。如果再没有则跳转到404   
27.	        if (mapping == null) {   
28.	            // there is no action in this request, should we look for a static resource?   
29.	            String resourcePath = RequestUtils.getServletPath(request);   
30.	  
31.	            if ("".equals(resourcePath) && null != request.getPathInfo()) {   
32.	                resourcePath = request.getPathInfo();   
33.	            }   
34.	  
35.	            if (serveStatic && resourcePath.startsWith("/struts")) {   
36.	                String name = resourcePath.substring("/struts".length());   
37.	                findStaticResource(name, request, response);   
38.	            } else {   
39.	                chain.doFilter(request, response);   
40.	            }   
41.	            return;   
42.	        }   
43.	        //正式开始Action的方法了   
44.	        dispatcher.serviceAction(request, response, servletContext, mapping);   
45.	  
46.	    } finally {   
47.	        try {   
48.	            ActionContextCleanUp.cleanUp(req);   
49.	        } finally {   
50.	            UtilTimerStack.pop(timerKey);   
51.	        }   
52.	    }   
53.	}  

    Dispatcher类的serviceAction方法: 
Java代码     
1.	public void serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context,ActionMapping mapping) throws ServletException {   
2.	  
3.	    Map<String, Object> extraContext = createContextMap(request, response, mapping, context);   
4.	  
5.	    // If there was a previous value stack, then create a new copy and pass it in to be used by the new Action   
6.	    ValueStack stack = (ValueStack) request.getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);   
7.	    if (stack != null) {   
8.	        extraContext.put(ActionContext.VALUE_STACK, valueStackFactory.createValueStack(stack));   
9.	    }   
10.	  
11.	    String timerKey = "Handling request from Dispatcher";   
12.	    try {   
13.	        UtilTimerStack.push(timerKey);   
14.	        String namespace = mapping.getNamespace();   
15.	        String name = mapping.getName();   
16.	        String method = mapping.getMethod();   
17.	  
18.	        Configuration config = configurationManager.getConfiguration();   
19.	        ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy(   
20.	                namespace, name, method, extraContext, true, false);   
21.	  
22.	        request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack());   
23.	  
24.	        // if the ActionMapping says to go straight to a result, do it!   
25.	        if (mapping.getResult() != null) {   
26.	            Result result = mapping.getResult();   
27.	            result.execute(proxy.getInvocation());   
28.	        } else {   
29.	            proxy.execute();   
30.	        }   
31.	  
32.	        // If there was a previous value stack then set it back onto the request   
33.	        if (stack != null) {   
34.	            request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, stack);   
35.	        }   
36.	    } catch (ConfigurationException e) {   
37.	        LOG.error("Could not find action or result", e);   
38.	        sendError(request, response, context, HttpServletResponse.SC_NOT_FOUND, e);   
39.	    } catch (Exception e) {   
40.	        sendError(request, response, context, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e);   
41.	    } finally {   
42.	        UtilTimerStack.pop(timerKey);   
43.	    }   
44.	}  

    第一句createContextMap()方法,该方法主要把Application、Session、Request的key value值拷贝到Map中,并放在HashMap<String,Object>中,可以参见createContextMap方法: 
Java代码     
1.	public Map<String,Object> createContextMap(HttpServletRequest request, HttpServletResponse response,   
2.	        ActionMapping mapping, ServletContext context) {   
3.	  
4.	    // request map wrapping the http request objects   
5.	    Map requestMap = new RequestMap(request);   
6.	    // parameters map wrapping the http parameters.  ActionMapping parameters are now handled and applied separately   
7.	    Map params = new HashMap(request.getParameterMap());   
8.	    // session map wrapping the http session   
9.	    Map session = new SessionMap(request);   
10.	    // application map wrapping the ServletContext   
11.	    Map application = new ApplicationMap(context);   
12.	    Map<String,Object> extraContext = createContextMap(requestMap, params, session, application, request, response, context);   
13.	    extraContext.put(ServletActionContext.ACTION_MAPPING, mapping);   
14.	    return extraContext;   
15.	}  

    后面才是最主要的--ActionProxy,ActionInvocation。ActionProxy是Action的一个代理类,也就是说Action的调用是通过ActionProxy实现的,其实就是调用了ActionProxy.execute()方法,而该方法又调用了ActionInvocation.invoke()方法。归根到底,最后调用的是DefaultActionInvocation.invokeAction()方法。先看DefaultActionInvocation的init方法。 
Java代码     
1.	public void init(ActionProxy proxy) {   
2.	    this.proxy = proxy;   
3.	    Map contextMap = createContextMap();   
4.	  
5.	    // Setting this so that other classes, like object factories, can use the ActionProxy and other   
6.	    // contextual information to operate   
7.	    ActionContext actionContext = ActionContext.getContext();   
8.	  
9.	    if(actionContext != null) {   
10.	        actionContext.setActionInvocation(this);   
11.	    }   
12.	    //创建Action,可Struts2里是每次请求都新建一个Action   
13.	    createAction(contextMap);   
14.	  
15.	    if (pushAction) {   
16.	        stack.push(action);   
17.	        contextMap.put("action", action);   
18.	    }   
19.	  
20.	    invocationContext = new ActionContext(contextMap);   
21.	    invocationContext.setName(proxy.getActionName());   
22.	  
23.	    // get a new List so we don't get problems with the iterator if someone changes the list   
24.	    List interceptorList = new ArrayList(proxy.getConfig().getInterceptors());   
25.	    interceptors = interceptorList.iterator();   
26.	}   
27.	  
28.	protected void createAction(Map contextMap) {   
29.	    // load action   
30.	    String timerKey = "actionCreate: "+proxy.getActionName();   
31.	    try {   
32.	        UtilTimerStack.push(timerKey);   
33.	        //这儿默认建立Action是StrutsObjectFactory,实际中我使用的时候都是使用Spring创建的Action,这个时候使用的是SpringObjectFactory   
34.	        action = objectFactory.buildAction(proxy.getActionName(), proxy.getNamespace(), proxy.getConfig(), contextMap);   
35.	    }    
36.	    ..   
37.	    } finally {   
38.	        UtilTimerStack.pop(timerKey);   
39.	    }   
40.	  
41.	    if (actionEventListener != null) {   
42.	        action = actionEventListener.prepare(action, stack);   
43.	    }   
44.	}  

    接下来看看DefaultActionInvocation 的invoke方法。 
Java代码     
1.	 public void init(ActionProxy proxy) {   
2.	     this.proxy = proxy;   
3.	     Map contextMap = createContextMap();   
4.	  
5.	     // Setting this so that other classes, like object factories, can use the ActionProxy and other   
6.	     // contextual information to operate   
7.	     ActionContext actionContext = ActionContext.getContext();   
8.	  
9.	     if(actionContext != null) {   
10.	         actionContext.setActionInvocation(this);   
11.	     }   
12.	     //创建Action,可Struts2里是每次请求都新建一个Action   
13.	     createAction(contextMap);   
14.	  
15.	     if (pushAction) {   
16.	         stack.push(action);   
17.	         contextMap.put("action", action);   
18.	     }   
19.	  
20.	     invocationContext = new ActionContext(contextMap);   
21.	     invocationContext.setName(proxy.getActionName());   
22.	  
23.	     // get a new List so we don't get problems with the iterator if someone changes the list   
24.	     List interceptorList = new ArrayList(proxy.getConfig().getInterceptors());   
25.	     interceptors = interceptorList.iterator();   
26.	 }   
27.	  
28.	 protected void createAction(Map contextMap) {   
29.	     // load action   
30.	     String timerKey = "actionCreate: "+proxy.getActionName();   
31.	     try {   
32.	         UtilTimerStack.push(timerKey);   
33.	         //这儿默认建立Action是StrutsObjectFactory,实际中我使用的时候都是使用Spring创建的Action,这个时候使用的是SpringObjectFactory   
34.	         action = objectFactory.buildAction(proxy.getActionName(), proxy.getNamespace(), proxy.getConfig(), contextMap);   
35.	     }    
36.	     ..   
37.	     } finally {   
38.	         UtilTimerStack.pop(timerKey);   
39.	     }   
40.	  
41.	     if (actionEventListener != null) {   
42.	         action = actionEventListener.prepare(action, stack);   
43.	     }   
44.	 }   
45.	  
46.	 //接下来看看DefaultActionInvocation 的invoke方法。    
47.	public String invoke() throws Exception {   
48.	     String profileKey = "invoke: ";   
49.	     try {   
50.	         UtilTimerStack.push(profileKey);   
51.	            
52.	         if (executed) {   
53.	             throw new IllegalStateException("Action has already executed");   
54.	         }   
55.	             //先执行interceptors   
56.	         if (interceptors.hasNext()) {   
57.	             final InterceptorMapping interceptor = (InterceptorMapping) interceptors.next();   
58.	             UtilTimerStack.profile("interceptor: "+interceptor.getName(),    
59.	                     new UtilTimerStack.ProfilingBlock<String>() {   
60.	                         public String doProfiling() throws Exception {   
61.	                             resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);   
62.	                             return null;   
63.	                         }   
64.	             });   
65.	         } else {   
66.	                     //interceptor执行完了之后执行action   
67.	             resultCode = invokeActionOnly();   
68.	         }   
69.	  
70.	         // this is needed because the result will be executed, then control will return to the Interceptor, which will   
71.	         // return above and flow through again   
72.	         if (!executed) {   
73.	                     //在Result返回之前调用preResultListeners   
74.	             if (preResultListeners != null) {   
75.	                 for (Iterator iterator = preResultListeners.iterator();   
76.	                     iterator.hasNext();) {   
77.	                     PreResultListener listener = (PreResultListener) iterator.next();   
78.	                        
79.	                     String _profileKey="preResultListener: ";   
80.	                     try {   
81.	                         UtilTimerStack.push(_profileKey);   
82.	                         listener.beforeResult(this, resultCode);   
83.	                     }   
84.	                     finally {   
85.	                         UtilTimerStack.pop(_profileKey);   
86.	                     }   
87.	                 }   
88.	             }   
89.	  
90.	             // now execute the result, if we're supposed to   
91.	             if (proxy.getExecuteResult()) {   
92.	                 executeResult();   
93.	             }   
94.	  
95.	             executed = true;   
96.	         }   
97.	  
98.	         return resultCode;   
99.	     }   
100.	     finally {   
101.	         UtilTimerStack.pop(profileKey);   
102.	     }   
103.	 }  


          回帖地址 
0 0 已投票


   发表时间:2009-08-16   
收藏  
    看程序中的if(interceptors.hasNext())语句,当然,interceptors里存储的是interceptorMapping列表(它包括一个Interceptor和一个name),所有的截拦器必须实现Interceptor的intercept方法,而该方法的参数恰恰又是ActionInvocation,在intercept方法中还是调用invocation.invoke(),从而实现了一个Interceptor链的调用。当所有的Interceptor执行完,最后调用invokeActionOnly方法来执行Action相应的方法。 
Java代码     
1.	protected String invokeAction(Object action, ActionConfig actionConfig) throws Exception {   
2.	    String methodName = proxy.getMethod();   
3.	    String timerKey = "invokeAction: "+proxy.getActionName();   
4.	    try {   
5.	        UtilTimerStack.push(timerKey);   
6.	           
7.	        boolean methodCalled = false;   
8.	        Object methodResult = null;   
9.	        Method method = null;   
10.	        try {   
11.	            //获得需要执行的方法   
12.	            method = getAction().getClass().getMethod(methodName, new Class[0]);   
13.	        } catch (NoSuchMethodException e) {   
14.	            //如果没有对应的方法,则使用do+Xxxx来再次获得方法   
15.	            try {   
16.	                String altMethodName = "do" + methodName.substring(0, 1).toUpperCase() + methodName.substring(1);   
17.	                method = getAction().getClass().getMethod(altMethodName, new Class[0]);   
18.	            } catch (NoSuchMethodException e1) {   
19.	                // well, give the unknown handler a shot   
20.	                if (unknownHandler != null) {   
21.	                    try {   
22.	                        methodResult = unknownHandler.handleUnknownActionMethod(action, methodName);   
23.	                        methodCalled = true;   
24.	                    } catch (NoSuchMethodException e2) {   
25.	                        // throw the original one   
26.	                        throw e;   
27.	                    }   
28.	                } else {   
29.	                    throw e;   
30.	                }   
31.	            }   
32.	        }   
33.	           
34.	        if (!methodCalled) {   
35.	            methodResult = method.invoke(action, new Object[0]);   
36.	        }   
37.	        //根据不同的Result类型返回不同值   
38.	        //如输出流Result   
39.	        if (methodResult instanceof Result) {   
40.	            this.explicitResult = (Result) methodResult;   
41.	            return null;   
42.	        } else {   
43.	            return (String) methodResult;   
44.	        }   
45.	    } catch (NoSuchMethodException e) {   
46.	        throw new IllegalArgumentException("The " + methodName + "() is not defined in action " + getAction().getClass() + "");   
47.	    } catch (InvocationTargetException e) {   
48.	        // We try to return the source exception.   
49.	        Throwable t = e.getTargetException();   
50.	  
51.	        if (actionEventListener != null) {   
52.	            String result = actionEventListener.handleException(t, getStack());   
53.	            if (result != null) {   
54.	                return result;   
55.	            }   
56.	        }   
57.	        if (t instanceof Exception) {   
58.	            throw(Exception) t;   
59.	        } else {   
60.	            throw e;   
61.	        }   
62.	    } finally {   
63.	        UtilTimerStack.pop(timerKey);   
64.	    }   
65.	}  

    好了,action执行完了,还要根据ResultConfig返回到view,也就是在invoke方法中调用executeResult方法。 
Java代码     
1.	private void executeResult() throws Exception {   
2.	     //根据ResultConfig创建Result   
3.	     result = createResult();   
4.	     String timerKey = "executeResult: "+getResultCode();   
5.	     try {   
6.	         UtilTimerStack.push(timerKey);   
7.	         if (result != null) {   
8.	             //这儿正式执行:)   
9.	             //可以参考Result的实现,如用了比较多的ServletDispatcherResult,ServletActionRedirectResult,ServletRedirectResult   
10.	             result.execute(this);   
11.	         } else if (resultCode != null && !Action.NONE.equals(resultCode)) {   
12.	             throw new ConfigurationException("No result defined for action " + getAction().getClass().getName()    
13.	                     + " and result " + getResultCode(), proxy.getConfig());   
14.	         } else {   
15.	             if (LOG.isDebugEnabled()) {   
16.	                 LOG.debug("No result returned for action "+getAction().getClass().getName()+" at "+proxy.getConfig().getLocation());   
17.	             }   
18.	         }   
19.	     } finally {   
20.	         UtilTimerStack.pop(timerKey);   
21.	     }   
22.	 }   
23.	 public Result createResult() throws Exception {   
24.	     if (explicitResult != null) {   
25.	         Result ret = explicitResult;   
26.	         explicitResult = null;;   
27.	         return ret;   
28.	     }   
29.	     ActionConfig config = proxy.getConfig();   
30.	     Map results = config.getResults();   
31.	     ResultConfig resultConfig = null;   
32.	     synchronized (config) {   
33.	         try {   
34.	             //根据result名称获得ResultConfig,resultCode就是result的name   
35.	             resultConfig = (ResultConfig) results.get(resultCode);   
36.	         } catch (NullPointerException e) {   
37.	         }   
38.	         if (resultConfig == null) {   
39.	             //如果找不到对应name的ResultConfig,则使用name为*的Result   
40.	             resultConfig = (ResultConfig) results.get("*");   
41.	         }   
42.	     }   
43.	     if (resultConfig != null) {   
44.	         try {   
45.	             //参照StrutsObjectFactory的代码   
46.	             Result result = objectFactory.buildResult(resultConfig, invocationContext.getContextMap());   
47.	             return result;   
48.	         } catch (Exception e) {   
49.	             LOG.error("There was an exception while instantiating the result of type " + resultConfig.getClassName(), e);   
50.	             throw new XWorkException(e, resultConfig);   
51.	         }   
52.	     } else if (resultCode != null && !Action.NONE.equals(resultCode) && unknownHandler != null) {   
53.	         return unknownHandler.handleUnknownResult(invocationContext, proxy.getActionName(), proxy.getConfig(), resultCode);   
54.	     }   
55.	     return null;   
56.	 }   
57.	 //StrutsObjectFactory   
58.	 public Result buildResult(ResultConfig resultConfig, Map extraContext) throws Exception {   
59.	     String resultClassName = resultConfig.getClassName();   
60.	     if (resultClassName == null)   
61.	         return null;   
62.	     //创建Result,因为Result是有状态的,所以每次请求都新建一个   
63.	     Object result = buildBean(resultClassName, extraContext);   
64.	     //这句很重要,后面将会谈到,reflectionProvider参见OgnlReflectionProvider;   
65.	     //resultConfig.getParams()就是result配置文件里所配置的参数<param></param>   
66.	     //setProperties方法最终调用的是Ognl类的setValue方法   
67.	     //这句其实就是把param名值设置到根对象result上   
68.	     reflectionProvider.setProperties(resultConfig.getParams(), result, extraContext);   
69.	     if (result instanceof Result)   
70.	         return (Result) result;   
71.	     throw new ConfigurationException(result.getClass().getName() + " does not implement Result.");   
72.	 }  

   这样,一个Struts2的请求流程基本上就结束了。 
Global site tag (gtag.js) - Google Analytics