struts2总结

jopen 10年前

1.为什么学习Struts框架

   a.Struts框架好处

struts2是主流的开发技术,大多数公司在使用,struts把相关的servlet组件的各种操作都进行了相应的封装,这样就可以轻松地完成复杂的操作。Struts2是基于MVC模式开发的,MVC结构是一个优秀的设计思想,可以提高程序结构的灵活性,便于日后的维护和扩展。

 --简化开发工作量,提高开发效率

     --采用了优秀的设计思想(MVC)

  b.Struts框架作用

struts充当控制器,接收请求,分发处理,

  c.如何学习Struts框架

     先重点掌握框架使用,原理性有余力可以多了解些。

 

注:我们目前使用的Struts版本为struts2 2.1.8。(2.1.6版本bug较多)

  d.学校struts要掌握的重点:

valuestack

action

result

拦截器

标记库

2.MVC模式在JavaWeb中的应用

   *a.什么是MVC

     是一种软件架构的思想,将一个软件的组成部分划分成三种不同类型的 模块,分别是:

模型(用于封装业务逻辑,包括业务数据和业务处理逻辑,由JavaBean组件   (比如实体类,DAO,Service)实现),

视图(负责显示界面与用户交互,处理表示逻辑,一种是展示数据,另一种      是接受数据,由jsp组件实现),

控制器(用于控制流程,协调模型与视图,由Servlet、Filter组件(比如之 前的ActionServlet)实现)。

使用mvc的最主要的原因是为了实现模型的复用:即模型只需要开发 一次, 模型不关心处理的结果如何展现,模型将结果交给不同的视 图,由视图来展 现这些数据;另外,可以利用不同的视图来访问 同一个模型。  

 

MVC:基于面向对象的一种常见的体系架构

Model:

封装数据以及对数据操作的方法(相当于饭店中做菜的材料(数据)和做菜的厨师(方法)

一个流程用一个方法

View:

封装的是用户界面,接收用户请求,负责输入和输出(和用户打交道)(相当于服务员)

Controller:

封装的是业务流程,控制业务流程(相当于饭店中的管理者)(如果程序比较简单,可以把Controller写在Model里面)

 

   b.为什么使用MVC

     企业程序更强调结构灵活,易于扩展和升级。所以广泛选用MVC模式开发。

   *c.JavaWeb中如何实现MVC结构

    JavaWeb技术主要有:Servlet,JSP,JavaBean,Filter,Listener等

    V部分:JSP组件 (cart.jsp)

    M部分:JavaBean组件(entity,dao)

    C部分:Servlet,Filter组件 (ShoppingCartServlet)

 *d.MVC处理流程

   --首先浏览器发出请求

   --请求进入C控制器

   --C控制器调用M模型组件处理

   --M模型组件处理后返回处理结果

   -- C控制器调用V视图组件生成响应信息

 

 *e.Struts框架的MVC实现

    原有ShoppingCart程序,虽然符合MVC结构,但不适合大型项目开发,因为请求一多,ShoppingCartServlet会出现大量的if..else... ,可以进行以下方式的改造:

为ShoppingCartServlet配置一个XML文件,该文件定义不同请求和不同Action组件的对应关系,将原有if..else..分支处理用Action组件封装。   

  **f.Struts框架的处理流程

    --浏览器发出struts请求

    --请求进入struts控制器

    --struts控制器解析xml配置文件(xml定义了请求和Action对应关系)

    --控制器根据不同请求,调用不同的Action

    --Action调用DAO处理请求,之后返回结果

    --控制器根据结果调用视图组件为用户响应

 struts总结

 

3.了解Struts历史

   Struts框架分成Struts1和Struts2.Struts2和Struts1没有必然联系。Struts2是以WebWork框架核心(xwork)为基础构建起来

最早出现的 Struts1是一个非常著名的框架,它实现了MVC模式。Struts1简单小巧,其中最成熟的版本是Struts1.2。之后出现了WebWork框架,其实现技术比Struts1先进,但影响力不如Struts1。在框架技术不断发展过程中,有人在WebWork核心XWork的基础上包装了 Struts1(算是两种框架的整合),由此,结合了Struts1的影响力和WebWork的先进技术,Struts 2诞生了。所以说,Struts2不是Struts1的升级,它更像是WebWork的升级版本。

*4.Struts基本使用

   a.引入struts2核心开发包(5个) 

(1).struts2-core-2.1.8.1.jar

Struts2核心包,是Struts框架的“外衣。

(2).xwork-core-2.1.6.jar

Struts2核心包,是WebWork内核。

(3).ognl-2.7.3.jar

用来支持ognl表达式的,类似于EL表达式,功能比EL表达式强大的

多。

(4).freemarker-2.3.15.jar

freemarker是比jsp更简单好用,功能更加强大的表现层技术,用来 替代jsp的。在Struts2中提倡使用freemarker模板,但实际项目中使 用jsp也很多。

(5).commons-fileupload-1.2.1.jar

用于实现文件上传功能的jar包。

b.在web.xml中配置下struts控制器

   c.根据请求编写Action,JSP

   d.在struts.xml定义Action和请求对应关系

5.HelloWord入门实例

welcome.action-->Struts控制器-->struts.xml-->WelcomeAction

-->welcome.jsp

(Action请求可以是/welcome.action或者/welcome两种写法)

7、struts.xml基本配置

struts.xml放在src根目录下。

a) <struts>

根元素,可以包含多个<package>元素

b)<package>

<package name="***" namespace="/默认" extends="struts-default">

元素主要用于将Action分组定义。name属性用于指定包名;extends一般指 定为struts-default。struts-default这个包在struts-default.xml中定义,其中 包含了struts2运行所需的必要组件。

添加namespace属性的主要作用是避免在大项目中出现的命名冲突问题。

注意:

http://localhost:8080/struts01/netctoss/welcome

<package name="demo1" namespace="/netctosss" 

extends="struts-default">

<action name="welcome" class="action.WelcomeAction">

     <result name="success">/WEB-INF/jsp/Welcome.jsp</result>

 </action>

namespace属性默认为“/”(即从应用名后的第一个"/"开始到最后一个“/”结束)。

eg:对于http://localhost:8080/struts01/netctoss/cost/showList.action

其中的namespace属性值应为”/netctoss/cost”。

eg:

如果不加namespace="/netctoss"那么

http://localhost:8080/struts01/netctoss/list.action便不会得到正确的匹配

一个<package>元素可以包含多个<action>..</action>主要用于定义请求

和Action组件的对应关系。name属性指定请求名;class属性指定Action的包

名.类名

c)<action>

<action name="***" method="execute默认" class="***">

一个<action>元素可以包含多个<result>...<result>主要用于定义视图响 应。name属性与Action.execute()返回值一致。

其中

(1).method属性可以省略,如果省略则默认调用Action中的execute()方法

(2.)如果不指定class属性,Struts会默认调用框架底层的ActionSupport

      类处理(见下面:)。框架会默认为该<action>添加一个class,作用是

      转发到对应

      的<result  name="success">中指定的页面。当有nameform请求发

      来时,struts不会调 用action(找不到)而是直接调用result中name属

      性为success对应的页面。

注:name=”success”可以省略

<action name="nameform">

  <!--<result name=”success”>/WEB-INF/jsp/Welcome.jsp</result>-->

       <result>/WEB-INF/jsp/Welcome.jsp</result>

</action>

  struts总结

--------ActionSupport类:---------

 public String execute(){

     return “success”;

}

  

d)<result>

<result name="success默认" type="dispatcher默认">

e:注意:

1.对于package、action、result,如果使用默认属性值,则可以省略属性名。

eg:<result name=”sucess” type=”dispatcher”>****</sesult>

  如果使用默认属性值:则可以写成:

  <result >****</sesult>

 

对于重复使用的result可以写成全局的result

<!-- 定义全局result -->

<global-results>

<!-- 错误处理 -->

<result name="error">/WEB-INF/jsp/error.html</result>

<!-- 拦截器登录处理 -->

<result name="login" type="redirectAction">showLogin</result>

</global-results>

 

2.page包中的各组件的放置顺序:

具体见struts-2.1.7dtd

package (result-types——> interceptors——>default-interceptor-ref——> 

default-action-ref——> default-class-ref——> global-results——> 

global-exception-mappings——> action*)>

拦截器配置放在global-results之前

 

3.当项目业务处理较多时如果将所有的配置都写在struts.xml一个文件中将很不方便,所以可以将不同的处理模块分别写在不同的xml文件中,最后在struts.xml中加载和模块配置文件,过程如下:

struts.xml:

<struts>

<!-- 加载各模块配置文件 -->

<include file="struts-cost.xml"/>

<include file="struts-account.xml"/>

...........

<package name="netctoss-default" extends="json-default">

<!-- 追加共通的Action,Interceptor组件 -->

拦截器、全局results....

</package>

</struts>

struts-cost.xml:

<!-- 资费模块配置文件 -->

<struts>

<package name="cost" extends="netctoss-default">

........

</package>

</struts>

struts-account.xml:

<!-- 资费模块配置文件 -->

<struts>

<package name="account" extends="netctoss-default">

........

</package>

</struts>

.............................

浏览器访问地址:

http://localhost:8080/应用名/namespace/action

http://localhost:8080/struts03_2/list/costList.action?page=4

以上的namespace默认为’’/’,本例为list

8、如何解决用户绕过ActionServlet,直接访问jsp文件的问题。

将所有jsp文件拷贝到WEB-INF目录下。因为WEB-INF目录中的内容不能直接访问,但能转发过来。

当然也可以用之前的session技术(太麻烦)。

对于struts中WebRoot文件夹下的.jsp页面仍可以直接输入地址访问。

9、Struts2提供的方便之处:

a.数据的自动的封装

根据页面组件的name属性,自动封装到Action中对应的name属性中。

在Jsp页面<input name=”name” type=”text”/>

在action中会自动给属性private String name 赋值。但是注意要有name对应的getter和setter方法。

b.数据的自动的传递

Action中的属性在jsp页面可以直接用EL表达式拿到

eg:

Action中属性private String name;

在jsp页面上可以直接${name}的到对应的值

按理说,EL表达式取数据的对象只有pageContext,request,session,application四类,不应该从Action对象中取name属性值,但 struts框架重新包装了request,getAttribute()方法被改写(request的其他方法没被改写),如果从request对象中调用getAttribute()方法没获取到值(null),那么会自动从action中取,这样,struts中EL表达式就可以直接获取到 Action属性中的值了。

上面介绍的采用了包装模式:(相当于手机加壳案例和线程池案例(其中的close方法特殊)。参考如下:

public class StrutsRequestWrapper extends HttpServletRequestWrapper{

     //重写

     public Object getAttribute(String key){

           //寻找原request信息

           Object obj = super.getAttribute(key);

           if(obj == null){

              //寻找valuestack信息

               obj = valuestack.findValue(key);

              //Ognl.getValue(key,context,root);

           } struts总结

           return obj;

     }

}

${name} -->request.getAttribute("name")

-->Ognl.getValue("name",context,root)

 

10.项目示例(资费列表显示)

a.显示默认主页面

 index.action-->struts控制器-->index.html

b.资费列表显示

feeList.action-->struts控制器

-->FeeListAction-->FeeDAO-->COST

-->fee_list.jsp

  1)编写COST对应的Entity实体类

  2)编写DAO,DBUtil

     --引入oracle驱动包

  3)编写Action

  4)配置Action

  5)编写JSP

    --引入jstl.jar,standard.jar

11.OGNL技术的使用

  1)什么是OGNL

OGNL是Object-Graphic Navigation Language(对象图导航语言)的缩写,

是一种功能强大的表达式语言(与EL表达式类似)。OGNL可以让我们用非常

简单的表达式访问对象层(访问对象的数据和方法),它用一个独立的lib形

式出现(封装于ognl.jar工具包中),方便我们使用或者构建自己的框架。 

 

OGNL区别于EL表达式,OGNL可以在.java文件中通过getValue()方法访问对

象,而EL表达式不可以在.java文件中使用;EL表达式在.jsp文件中可以直接

输出数据,而OGNL不可以,OGNL在.jsp文件中只能当做struts标签中的属性

值,通过struts标签访问数据。

  2)OGNL基本原理

    OGNL工具主要由3部分构成,具体如下

    a.OGNL引擎

        负责解析ONGL表达式,定位数据

    b.Root根存储区(Object类型)

        负责存储要访问的目标对象(一个)。

    c.Context变量存储区(Map类型)

        负责存放多个要访问的目标对象。


  *3)OGNL基本语法

    a.访问Root区域对象基本语法

       Ognl.getValue("OGNL表达式""Map存储器内容","根存储器内容");

Ognl.getValue("OGNL表达式""根存储器内容");

--访问简单数据:"属性" 例如"name"

int id = (Integer)Ognl.getValue("id", foo);

String name = (String)Ognl.getValue("name", foo);

       --访问数组或List集合:"属性[0]" 

           例如"arr[1]"

String arrVal = (String)Ognl.getValue("arr[1]", foo);

String  listVal = (String)Ognl.getValue("list[2]", foo);

       --访问map集合元素:"属性['key']"或"属性.key"

     例如"map.c"或"map[‘two’]"  

          注:”map[‘a’]”不可用(‘a’结果为整数)

String mapVal = (String)Ognl.getValue("map.b", foo);

String mapval2 = (String)Ognl.getValue("map['user.name']",foo);

 --访问方法:"属性值.方法()",

    例如"list.size()"

System.out.println(Ognl.getValue("map['two'].toUpperCase()", foo));

System.out.println(Ognl.getValue("list.size()", foo));

 --访问构造方法:"new 包名.构造方法"

    例如"new java.util.Date()"

Object obj = 

Ognl.getValue("new org.tarena.entity.Foo('TOM')", foo);

 --访问静态成员:"@包名.类名@成员"

    例如"@java.lang.Math@PI"

Ognl.getValue("@java.lang.Math@abs(-1)", foo);

Ognl.getValue("@java.lang.Math@PI", foo);

 --创建List对象:"{元素1,元素2}"

Object obj1 = Ognl.getValue("{'struts','hibernate','spring'}",foo);

 --创建Map对象

    "#{key1:value1,key2:value2}"

Object obj2 = Ognl.getValue("#{1:'tom',2:'jack',3:'rose'}", foo);

 

public static void main(String[] args) throws Exception {

Foo foo = new Foo();

foo.setId(100);

foo.setName("张三");

foo.setArr(new String[] { "A""B""C" });

foo.setList(Arrays.asList("芙蓉""凤姐""春哥"));

Map<String, String> map = new HashMap<String, String>();

map.put("one""Java");

map.put("two""JavaWeb");

map.put("three""Struts2");

map.put("user.name""Scott");

foo.setMap(map);

//测试OGNL访问foo目标对象

//Ognl.getValue("OGNL表达式", "Map存储器内容","根存储器内容");

//Ognl.getValue(expression, root);

Map context = new HashMap();

//访问root基本的属性值

int id = (Integer)Ognl.getValue("id", foo);

System.out.println(id);

String name = (String)Ognl.getValue("name", foo);

System.out.println(name);

System.out.println(Ognl.getValue("user.name", foo));

//访问root数组和集合元素

String arrVal = (String)Ognl.getValue("arr[1]", foo);

System.out.println(arrVal);

String  listVal = (String)Ognl.getValue("list[2]", foo);

System.out.println(listVal);

//访问root对象的map集合元素

String mapVal = (String)Ognl.getValue("map.b", foo);

System.out.println(mapVal);

String mapval2 = (String)Ognl.getValue("map['user.name']",foo);

System.out.println(mapval2);

//支持运算符

System.out.println(Ognl.getValue("id+1",foo));

//System.out.println((Integer)Ognl.getValue("id",foo)+1);

System.out.println(Ognl.getValue("\"姓名:\"+name", foo));

System.out.println(Ognl.getValue("id > 200",foo));

//方法调用

System.out.println(Ognl.getValue("map['two'].toUpperCase()", foo));

System.out.println(Ognl.getValue("list.size()", foo));

//构造方法的调用

Object obj = Ognl.getValue("new org.tarena.entity.Foo('TOM')", foo);

System.out.println(obj);

System.out.println(Ognl.getValue("name", obj));

//静态方法和常量的调用

System.out.println(Ognl.getValue("@java.lang.Math@abs(-1)", foo));

System.out.println(Ognl.getValue("@java.lang.Math@PI", foo));

//创建一个list集合

Object obj1 = Ognl.getValue("{'struts','hibernate','spring'}",foo);

System.out.println(obj1.getClass().getName());

//创建一个map集合

Object obj2 = Ognl.getValue("#{1:'tom',2:'jack',3:'rose'}", foo);

System.out.println(obj2.getClass().getName());

}

12、OGNL技术在Struts的使用

在Struts2中有一个ValueStack(本质就是存放数据的集合)数据对象,该对象存储了请求相关的所有数据信息。例如request,session,application,action等.

  Struts2采用OGNL工具对ValueStack进行操作。

  1)xwork对OGNL进行了部分改造

    在xwork中将原来OGNL标准结构中的Root存储区改造成了一个栈结构

    (CompoundRoot)以前只能存放一个对象,现在进行了扩容,可以存放

    多个对象

    当利用"属性"表达式访问时,优先去栈顶对象寻找,没有再去次栈顶寻找。

2)ValueStack结构(OgnlValueStack)

    ValueStack就是一个容器(Map集合)(存放东西的不是Tomcat容器)

 

3)Struts2标签的使用

  在JSP中,利用Struts2标签显示数据,需要为标签指定OGNL表达式,标签利用表达式定位ValueStack中的数据,进行相应操作。

  a) debug标签:显示valuestack状态

<s:debug></s:debug>

  b) iterator标签:循环集合元素

    value属性:指定ognl

    var属性:指定循环变量,会被存放到ValueStack的context区域。

    status属性:指定循环状态变量,会被存放到ValueStack的context区域.该

                 变量有count属性表示一共循环了多少个元素。

    index属性表示当前循环元素的索引。

<s:iterator value="new int[totalPages]" status="i">

       <s:if test="#i.count==page">

          <a class="current_page"href="costList.action?page=${i.count }">

              <s:property value="#i.count"/>

           </a>

        </s:if>

         <s:else>

          <a href="costList.action?page=${i.count }">

           <s:property value="#i.count"/>

          </a>

        </s:else>

 </s:iterator>          

  c) if...else...标签:判断分支

       test属性:指定ognl判断表达式

<s:if test="page < totalPages>                  

          <a href="costList?page=${page+1 }">下一页</a>

        </s:if>

        <s:else>

           下一页 

 </s:else>

  d) property标签:显示数据

       value属性:指定ognl表达式

<s:iterator value="costList" var="cost">

  <tr><td><s:property value="#cost.id"/></td></tr>

</s:iterator>

  e) date标签:将date数据格式化输出

       name属性:指定ognl表达式

       format属性:指定日期模板

<td>

<s:date name="#cost.createTime" format="yyyy-MM-dd HH:mm:ss"/>

</td> 

13、Action组件的相关使用

   1)Action组件的原理

       --客户发出action请求给struts控制器

--前端控制器截获请求并根据配置信息调用对应的action

       --struts控制器会创建valuestack对象

       --struts控制器根据请求创建Action对象并将Action压入valuestack的root

         栈顶(Action线性安全,不用考虑并发问题,struts会为每一个请求 创

         建一个Action对象,区别于Servlet)

       --struts控制器调用一系列拦截器做一些辅助性的工作(一般都是

         valuestack的操作),如通过parmaeterInterceptor将表单中的数据赋值

          给action中的对象;还有将请求相关request,session对象放入到

          valuestack的context区域,同时将ValueStack对象存入到request中。

          存储的key为”struts.valueStack”。(这样,当一次请求结束后,容器

          会销毁request,request销毁了,其中的valuestack对象也就销毁了)

       --控制器调用Action,执行业务方法处理(根据输入算输出并返回结果)

       --控制器根据Action返回值调用result视图组件处理。

       --请求处理完毕,销毁valuestack和Action对象。

       一次请求,一个valuestack和action对象

 2Struts中写Action过程

定输入、定输出、根据输入算输出、返回结果

   *3)Action中如何使用session,request

        (1).利用ActionContext和ServletActionContext工具类(不推荐)

         a.ActionContext返回是Struts框架封装成Map之后的

  request,session,application.

eg:

//将用户信息写入Session

ActionContext ac = ActionContext.getContext();

Map<String,Object> session = ac.getSession();

session.put("user", adminCode);

Map<String,Object> application = ac.getApplication();

Map<String,Object> request = (Map)ac.get("request");

         b.ServletActionContext返回的是Servlet中使用的request, session,

           application原有类型。

        (使用建议:存值取值建议用Map结构API,如果有特殊需求再采用

          Servlet原有类型(如获取客户端IP))

eg:

HttpServletRequest request = ServletActionContext.getRequest();

HttpSession session = httpRequest.getSession();

HttpServletResponse response = ServletActionContext.getResponse();

ServletContext application = ServletActionContext.getServletContext()

 

ActionContext工具类中的方法getSession()如何将HttpSession对

象封装成Map对象的说明:

      Map----impements--->AbstructMap---extends-->SessionMap(重

                                            写了Map中的put和get方法)

----如何将原有HttpSession封装成Map------

public class SessionMap extends AbstractMap{

    private HttpSession session;   

     public SessionMap(HttpSession session){

          this.session = session;

    }

      public void put(Object key,Object val){

          session.setAttribute(key,val);

     }

     public Object get(Object key){

         session.getAttribute(key);

     }

}

不推荐使用 ActionContext访问Session的方式,因为这种方式的“侵入性”较强。ActionContext是Struts2的API, 如果使用其他框架代替目前的Struts2框架,而我们实际项目中的Action的数量非常庞大,每个类都修改的话,会非常麻烦。

   (2).利用Aware接口方式注入(推荐)

将Action实现Aware接口,由框架底层将对象注入给Action中相应的变量

当action实例化时候,struts框架就会自动将session对象(在valuestack中)赋值给实现了SessionAware接口的类中的变量(就是注入)。

(即session,ruquest,response,application) 

RequestAware 对应的是Map结构的Request

  SessionAware 对应的是Map结构的Session

  ApplicationAware 对应的是Map结构的Application

           ServletRequestAware对应的HttpServletRequest

ServletResponseAware对应的HttpServeltResponse

ServletContextAware对应的ServletContext

eg:

public class LoginAction implements SessionAware{

protected Map<String,Object> session;

    ...............

public void setSession(Map<String, Object> session) {

this.session = session;

}

public String execute(){

if(("admin".equals(adminCode)) && ("1234".equals(passWord))){

session.put("user"adminCode);

return "success";

}else{

return "error";

}

}

}

(3)、使用工具类(ActionContext、ServletActionContext)还是

Aware接口?

Action组件内部用Aware接口、Action组件之外(比如后面的拦截器)用ActionContext、ServletActionContext

   4)Action属性注入

  作用:在创建Action对象时指定属性值

  在<action>配置中为Action组件的属性指定一个初始值,该值在创建

       Action对象时注入。可以将一些变化的参数值,利用该方法指定。例如

       pageSize,管理员用户名,dir存储路径等。

在<action>配置中,为Action对象的属性指定初始值。使用格式如下:

<action name="feeList" class="org.action.FeeListAction">

        <!--创建Action对象时给属性指定一个值-->

        <param name="属性名">属性值</param>

        <result></result>

      </action>

eg:

public class ListAction{

    private Stirng param1;

    private String param2;

    .....

public String getParam1(){

return param1;

}

..... struts总结

public setParam1(Stirng param1){

this.param1 = param1;

}

.......

 }

       struts.xml:

       <action name="feeList" class="org.action.FeeListAction">

        <param name="param1">admin</param>

        <param name="param2">1234</param>

        <result>........</result>

      </action>

   5)Action通配符配置

       Action通配符可以将多个具有相同属性的Action整合为一个Action。

在<action>配置时,name请求名可以使用若干个*符号。然后再其他属

       性中通过使用{1},{2}引用第一个*号,第二个*号所代表的字符串。

<!-- 通配符配置 -->

<action name="*_*" method="{2}"class="com.tarena.action.{1}">

    <result name="success">/index.jsp</result>

    </action>

 

  eg:

           CostAction.java:

public class CostAction {

//属性定义--省略

public String add(){

System.out.println("资费添加操作");

return "success";

}

public String update(){

System.out.println("资费更新操作");

return "success";

}

}

struts.xml:

<action name="cost_*" method="{1}"

class="com.tarena.action.CostAction">

  <result name="success">/index.jsp</result>

</action>

 

14、Result组件的相关使用

  1)Result组件原理

    a.Result组件的作用

     Result组件负责生成响应视图内容。将ValueStack中的数据做不同形式

     的输出。

     Struts2框架提供了多种不同的Result组件类型,用于做不同形式响应。

      例如json数据响应,流数据响应,jsp响应等。

    b.Result组件相关配置

       --Result声明定义

         <package>

              <result-types>

                <result-type name="result类型" class="Result组件实现类"/>

              </result-types>

         </package>

        --Result使用定义

        <action>

          <result name="标示符" type="result类型"></result>

        </action>

c.编写一个转发功能的Result组件

定义的类类必须实现Result接口(Result组件),并且将约定的execute方法实现在该方法中编写了生成响应输出的逻辑代码。

public class MyDispatcherResult implements Result{

     private String location;

     //setter和getter方法

................

     //约定方法

  public void execute(){

            HttpServletRequest request = ServletActionContext.getRequest();

         HttpServletResponse response =ServletActionContext.getResponse();

          RequestDispatcher rd = request.getRequestDispatcher(location);

             rd.forward(request,response);

      }

}

  *2)掌握经常使用的Result组件

      a. JSP视图

       --dispatcher(默认):以转发方式调用JSP

       --redirect:以重定向方式调用JSP

        <result name="" type="dispatcher(默认)/redirect">

          jsp页面

       </result>

      b.Action视图

       --chain:以转发方式调用下一个Action

       --redirectAction:以重定向方式调用下一个Action

    c:

stream:以字节流方式响应,Action中的InputStream类型的属性以

         字节流方式输出。

eg:由服务器端生成验证码图片并以流的方式发送给浏览器。

 

在struts.xml端的配置如下:

 <!--验证码处理 -->

    <action name="image" class="org.tarena.netctoss.action.ImageAction">

       <result type="stream"  name="success">

       <param name="inputName">imageStream</param>

       </result>

       <result name="fail">/WEB-INF/jsp/login.jsp</result>

   </action>

注:其中的<param name="inputName">imageStream</param>区别于Action

    属性注入,其作用为:给result对象指定参数值

     d:

json:以json字符串响应,将Action中指定的属性拼成一个json字符串输

出。

     注:

其中,a、b、c中的result组件在’struts-defaulg”包下,d中的result组件

     在”json-default”,struts-core.jar中的默认包

<package name="struts-default" abstract="true">如下:

 

<package name="struts-default" abstract="true">

<result-types>

            <result-type name="chain" class="*****"/>

            <result-type name="dispatcher" class="*****"/>

            <result-type name="freemarker" class="*****"/>

            <result-type name="httpheader" class="*****"/>

            <result-type name="redirect" class="*****"/>

            <result-type name="redirectAction" class="*****"/>

            <result-type name="stream" class="class="*****"/>

            <result-type name="velocity" class="class="*****"/>

            <result-type name="xslt" class="*****"/>

            <result-type name="plainText" class="*****"/>

        </result-types>

</package>

    相同命名空间调用:

  <result type="redirectAction">

      请求名

  </result>

    跨命名空间调用

  <result type="redirectAction">

      <param name="actionName">请求名</param>

      <param name="namespace">/</param>

  </result>

    ======资费删除流程=====

fee_delete.action-->FeeDeleteAction-->FeeDAO

-->FeeListAction-->fee_list.jsp

============练习=============

完成课上项目任务。

扩展任务:完成资费的添加,更新处理,查看,启用等。

 

16、json Result组件

   主要负责将Action的属性以json字符串格式输出。

   json Result的使用步骤:

   a.引入struts2-json-plugin.jar

   b.将<package>元素的extends属性改为继承"json-default"(json-default包

     的范围比原struts-default包大,所以更改后仍可以使用以前包中的组件)

   c.<result>使用配置

     //只返回Action中一个属性值

  <result type="json">

       <param name="root">属性名</param>

  </result>

    //返回Action中多个属性值

  <result type="json">

       <param name="includeProperties">

       属性名1,属性名2,属性名3

      </param>

  </result>

   //返回Action中所有属性值

  <result type="json">

  </result>

 

   因为使用name属性默认值,所以name=”success”可以省略。

 

17、Struts2表单标签

  1.什么是标签

struts标签封装了界面显示逻辑,用于简化JSP。

jstl标签用来代替jsp页面中的java代码,struts标签不但可以代替jsp页面中的

java代码,还可以代替jsp页面中html标记

  2.struts常用标签:

textfield,password,hidden,textarea

radio,checkboxlist,select

checkbox,form

a) textfield 

   以输入框方式显示属性值.

   name属性指定ognl表达式,表示输入框中的值

<input type="text" class="readonly" name=cost.id value=${cost.id

 readonly />

相当于:

  <s:textfield name="cost.id" cssClass="readonly" readonly="true">

</s:textfield>

规定输入字段为只读(内容不可被更改)。在html中input标签readonly属性

     直接写,在struts标签中要写成:readonly=”true”。类似的的还有disabled要写成

     disabled=”true”

b) textarea 

   以文本域方式显示属性值.

   name属性指定ognl表达式,表示文本域中的值

 <textarea class="width300 height70">${cost.descr}</textarea>

<s:textarea name="cost.descr" cssClass="width300 height70"></s:textarea>

c)password:

 以文本框方式显示密码

 name属性指定ognl表达式,,表示输入框中的值

注:默认情况下,文本框内不会显示密码,如果要显示密码则需要设置showPassword属性。

密码:<s:password name="password" showPassword="true"></s:password>

d) radio: 

    以一组单选框方式显示,并可以根据属性值决定哪个选项选中。

 list属性指定集合ognl,用于生成若干radio选项。

 name属性指定ognl,用于决定哪个raido选中。

<s:radio name="cost.costType" list="#{'1':'包月','2':'套餐','3':'计时'}">

</s:radio>

e)checkbox:

以单选框方式显示选中状态

name属性指定ognl表达式,name属性值为true时,单选框默认会被选中

eg:

婚姻状况:<s:checkbox name="marry""></s:checkbox>已婚

f)checkboxlist:

以复选框方式显示选中状态

name属性指定ognl表达式,用来决定哪些复选框默认被选中。

list属性为map<key,value>集合

listKey属性为map集合中的key

listValue属性为map集合中的value,为在界面显示的内容

参考struts05_1中的form.jsp

eg1:

爱好:<s:checkboxlist name="chk" list="#session.favors" listKey="id" 

       listValue="name"></s:checkboxlist>

eg2:

    <s:checkboxlist name=”” list=”costList’” listKey="id" 

       listValue="name"></s:checkboxlist>

 

g)select:

 以下拉列表的方式显示数据:

     name属性指定ognl表达式,用来决定哪个option默认被选中。

eg1:

<s:select name="status" cssClass="select_search"

         list="#{0:暂停,'1':'开通','2':'暂停','3':'删除'}">

          </s:select>

相当于

 <select class="select_search" >

                     <option value="2">全部</option>

                     <option value="1">开通</option>

                     <option value="0">暂停</option>

                     <option value="-1">删除</option>

     </select>

   eg2:

   <s:select list=costList name=”” listKey=id listValue=name></s:select>

注:对于上面的struts标签中的name属性,不但可以指定ognl表达式获取相关的数据,还可以将action中的属性名设置为name的属性值,这样就可以实现表单数据的自动提交。

h)form:

theme属性:表示主题,可选项有xhtml和simple;如果不写,默认为xhtml 。

  theme属性如果为默认值(xhtml),那么在使用struts表单标

    签时候会自动添加样式(自动添加tr、tr,让显示更美观),

    这样就会破坏当前页面的布局和样式。

    如果在用struts表单标签时,没有用struts中的form标签而是

    使用html中的form标签,那么在使用struts表单标签时候也

    会自动添加样式

<s:form action="update" namespace="/" theme="simple">

   .............

</s:form>

 通用属性(所有标记都具备的属性):

label

labelpostion

required

tooltip

tooltipIconPath

cssClass(html中的class)

cssStyle(html中的style)

name

value

 

18、拦截器组件

2)什么是拦截器:

  一般利用拦截器封装一些通用性处理功能,便于重复利用。例如请求参

    数给action属性赋值、日志记录、权限检查、文件上传、事务处理等。拦截

    器通过配置方式调用,因此使用方法比较灵活,便于维护或扩展。

Struts2框架提供了大量的拦截器组件,如果不能满足开发者需要,可以

 进行自定义。

eg:

<interceptor name="params" 

class="com.opensymphony.xwork2.interceptor.ParametersInterceptor"/>

其中ParametersInterceptor类用于在创建Action对象时将表单中的参数

赋值给Action中的相关属性。

具体见:

ReferencedLibraries/struts2-core-2.1.8.jar/struts-default.xml

 /<interceptors>标记

 

2)拦截器完整工作流程:

 

a.客户发送一个Action请求,请求交给控制器

b.控制器创建一个ValueStack、Action对象,并将Action压入ValueStack栈顶

c.控制器调用ActionInvocation组件执行请求处理

d.ActionInvocation组件调用请求相关的拦截器组件--前部分处理.

e.然后再调用Action组件业务方法

f.然后再调用Result组件处理方法

g.最后执行拦截器组件--后部分处理.

h.将响应信息输出,为客户端响应

 3)拦截器作用

    拦截器可以在Action和Result组件调用之前执行,也可以在Action和Result

    组件之后执行。

 4)自定义拦截器

    a.编写拦截器组件实现类(两种方法):

     1.编写拦截器组件实现类实现Interceptor接口的interceptor方法。

   public class MyInterceptor implements Interceptor{

      public String intercept (ActionInvocation in){

           //拦截器的前期处理逻辑

           in.invoke();//执行Action和Result

          //in.invokeActionOnly();//仅执行Action

           //拦截器的后期处理逻辑

       }

}

eg:

public class CheckLoginInterceptor implements Interceptor{

public void destroy() {

// TODO Auto-generated method stub

}

public void init() {

// TODO Auto-generated method stub

}

public String intercept(ActionInvocation in) throws Exception {

System.out.println("(前期处理)自定义拦截器被调用。。。");

ActionContext ac = ActionContext.getContext();

//ActionContext ac = in.getInvocationContext();

Map<String,Object> session = ac.getSession();

//登录检查

if(session.get("user") == null){

//为登录

return "login";//进入登录页面

}

in.invoke();//执行ActionDAO和Result--->jsp

    System.out.println("(后期处理)自定义拦截器被调用。。。");

return null;//invoke()方法执行之后,后面的返回值都无效即不会返回),因为执行in.invoke(),也即执行Action和Result调用Jsp后,返回的值没有任何意义。

}

           }

  2.所有拦截器组件实现类也可以通过继承AbstractInterceptor类并实现

   inteceptor方法。

eg:

public class Interceptor2 extends AbstractInterceptor {

public String intercept(ActionInvocation in) throws Exception {

System.out.println("(前期处理)自定义拦截器被调用。。。");

// ActionContext ac = ActionContext.getContext();

ActionContext ac = in.getInvocationContext();

Map<String, Object> session = ac.getSession();

// 登录检查

if (session.get("user") == null) {

// 未登录

return "login";// 进入登录页面

}

in.invoke();// 执行Action和Result

              System.out.println("(后期处理)自定义拦截器被调用。。。");

return null;//invoke()方法执行之后,后面的返回值都无效(即不会返回),写着只是好看一点

}

       }

   注意:当执行了invoke()方法后,invoke()方法后面的返回值无效(即不会返回),写着只是好看一点。如果没有执行invoke(),返回值有效。控制器会根据该返回值调用一个Result生成响应信息

 b.注册拦截器(将拦截器注册给struts2框架)

(1).方法一:

<package>

   <interceptors>

       <interceptor name="拦截器名"class="拦截器实现类"/>

       <interceptor name="" class=""/>

   </interceptors>

</package>

eg:

 <!-- 定义拦截器 -->

<interceptors>

  <interceptor name="checklogin" 

        class="org.tarena.netctoss.interceptor.CheckLoginInterceptor2"/>

 </interceptors>

 <!-- 使用拦截器 -->

<action name="costDelete" class=org.tarena.netctos.CostDeleteAction>

      <interceptor-ref name="checklogin"/>

    <interceptor-ref name="defaultStack"/>//调用默认拦截器栈中的拦截器

  <result name=cuccess>/WEB-INF/jsp/cost/cost_delete.jsp</result>

</action>

  struts总结

(1).方法二

创建拦截器栈(<interceptor-stack name="loginStack">),将多个拦截器放在拦截器栈中,这样就可以在需要的地方直接引入拦截器栈就行了(<interceptor-ref name="loginStack"/>

eg:

<!-- 定义拦截器 -->

<interceptors>

  <interceptor name="checklogin" 

        class="org.tarena.netctoss.interceptor.CheckLoginInterceptor2"/>

<interceptor-stack name="loginStack">

<interceptor-ref name="checklogin"/>

<interceptor-ref name="defaultStack"/>

 </interceptor-stack>

</interceptors>

    <!-- 使用拦截器 -->

<action name="costDelete" class=org.tarena.netctos.CostDeleteAction>  

<interceptor-ref name="loginStack"/>   

<result name=cuccess>/WEB-INF/jsp/cost/cost_delete.jsp</result>

</action>

方法二中的定义拦截器的方法实际上是先定义一个checklogin拦截器,然后定义一个拦截器栈(用于存放多个拦截器),把定义的checklogin拦截器和struts默认的拦截器放在拦截器栈里面。在使用拦截器时候,直接引入拦截器栈就可以了(相当于引入了checklogin拦截器和struts默认拦截器)。

 

  c.使用拦截器组件

    (1).为所有Action指定默认使用配置,这样,拦截器便对该包中的所有Action

       起拦截作用

   <package ******>

        <default-interceptor-ref  name="拦截器名"/>

   </package>

   --(2).为某一个Action指定使用配置

   <action>

      <interceptor-ref name="拦截器名"/>

   </action>

eg:

<action name="showAddCost">

       <interceptor-ref name="loginStack"/>

<interceptor-ref name="defaultStack"/>

<result>/WEB-INF/jsp/cost/cost_add.jsp</result>

</action>

   注意:如果为某个Action指定了拦截器配置,不会再调用指定的默认拦截器.所以一般会加上<interceptor-ref name="defaultStack"/>

 

5)fileUpload内置拦截器的使用

   a)fileUpload拦截器功能

       用于实现文件上传功能。

fileUpload拦截器处理流程:

  客户端发送上传文件请求

首先,struts控制器收到请求后调用fileUpload拦截器,fileUpload拦截器调用commons-file-upload.jar上传组件对请求提交

的表单信息进行解析。

然后将解析后的文件保存到服务器临时目录下,并将临时目录下的文件

   对象赋值给Action的File属性。

执行Action,在Actin业务中,需要做文件复制,将临时文件转移到目标

   目录中。

当Action和Result调用完毕后,清除临时目录下的文件,因此在Actin业务中,需要做文件复制,将临时文件转移到目标目录中。

   b)上传示例

     注意事项:

      --需要引入commons-io.jar)

      --form表单method必须为post;enctype必须为multipart/form-data

      --Action组件按下面规则接收表单信息

          <input type="file" name="xxx">

        Action属性如下:

          private File xxx;// 临时目录下的文件

          private String xxxFileName;// 源文件名

          private String xxxContentType;// 源文件类型

     ---struts默认允许上传的文件所占最大内存为2097152字节(不同计算机可

        能略有不同)。如果要更改默认设置,可以参考如下“更改struts中的默

        认设置”

更改struts中的默认设置(即修改如下配置文件):

ReferencedLibraries/struts2-core-2.1.8.jar/org-apache.struts2/default.properties

 

18、更改strts中的默认配置:

1.更改对客服端发送过来的参数进行解码的方式:

<constant name="struts.i18n.encoding" value="gbk"/>(默认为utf-8)

2.更该允许客户端上传的最大文件大小值:

<constant name=struts.multipart.maxSize value=100000000/>

(默认大小为:2097152字节,约2M)

 

19、全局异常处理:

<!--全局的异常处理,当异常从Action中抛出时,会自动进入全局异常处理模块-->

   <global-results>

      <!-- 错误处理 -->

    <result name="error">/WEB-INF/jsp/error.html</result>

     </global-results>

 

 <global-exception-mappings>

<exception-mapping exception="java.lang.Exception" result="error">

       </exception-mapping>

 </global-exception-mappings>

 

20、用户输入不存在请求资源路径(及action不存在)处理:

<!-- 指定一个默认响应的Action,当遇到例外的action请求,该项配置启作用 -->

<default-action-ref name="defaultAction"/>

<!-- 采用重定向方式调用toLogin -->

<action name="defaultAction">

<result type="redirectAction">

<param name="namespace">/</param>

<param name="actionName">toLogin</param>

</result>

</action>

    <!-- 显示登录页面 -->

    <action name="toLogin">

<result>/WEB-INF/jsp/login.jsp</result>

 </action>

通过此配置后,用户输入非法请求资源路径时便不会出现“404”错误提示了,而是友好地跳转到登录页面。

======NETCTOSS项目========

1)项目描述(一期工程)

  实现电信计费系统的账号管理,业务管理,资费管理,管理员和角色管理的功能。

2)项目技术架构

  主要采用Struts2,JDBC,Ajax,jQuery技术。

   采用MVC结构分层思想开发。

   表现层(V):JSP,Ajax,jQuery

   控制层(C):Struts2控制器Filter+Action

   业务层(M):JavaBean,Bean组件

   数据访问层(M):DAO+JDBC

3)项目工程结构

  a.开发包引入

    --Struts2开发包(5个核心+1个json响应包)

    --Oracle驱动包

  b.src源码

    org.tarena.netctoss.action

    org.tarena.netctoss.action.fee

    org.tarena.netctoss.action.account

    org.tarena.netctoss.service

    org.tarena.netctoss.dao

    org.tarena.netctoss.pojo

    org.tarena.netctoss.util

    org.tarena.netctoss.test

  c.配置文件

    web.xml : 配置struts2控制器

    struts.xml:struts2框架主配置

     --struts-fee.xml : 资费模块配置

     --struts-account.xml:账号模块配置

  d.WebRoot

     WebRoot/WEB-INFO/jsp/fee/资费页面

     WebRoot/styles/样式文件

     WebRoot/js/脚本文件

     WebRoot/images/页面图片

  

=========记录检索=========

search.action-->SearchAction-->DAO

 

 

 

1.项目评审

  1)自我简介,项目需求描述,功能演示

  2)典型功能实现讲解

2.共通问题处理

  1)表单校验处理(参考“jQuery表单校验插件”目录的资料)

     可以利用jQuery编写校验函数,

     也可以基于现有的jQuery扩展插件来实现

     例如validate和formvalidate等插件都可以

   2)Struts2全局异常处理

       当异常抛出Action的业务方法时,可以利用全局异常处理方式解决(在struts.xml追加配置)。

   3)将页眉导航部分提取成一个head.jsp,

 通过include方式引入各个页面。

   4)设置一个默认响应Action。

  遇到不能识别的Action请求,或找不到请求对应的Action时,调用该默认Action做响应