编程开源技术交流,分享技术与知识

网站首页 > 开源技术 正文

struts2原理及配置详解(struts struts2)

wxchong 2024-08-17 01:09:51 开源技术 49 ℃ 0 评论

struts简介

Struts2是一个基于MVC设计模式的Web应用框架,它本质上相当于一个servlet,在MVC设计模式中,Struts2作为控制器(Controller)来建立模型与视图的数据交互。Struts 2是Struts的下一代产品,是在 struts 1和WebWork的技术基础上进行了合并的全新的Struts 2框架。其全新的Struts 2的体系结构与Struts 1的体系结构差别巨大。Struts 2以WebWork为核心,采用拦截器的机制来处理用户的请求,这样的设计也使得业务逻辑控制器能够与ServletAPI完全脱离开,所以Struts 2可以理解为WebWork的更新产品。虽然从Struts 1到Struts 2有着太大的变化,但是相对于WebWork,Struts 2的变化很小。

struts2体系结构

应用流程注解

当Web容器收到请求(HttpServletRequest)它将请求传递给一个标准的的过滤链包括(ActionContextCleanUp)过滤器。

经过Other filters(SiteMesh ,etc),需要调用FilterDispatcher核心控制器,然后它调用ActionMapper确定请求哪个Action,ActionMapper返回一个收集Action详细信息的ActionMaping对象。

FilterDispatcher将控制权委派给ActionProxy,ActionProxy调用配置管理器(ConfigurationManager) 从配置文件中读取配置信息(struts.xml),然后创建ActionInvocation对象。

ActionInvocation在调用Action之前会依次的调用所用配置拦截器(Interceptor N)一旦执行结果返回结果字符串ActionInvocation负责查找结果字符串对应的(Result)然后执行这个Result Result会调用一些模版(JSP)来呈现页面。

拦截器(Interceptor N)会再被执行(顺序和Action执行之前相反)最后响应(HttpServletResponse)被返回在web.xml中配置的那些过滤器和核心控制器(FilterDispatcher)

Struts2框架的内部执行过程

上面讲了MVC中个文件的执行过程,那只是一小部分,现在来看一下整个struts2框架的执行机制,下面的图来源于官网,然后加了自己的改动。

由上图看出,整个框架的运行又紧紧围绕着核心过滤器StrutsPrepareAndExecuteFilter展开工作。深入到filter的源码中会对理解有所帮助。下面几篇博客对filter的介绍很好。

3,默认拦截器

struts-default.xml配置文件中定义了一个默认拦截器栈,这些拦截器就是动作方法执行之前的要执行的。常用的有封装用户表单数据到javabean的modelDriven拦截器,用于输入验证的validation拦截器,等等。

4,View和Controller之间的交互

从视图页面每次发来的用户请求都会产生一些数据,这些数据都存放在哪儿呢?实际上,每次动作类执行前,核心过滤器StrutsPrepareAndExecuteFilter都会创建2个对象:ActionContext和ValueStack,这2个对象存储了动作访问期间用到的所有数据。这些数据又可以在jsp页面上通过strut标签和OGNL表达式来取得。

(1),ActionContext是一个map数据结构,其中的key是一些常见的域对象(application,session,request等),而value又是一个map。也就是说ActionContext是一个大map包裹着一些小map。

(2),ValueStack是一个ArrayList数据结构,并且是一个栈结构,每次都在栈顶存取数据。

5,Controller与Model之间的交互

C和M之间的交互比较简单,利用Struts框架提供的拦截器:ModelDriven,即可实现将用户表单提交的数据封装到对应的javabean中。要点:(1)javabean类自己编写。(2)动作类要实现ModelDriven接口。(3)实现抽象方法getModel()。

strut2源码分析:

首先我们使用struts2框架都会在web.xml中注册和映射struts2,配置内容如下:

1 <filter> 
2 <filter-name>struts2</filter-name> 
3 <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class> 
4 </filter> 
5 <filter-mapping> 
6 <filter-name>struts2</filter-name> 
7 <url-pattern>/*</url-pattern> 
8 </filter-mapping>

注:在早期的struts2中,都是使用FilterDispathcer,从Struts 2.1.3开始,它已不推荐使用。如果你使用的Struts的版本 >= 2.1.3,推荐升级到新的Filter,StrutsPrepareAndExecuteFilter。在此研究的是StrutsPrepareAndExecuteFilter。

  StrutsPrepareAndExecuteFilter中的方法:

void init(FilterConfig filterConfig) 继承自Filter,过滤器的初始化
doFilter(ServletRequest req, ServletResponse res, FilterChain chain) 继承自Filter,执行过滤器
void destroy()继承自Filter,用于资源释放
void postInit(Dispatcher dispatcher, FilterConfig filterConfig) Callback for post initialization(一个空的方法,用于方法回调初始化)

web容器一启动,就会初始化核心过滤器StrutsPrepareAndExecuteFilter,并执行初始化方法,初始化方法如下:

1 public void init(FilterConfig filterConfig) throws ServletException { 
2 InitOperations init = new InitOperations(); 
3 Dispatcher dispatcher = null; 
4 try { 
5 //封装filterConfig,其中有个主要方法getInitParameterNames将参数名字以String格式存储在List中 
6 FilterHostConfig config = new FilterHostConfig(filterConfig); 
7 //初始化struts内部日志 
8 init.initLogging(config); 
9 //创建dispatcher ,并初始化
10 dispatcher = init.initDispatcher(config);
11 init.initStaticContentLoader(config, dispatcher);
12 //初始化类属性:prepare 、execute
13 prepare = new PrepareOperations(filterConfig.getServletContext(), dispatcher);
14 execute = new ExecuteOperations(filterConfig.getServletContext(), dispatcher);
15 this.excludedPatterns = init.buildExcludedPatternsList(dispatcher);
16 //回调空的postInit方法17 postInit(dispatcher, filterConfig);
18 } finally {
19 if (dispatcher != null) {
20 dispatcher.cleanUpAfterInit();21 }
22 init.cleanup();
23 }
24 }

关于封装filterConfig,首先看下FilterHostConfig ,源码如下:

1 public class FilterHostConfig implements HostConfig { 
2 
3 private FilterConfig config; 
4 //构造方法 5 public FilterHostConfig(FilterConfig config) { 
6 this.config = config; 
7 } 
8 //根据init-param配置的param-name获取param-value的值 
9 public String getInitParameter(String key) {
10 return config.getInitParameter(key);
11 }
12 //返回初始化参数名的迭代器 
13 public Iterator<String> getInitParameterNames() {
14 return MakeIterator.convert(config.getInitParameterNames());
15 }
16 //返回Servlet上下文
17 public ServletContext getServletContext() {
18 return config.getServletContext();
19 }
20 }  只有短短的几行代码,getInitParameterNames是这个类的核心,将Filter初始化参数名称有枚举类型转为Iterator。此类的主要作为是对filterConfig 封装。

一个Action内包含多个请求处理方法的处理

Struts1提供了DispatchAction,从而允许一个Action内包含多个请求处理方法。Struts2也提供了类似的功能。

处理方式主要有以下三种方式:

1. 1 动态方法调用:

DMI:Dynamic Method Invocation 动态方法调用。

动态方法调用是指:表单元素的action不直接等于某个Action的名字,而是以感叹号后加方法名来指定对应的动作名:

<!-- 动态方法调用HTML标签与Struts2标签 -->
<form action="computeAction!add.action" name="from" >
<s:form action="computeAction!add.action" name="form" theme="simple" >

则用户的请求将提交到名为”computeAction”的Action实例,Action实例将调用名为”add”方法来处理请求。

当指定调用某一方法来处理请求时,就不会走默认执行处理请求的execute()方法。

注意:要使用动态方法调用,必须设置Struts2允许动态方法调用,通过设置struts.enable.DynamicMethodInvocation常量来完成,该常量属性的默认值是true。

<struts>
<!--
 //禁用动态方法调用,默认为true启用,false禁用
 constant:name="struts.enable.DynamicMethodInvocation" -->
<constant name="struts.enable.DynamicMethodInvocation" value="true" /></struts>

示列:简单的一个加法和减法例子。

1. index.jsp用户在页面输入两个数字,选择相加,或者相减

当用户点击加或减需要走同一个Action但处理请求方法不同,这里使用了js动态选择。

<body>
<!-- 动态方法调用 使用:Struts2标签也可以使用HTML标签 -->
<s: name="form" theme="simple" >
 num1:<s:textfield name="num1" />
 num2:<s:textfield name="num2" />
<s:submit type="button" value="加" onclick="computeMethod('add')" />
<s:submit type="button" value="减" onclick="computeMethod('subtract')" />
</s:form>
<!-- js -->
<script type="text/javascript">
function computeMethod(op){
 document.form.action="computeAction!"+op;//动态选择处理请求的方法 document.form.submit();//提交 } </script>
</body>

2. struts.xml配置信息,启用动态方法调用(可选)

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1//EN" "struts-2.1.dtd" ><struts>
<!--
 //禁用动态方法调用,默认为true启用,false禁用
 constant:name="struts.enable.DynamicMethodInvocation" -->
<constant name="struts.enable.DynamicMethodInvocation" value="true" />
<package name="struts2" extends="struts-default">
<action name="computeAction" class="com.struts.ComputeAction" >
<result name="fruitPage" >/fruit.jsp</result>
</action>
</package></struts>

3. ComputeAction控制器的类处理请求

package com.struts;/**

* Struts2控制器的类
* @author asus
* */public class ComputeAction { /** 属性 */
private int num1; private int num2; private int fruit;//结果
/** 若请求为指定操作方法默认执行execute()方法 */
public String execute(){
 System.out.println("当调用其它方法就不会走这个方法!"); return "";
 } 
 /** 执行处理加法 */
public String add(){ this.fruit=num1+num2;//加
return "fruitPage";
 } 
 /** 执行处理减法 */
public String subtract(){ this.fruit=num1-num2;//减
return "fruitPage";
 } 
 /** JavaBean */
public int getNum1() { return num1;
 } public void setNum1(int num1) { this.num1 = num1;
 } public int getNum2() { return num2;
 } public void setNum2(int num2) { this.num2 = num2;
 } public int getFruit() { return fruit;
 } public void setFruit(int fruit) { this.fruit = fruit;
 }
}

4. fruit.jsp响应结果的页面

<body>
<!-- 结果页面 -->
 计算结果:<s:property value="fruit" /></body>

1.2Action配置method属性(示列与以上代码大多一致,只修改有变更的):

将Action类中的每一个处理方法都定义成一个逻辑Action方法。

1. index.jsp页面

<body>
<!-- Action配置method属性 使用:Struts2标签也可以使用HTML标签 -->
<s:form name="form" theme="simple" >
 num1:<s:textfield name="num1" />
 num2:<s:textfield name="num2" />
<s:submit type="button" value="加" onclick="computeMethod('addAction')" />
<s:submit type="button" value="减" onclick="computeMethod('subtractAction')" />
</s:form>
<!-- js -->
<script type="text/javascript">
function computeMethod(op){
 document.form.action=op;//动态选择处理请求的方法 document.form.submit();//提交 } 
 </script>
</body>

2. struts.xml配置信息

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1//EN" "struts-2.1.dtd" ><struts>
<package name="struts2" extends="struts-default">
<action name="addAction" class="com.struts.ComputeAction" method="add" >
<result name="fruitPage" >/fruit.jsp</result>
</action>
<action name="subtractAction" class="com.struts.ComputeAction" method="subtract" >
<result name="fruitPage" >/fruit.jsp</result>
</action>
</package></struts>

  通过action元素的method属性来指定Action执行时调用的方法。

优点:使得以更加安全的方式来实现动态方法的调用,不让别人看到你的实现方法。

缺点:繁琐,一个处理请求的方法要跟一个action。

  Struts2根据method属性查找方法有两种途径:

  1.查找与method属性值完全一致的方法

  2.查找doMethod形式的方法

  使用动态方法调用和method属性的区别:

    1.通过以上三个struts.xml中的配置信息例子来说,他们的共同点是都在操作同一个Action。

2.<form action="">中请求地址不同。

3.动态方法的返回值相同,则会通过result进入一个页面。而method属性就算两个方法的返回值相同但进去不同的result,可能会进入两个不同的页面。

由上可以分析出:

(1)如果使用同一个Action,不同的处理请求的方法,响应使用相同的配置(result等)则使用动态方法调用。

(2)如果使用同一个Action,不同的处理请求的方法,响应分别使用不同的配置,则使用action元素的method属性,为同一个Action配置多个名称。

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表