一: 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.】
|
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的请求流程基本上就结束了。
|