struts2笔记最终版

学习方法

 动手(先实践)—》原理

 动手 动手 再动手

 配置文件    理解+copy

Strutsà表现层 核心:视图(index.jsp)和请求分离 action

Struts1名气比 webWork大 但是它的技术并么有webwork强大

 

Struts2

Struts简介

a) Struts à(英文)支架—>框架

b) Jakarta项目—>Apache软件基金会(包含HTTP服务器 Tomcat  Lucene Struts)

c) Stuts是流行和成熟的基于MVC设计模式的Web应用程序框架

d) 使用Struts目的:帮助我们减少在运用MVC设计模型来开发Web应用的时间

MVC模式 

a) javaWeb开发主要由jsp完成

b) jsp+JavaBean=Model1(小型开发)

c) jsp+servlet+javabean=model2(最典型的MVC )

d) MVC是模型视图控制器(Mode(模型) View(视图) Controller(控制器)),一种软件设计典范,用一种业务逻辑,数据,界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性定制界面及用户交互的同时,不需要重新编写业务逻辑

e) 原理

用户输入—-àController控制器(将用户输入的指令和数据传递给业务模型)àModel(进行业务逻辑判断,数据库存取)—>DB数据库

                         —>View视图(进行业务选择不同的视图)-à(将结果反馈给用户) Controller控制器

Struts2发展史

a) Struts1(2001)àStruts2(2007)ßWebWork

b) Struts2不是一个全新的框架,因此稳定性,性能等各方面都有很好的保证,同时吸收了Struts1WebWork两者的优势.

第一个Struts2例子

http://www.yiibai.com/struts_2/struts_architecture.html 在线不错的XX

   Jar下载:http://struts.apache.org/(官网)

            http://archive.apache.org/dist/struts/ (历史版本)

1MyEclipse中新建web工程

2struts-2.3.28\apps解压struts2-blank.war( 最基础的示例程序 )

3进入struts-2.3.28\apps\struts2-blank\WEB-INF\classes下把struts.xml拷到web工程的src下面,因为工程编译完它默认就把src下的文件放到class文件下面。

 

4.拷类库,把以下jar包拷到项目的lib文件下

 

5.jar包添加到项目的引用中

6.web.xml,参考struts自带的web.xml,把filter的配置拷过来

<filter>

<filter-name>struts2</filter-name>

<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter

</filter-class>

</filter>

<filter-mapping>

<filter-name>struts2</filter-name>

<url-pattern>/*</url-pattern>

</filter-mapping>

7. 创建类IndexAction extends ActionSupport,重写execute()方法

@Override

public String execute() throws Exception {

System.out.println("执行action方法");

return SUCCESS;

}

8 struts.xml配置

<struts>

<constant name="struts.devMode" value="true" />

 

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

<action name="hello" class="com.whhp.action.s">

<result>/hello.jsp</result>

</action>

</package></struts>

http://localhost:8080/HelloWorldAction/ 

http://localhost:8080/HelloWorldAction/hello 或者

http://localhost:8080/HelloWorldAction/hello.action

跳转到hello.jsp,第一个示例程序成功!

 

Struts2工作原理

当你在客户端敲http://localhost:8080/strust2_0100_Introduction/hello

首先找到:strust2_0100_Introduction这个web application,找到后去执行这个web application下的web.xml

Tomcat接收到请求之后,会发现这个web.xml下面,配了一个filter,而这个filter过滤所有的url地址,所以当我们在地址栏敲http://localhost:8080/strust2_0100_Introduction/hello后,会被StrutsPrepareAndExecuteFilter接收到

<filter>

        <filter-name>struts2</filter-name>    

<filter-class>

org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>

  </filter>

  <filter-mapping>

        <filter-name>struts2</filter-name>

        <url-pattern>/*</url-pattern>

</filter-mapping>

StrutsPrepareAndExecuteFilter接收到后url情求后,它首先看namespace

Struts.xml

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

        <action name="hello" >

            <result>/hello.jsp</result>

        </action>

</package>

查到“/”后面的hello,它就会去package下面查是否有name属性叫“hello”的action,有的话,找里面对应的result是谁--àhello.jsp

 

Struts的好处就是:我可以把“请求”和“视图展现”分开,而不是写死。分开的好处就是:如果要换成其他视图,配一下就好了,所以更加灵活。Struts核心的本质就是解决了:把你的请求和最后的结果分开。

Struts2namespace

示例工程Struts2_0200_Namespace

Struts.xml

<struts>

    <constant name="struts.devMode" value="true" />

    <package name="front" extends="struts-default" namespace="/front">

        <action name="index">

            <result>/Namespace.jsp</result>

        </action>

    </package>

     <package name="main" extends="struts-default" namespace="">

        <action name="index">

            <result>/Namespace.jsp</result>

        </action>

    </package>

</struts>

所以namespace为空意味着:只要找到一个index.action,没有找到精确的对应的namespace,全部都交给namespace为空的这个package去处理,所以这个package囊括了其他所有package处理不了的action

 

Struts2详细讲解

i. 访问Servlet API

ii. Action搜索顺序

iii. 动态方法调用

iv. 指定多个配置文件

v. 默认Action

vi. Struts2后缀

vii. 接收参数

viii. 处理结果类型

 

访问Servlet API

1) Struts2提供了三种方式去访问Servlet API

a) ActionContext

b) 实现***Aware接口

c) ServletActionContext

2) Action搜索顺序

 http://localhost:8080/HelloWorldAction/path1/path2/path3/hello.action

第一步:判断package是否存在,: path1/path2/path3/

 存在

第二步:判断action是否存在,如果不存在则去默认namespacepackage里面寻找action

第三步:如果没有,则报错

不存在

第二步:检查上一级路径的package是否存在(直到默认namespace),重复第一步

第三步:如果没有,则报错

3) 动态方法调用

动态方法调用就是为了解决一个action对应多个请求的处理,以免action太多

a) 指定method属性

1.添加public  String add(){

System.out.println("执行添加方法");

return SUCCESS;

}

2. struts.xml中添加

 <action name="addaction" method="add" class="com.whhp.action.IndexAction">

          <result>/add.jsp</result>

        </action>

3.访问http://localhost:8080/HelloWorldAction/addaction

4.很麻烦action很多

b) 感叹号方式(需要开启) ,官网不推荐使用这种方式,建议大家不要使用.

用这种方式需要先开启一个开关

<constant name="struts.enable.DynamicMethodInvocation" value="true" />

struts.xml中做如下修改

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

        <action name="hello" class="com.whhp.action.IndexAction">

          <result>/hello.jsp</result>

          <result name="add">/add.jsp</result>

          <result name="delete">/delete.jsp</result>

        </action>

      

</package>

访问时http://localhost:8080/HelloWorldAction/hello!delete

c) 通配符方式(官网推荐做法)

方式1

  <action name="hello_*" method="{1}" class="com.whhp.action.IndexAction">

          <result>/hello.jsp</result>

          <result name="add">/{1}.jsp</result>

          <result name="delete">/{1}.jsp</result>

        </action>

            访问: http://localhost:8080/HelloWorldAction/hello_add

            方式2

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

<result>/hello.jsp</result>

<result name="add">/{2}.jsp</result>

<result name="delete">/{2}.jsp</result>

<result name="update">/{2}.jsp</result>

            访问:http://localhost:8080/HelloWorldAction/Index_add

4) 指定多个配置文件

在一个项目中action可能有几百个,如果配置在一个struts.xml文件中,你懂的

解决如下: <include file="login.xml"></include>每个模块的配置文件写在独自的xml

Login.xml中写法与struts.xml中相同

添加login.xml

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

<!DOCTYPE struts PUBLIC

"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"

"http://struts.apache.org/dtds/struts-2.3.dtd">

<struts>

<constant name="struts.devMode" value="true" />

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

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

<result>/hello.jsp</result>

<result name="add">/{2}.jsp</result>

<result name="delete">/{2}.jsp</result>

<result name="update">/{2}.jsp</result>

</action>

</package>

</struts>

Struts.xml中写法

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

<!DOCTYPE struts PUBLIC

"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"

"http://struts.apache.org/dtds/struts-2.3.dtd">

 

<struts>

<constant name="struts.devMode" value="true" />

<include file="login.xml"></include>

</struts>

页面访问http://localhost:8080/HelloWorldAction/Index_add_action

Action代表包名 index代表类名 add代表方法名

注意:每个xml中可以指定编码格式,如下:

<constant name="struts.custom.i18n.resources" value="UTF-8”/>

5) 默认Action  

用户在输入时可能会输入错误,为了有更好的体验度,需要默认action

struts.xml 

<struts>

<constant name="struts.devMode" value="true" />

<include file="login.xml"></include>

</struts>

 

login.xml

<struts>

<constant name="struts.devMode" value="true" />

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

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

<action name="index">

<result>/error.jsp</result>

</package>

</struts>

Web输入http://localhost:8080/HelloWorldAction/bbb

跳转到error.jsp

6) Struts2后缀

Web输入http://localhost:8080/HelloWorldAction/bbb.html时由于后缀是action会提示找不到文件

struts.xml中添加如下配置

<struts>

<constant name="struts.devMode" value="true" />

<include file="login.xml"></include>

<constant name="struts.action.extension" value="html,do"></constant>

</struts>

这个时候输入后缀为html,do,action,jsp都没问题,但是不能不写后缀了

7) 接收参数

Action有三种接收参数的方式

a) action属性接收参数

b) 用DomainModel(实体模型)接收参数(VO DTO)

c) 用ModelDriven接收参数(不建议使用,不演示)

Action的属性接收参数

Index.jsp

<head>

<base href="<%=basePath %>"/>  </head>

使用action属性接收参数<a href="user/user!add?name=a&age=8">添加用户</a>

</body> 

</html>

链接的意思是:执行user下面的user.action下面的add方法

怎么接受参数的呢?第一种方式.在自己的action下面定义两个属性,写好get,set方法,当new action的时候,会自动把这两个属性从参数里面拿过来,帮你设置好。

参数跟我们的成员变量一一对应,这时候它就会自动把我们的参数传递到我们成员变量里。这时候当我们调用add()方法时,它直接可以用了。

UserAction.java

public class UserAction extends ActionSupport {

private String name;

private int age;

public String add() {

System.out.println("name=" + name);

System.out.println("age=" + age);

return SUCCESS;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public int getAge() {

return age;

}

public void setAge(int age) {

this.age = age;

}

}

Struts.xml

<struts>

    <package name="user" extends="struts-default" namespace="/user">

 <action name="user" class="com.demo.struts2.user.action.UserAction">

            <result>/user_add_success.jsp</result>

        </action>

    </package>

</struts>

使用Domain Model (实体模型) 接收参数

<html>

<body> 使用Domain Model接收参数

<a href="user/user!add?user.name=a&user.age=8">添加用户</a>

</body>

</html>

public class UserAction extends ActionSupport {

private User user;

//private UserDTO userDTO;

public String add() {

System.out.println("name=" + user.getName());

System.out.println("age=" + user.getAge());

return SUCCESS;

}

public User getUser() {

return user;

}

public void setUser(User user) {

this.user = user;

}

}

public class User {

private String name;

private int age;

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public int getAge() {

return age;

}

public void setAge(int age) {

this.age = age;

}

jsp页面:

 

<form action="testmodel">

用户名:<input type="text" name="user.uname"><br> 密码:<input

type="text" name="user.pwd"><br> <input type="submit"

value="登录">

</form>

一般来说,我们输入参数不一定刚好跟我们的域模型一致,比如说:用户有namepassword两个属性,但是你输入进来的应该还有个密码确认passwordconfiguration

 

Struts模块包含

 

Struts.xml:

<include file=”login.xml”/>

 

相当于把文件login.xm内容l复制过来


Struts简单数据验证

UserAction.java

public class UserAction extends ActionSupport {

private String name;

public String add() {

if(name == null || !name.equals("admin")) {

this.addFieldError("name", "name is error");

this.addFieldError("name", "name is too long");

return ERROR;

}

return SUCCESS;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

}

Struts.xml

<struts>

    <constant name="struts.devMode" value="true" />

    <package name="user" extends="struts-default" namespace="/user">

        <action name="user" class="com.demo.struts2.user.action.UserAction">

            <result>/user_add_success.jsp</result>

            <result name="error">/user_add_error.jsp</result>

        </action>

    </package>

</struts>

登陆不成功的时候,该怎么样向前台传递信息呢? 这时候其实是个麻烦事,因为我们的userAction实际上是没有跟我们的Request, response属性绑在一起的;userAction访问不到我们的Request, response,ServletContext,这些都没有,Struts2里面是采用另外一种机制。

This.addFieldError(“name”,”name is error”);

添加对于属性校验的错误信息的,错误信息的名字一般也是我们的属性名字叫做name,那么这个属性出错,后面是这个错误的具体信息:name is error

 

 

 

 

 

user_add_error.jsp

<body>

User Add Error!

<s:fielderror fieldName="name" theme="simple"/>

<br /> 

<s:property value="errors.name[0]"/>

<s:debug></s:debug>

</body>

 

这里调用addFieldError()之后在前面如何把它拿出来?

在这里我们用到struts2的标签

<s:fielderror  fieldname=”name” theme=”simple”>

调用标签库的时候,必须这么写:

<%@taglib uri=”/struts-tags” prefix=”s”%>

Struts的标签定义是位于

 

 

如果我们去看源码的话会看到它把我们的错误信息封装成:

<url class=”errorMassage”>

<li><span>name is error!</span></li>

</url>

指定成CSS修饰,这样就给我们带来不便,这是Struts2设计不太好的地方。所以Struts2展现的标签在企业开发中应用得不多,因为它都是强制要求你必须按照它的命名规则来去定义它的各种各样的展现。

那我们应该怎么样把我们的字符串拿出来?引出下一个标签,这个标签以后会很常用:

<s:debug></s:debug>

当你写了这个标签之后在页面就会默认显示这个标签 [Debug]

 

点开[Debug]

Struts ValueStack(值栈) Debug

Value Stack Contents

Object

Property Name

Property Value

com.demo.struts2.user.action.UserAction

texts

null

actionErrors

[]

errors

{name=[name is error, name is too long]}

fieldErrors

{name=[name is error, name is too long]}

errorMessages

[]

name

a

locale

zh_CN

actionMessages

[]

com.opensymphony.xwork2.DefaultTextProvider

texts

null

首先:Struts2会把Action里面的属性挨着排给你放进Value Stack

专门有这个标签很常用s:property

<s:property value="errors.name[0]"/>

 

<s:property value="errors"/>

取到errors实际上是一个map:{name=[name is error, name is too long]}

那么我想取到map里面某一个key的内容:

<s:property value="errors.name"/>

[name is error, name is too long]

而这时候实际上value是一个数组,所以我要想去数组的第一项

<s:property value="errors.name[0]"/>

name is error

Struts2_访问Web元素

后台的Action跟我们前台的页面来通讯的时候,由于它拿不到request,sessionservletContext比如当我们有人登陆了,我们要在session中设一个值,表示它登陆了,但是如果你Action访问不到session,你如何把相关的数据设到session里面,response不用访问它,因为我们的结果是通过result返回的。

<body>

取得Map类型request,session,application,真实类型 HttpServletRequest, HttpSession, ServletContext的引用:

<li>前三者:依赖于容器(struts2 )</li>

<li>前三者:IOC</li> (只用这种)

<li>后三者:依赖于容器</li>

<li>后三者:IOC</li>

<form name="f" action="" method="post">

用户名:<input type="text" name="name"/>

密码:<input type="text" name="password"/>

<input type="button" value="submit1" οnclick="javascript:document.f.action='login/login1';document.f.submit();" />

<input type="button" value="submit2" οnclick="javascript:document.f.action='login/login2';document.f.submit();" />

<input type="button" value="submit3" οnclick="javascript:document.f.action='login/login3';document.f.submit();" />

<input type="button" value="submit4" οnclick="javascript:document.f.action='login/login4';document.f.submit();" />

</form>

</body>

Strust.xml

<struts>

    <constant name="struts.devMode" value="true" />

    <package name="login" extends="struts-default" namespace="/login">

        <action name="login*" class="com.demo.struts2.user.action.LoginAction{1}">

            <result>/user_login_success.jsp</result>

        </action>

    </package>

</struts>

 

其实request我们也很少去拿它,因为我们Action的成员变量默认会起到request的作用,它自己会放到valueStack里面, valueStack本身就是放到request里面,所以根本不用去拿request.

import org.apache.struts2.interceptor.ApplicationAware;

import org.apache.struts2.interceptor.RequestAware;

import org.apache.struts2.interceptor.SessionAware;

public class LoginAction2 extends ActionSupport implements RequestAware,SessionAware, ApplicationAware {

//第二种方式区别于第一种方式的不同点是:第一种方式还要在构造函数中进行“取值”,第二种方式直接就能用了!!这个就是IOC(控制反转)的设计思想,依赖注入DI

private Map<String, Object> request;

private Map<String, Object> session;

private Map<String, Object> application;

public String execute() {

request.put("r1", "r1");

session.put("s1", "s1");

application.put("a1", "a1");

return SUCCESS;

}

public void setRequest(Map<String, Object> request) {

this.request = request;

}

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

this.session = session;

}

public void setApplication(Map<String, Object> application) {

this.application = application;

}

}

 

Action总结

1. 实现一个Action最常用的方式,从ActionSupport继承

2. DMI动态方法调用!

3. 通配符*{1}{2}   *_*_*

4. 接收参数的方法(一般用属性或者DomainModel来接收)

5. 简单参数验证addFieldError   一般不使用Struts2UI标签

6. 访问Web元素

1) Map类型

1. IOC

2. 以来Struts2

7. 包含文件配置

8. 默认action处理

Struts2_结果类型

<struts>

    <constant name="struts.devMode" value="true" />

    <package name="resultTypes" namespace="/r" extends="struts-default">

    <action name="r1">

     <result type="dispatcher">/r1.jsp</result>

    </action>

    <action name="r2">

     <result type="redirect">/r2.jsp</result>

    </action> 

    <action name="r3">

     <result type="chain">r1</result>

    </action>

    <action name="r4">

     <result type="redirectAction">r2</result>

    </action>

    </package>

</struts>

Result类型

1. dispatcher 

2. redirect 

3. chain 

4. redirectAction 

5. freemarker

6. httpheader

7. stream

8. velocity

9. xslt

10. plaintext

11. tiles

前面四个要掌握,只要用前两个。

 

ResultType是chain的时候,跳转到一个Action那边去处理,如果Action是在另外一个包p1该怎么办呢?

Struts2文档的路径---guides----resultTypes-----chain---

例子:

<package name="public" extends="struts-default">

    <!-- Chain creatAccount to login, using the default parameter -->

    <action name="createAccount" class="...">

        <result type="chain">login</result>

    </action>

    <action name="login" class="...">

        <!-- Chain to another namespace -->

        <result type="chain">

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

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

        </result>

    </action>

</package>

<param name="namespace">:指定要跳到的namespace

<param name="actionName">指定要跳到的Action

 

动态结果集

 

 

结果类型中redirect和redirectAction的区别

redirect是在处理完当前Action之后,重定向到另外一个实际的物理资源

redirectAction也是重定向,但它重定向到的是另外一个Action

只要是重定向,那么之前凡是保存在request里面的东西就全都消失了

 因为重定向实际是发送第二个请求,故请求中的东西也就不会出现在第二个请求里面了

 也就是说重定向是不共享request的东西,重定向后的页面中无法接收request里的东西

 另外dispatcher结果类型的default属性为TRUE,故<result-type/>缺省为dispatcher

所以如果没有设置type属性的话,那么默认的是请求转发,即浏览器显示的是*.action

但是在设置type="redirect"属性后,就可以重定向了,即浏览器显示的是/login2.jsp

 

Struts2的Action处理链

 从一个Action跳转到另一个Action,有两种办法,即将type设置为chain或者redirectAction

chain结果类型表示将多个Action作为一个链来处理

 而使用chainredirectAction的好处就是:它会按照框架的默认后缀去自动匹配后缀

 而chainredirectAction的区别与dispatcherredirect的区别是一样的

 即同样是跳转到一个Action上,但chain是服务器跳转,而redirectAction是客户端跳转

服务器跳转的过程中,可以共享数据,这时后面的Action就可以接收前面Action中的属性信息进行二次处理

OGNL 

OGNLObject-Graph Navigation Language对象图导航语言)的缩写,它是一种功能强大的表达式语言Expression Language,简称为EL),通过它简单一致的表达式语法,可以存取对象的任意属性,调用对象的方法,遍历整个对象的结构图,实现字段类型转化等功能。它使用相同的表达式去存取对象的属性。

第一个例子

Index.jsp

<body>

访问属性

<a href="<%=contextPath %>/ognl.action?username=u&password=p">ognl</a>

</body>

 

Ognl.xml

<struts>

    <package name="ognl" extends="struts-default">

        <action name="ognl" class="com.demo.struts2.ognl.OgnlAction">

            <result>/ognl.jsp</result>

        </action>

    </package>

</struts>

OgnlAction.java

public class OgnlAction extends ActionSupport {

private String password;

private String username;

private User user;

public User getUser() {

return user;

}

public void setUser(User user) {

this.user = user;

}

public String getPassword() {

return password;

}

public String getUsername() {

return username;

}

public void setPassword(String password) {

this.password = password;

}

public void setUsername(String username) {

this.username = username;

}

}

User.java

public class User {

private int age = 8;

public User() {

}

public User(int age) {

super();

this.age = age;

}

public int getAge() {

return age;

}

public void setAge(int age) {

this.age = age;

}

@Override//重写toString

public String toString() {

return "user" + age;

}

}

此时的urlhttp://localhost:8080/ognl_0100/ognl.action?username=u&password=p 值栈中的内容

<body>

<ol>

<li>访问值栈中的:username = <s:property value="username"/> </li>

<li>访问值栈中的:password=<s:property value="password"/></li>

</ol>

<s:debug></s:debug>

</body>

[Debug] 

Struts ValueStack Debug

Value Stack Contents

Object

Property Name

Property Value

com.demo.struts2.ognl.OgnlAction

password

p

locale

zh_CN

user

null

errorMessages

[]

errors

{}

actionErrors

[]

username

u

texts

null

fieldErrors

{}

actionMessages

[]

com.opensymphony.xwork2.DefaultTextProvider

texts

null

http://localhost:8080/ognl_0100/ognl.action?username=u&password=p&user.age=9就出来了。

 

OGNL表达式的里面传参的时候必须把user.xxx 传进去,Struts才能帮我们构造user方法

只有user.xxx ,才会构造

1.能不能不在url中传user.xxx直接在Action中直接new 出来,能不能取到user的值呢?

Private User user=new User(10);

//原来初始值等于8

结论:也是可以的,因为我们newActionActionnewUser,当然就不为null

2.User()空的构造方法去掉,能不能取到user的值呢?

 

http://localhost:8080/ognl_0100/ognl.action?username=u&password=p&user.age=9

User为空的构造方法去掉之后,它就没有默认为空的构造方法,别人要帮你初始化这个User的时候它确定不了应该调用哪个构造方法,所以取不到值。

 

java.lang.InstantiationException: com.demo.struts2.ognl.User

实例化User出错,所以一般我们的Domain Model(领域模型)永远都要提供一个默认参数为空的方法;

结论:如果我的Domain Model,也就是User对象我自己没有new,也就是Action里面自己没有new,想让Struts2帮你new的时候必须要给domainModel传值,在它帮你new的过程中你的damainModel必须有一个默认为空的构造方法,否则它不知道new出哪一个。

 

action获取页码值中文乱码

   1、需要在struts.xml中做i18n配置

<constant name="struts.i18n.encoding" value="utf-8" />

2、配置后action中打印是否能够获取到中文,如果获取不到中文,在action中进行转码,如下:

String name = new String(project.getName().getBytes("ISO-8859-1"),"UTF-8");

或者写一个类定义转换字符编码的方法如下:

public static String convert(String str){

        try {

            byte[] bytes = str.getBytes("ISO-8859-1");

            return new String(bytes,"UTF-8");

        } catch (UnsupportedEncodingException e) {

            e.printStackTrace();

            return str;

        }

     }

OGNL第二部分

在原来的OgnlAction.java中增加Cat属性和get set 方法

 

Cat.java

public class Cat {

private Dog friend;

public Dog getFriend() {

return friend;

}

public void setFriend(Dog friend) {

this.friend = friend;

}

public String miaomiao() {

return "miaomiao";

}

}

Dog.java

public class Dog {

private String name;

public Dog() { }

public Dog(String name) {

super();

this.name = name;

}

Getset方法……..

public String toString() {

return "dog: " + name;

}

}

 

为什么叫做OGNL对象图导航语言?比如说我们访问OgnlAction.java里面通过“user.age

那么这时候如果我们想访问ognlAction里面的catdog该怎么写? Cat.friend.name

OGNL

1. user.xxx只有传,才会构造,想初始化domain Model,可以自己new也可以传值,但这时候必须有默认为空的构造方法。

OGNL访问

http://localhost:8080/ognl_0100/ognl.action?username=u&password=p&cat.friend.name=judi 

值栈中的内容

<body>

<li>访问值栈中对象的普通属性(get set方法):

<s:property value="cat.friend.name"/></li>

<li>访问值栈中对象的普通方法:

<s:property value="password.length()"/></li>

<s:property value="cat.miaomiao()" /></li>

<li>访问值栈中action的普通方法:<s:property value="m()" /></li>

</body>

OGNL访问静态方法/属性:

访问静态方法/属性的语法:前面@+类名,后面@+方法名/前面@+类名,后面@+属性名

先写类S.java

public class S {

public static String STR="STATIC STRING ";

public static String s(){

return "Hello";

}

}

<body>

访问静态方法:<s:property value="@com.demo.struts2.ognl.S@s()"/>

</body>

 

要在struts.xml中增加配置:

<constant name="struts.ognl.allowStaticMethodAccess" value="true">

</constant>

Struts2中常量的值在:

struts2-core-2.1.8.1.jar-->org.apache.struts2-->default.properties中查:

         

<body>访问普通类的构造方法:

<s:property value="new com.demo.struts2.ognl.User(8)"/>

</body>

 

 

OGNL访问集合

OgnlAction.java中添加:

private List<User> users = new ArrayList<User>();

private Map<String, Dog> dogMap = new HashMap<String, Dog>();

private Set<Dog> dogs = new HashSet<Dog>();

get方法。。。set方法。。。。()

public OgnlAction() {

users.add(new User(1));

users.add(new User(2));

users.add(new User(3));

 

dogs.add(new Dog("dog1"));

dogs.add(new Dog("dog2"));

dogs.add(new Dog("dog3"));

 

dogMap.put("dog100", new Dog("dog100"));

dogMap.put("dog101", new Dog("dog101"));

dogMap.put("dog102", new Dog("dog102"));

}

11.访问List:<s:property value="users"/>

12.访问List中某个元素:<s:property value="users[1]"/>

13.访问List中元素某个属性的集合:<s:property value="users.{age}"/>

14.访问List中元素某个属性的集合中的特定值:<s:property value="users.{age}[0]"/> | 第二种访问方式<s:property value="users[0].age"/>

15.访问Set:<s:property value="dogs"/>

16.访问Set中某个元素:<s:property value="dogs[1]"/>

17.访问Map:<s:property value="dogMap"/>

18.访问Map中某个元素:

第一种写法:<s:property value="dogMap.dog101"/> |

第二种写法:<s:property value="dogMap['dog101']"/>|  

<!--第三种写法:<s:property value="dogMap[\"dog101\"]"/>-->(好像无效果)

19.访问Map中所有的key:<s:property value="dogMap.keys"/>

20.访问Map中所有的value:<s:property value="dogMap.values"/>

21.访问容器的大小:

<s:property value="dogMap.size()"/> | <s:property value="users.size"/> 

 

.获取Stack Context中的信息

我们知道,除了可以从值栈中获取信息,还可以从Stack Context中获取信息,只是要加上#,下面我们通过scope对象来演示。首先是在LoginAction中增加如下字段:
Map myRequest;
Map mySession;
随后再用前面提到的“在Action中获取Scope对象”的方式二来完成这些对象的初始化。即实现RequestAwareSessionAware接口。然后再在execute方法中增加如下内容:
myRequest.put("req", "Req属性");

mySession.put("ses", "Ses属性");
最后在loginSuc.jsp中增加如下代码:
获取Request属性:<s:property value="#request.req"/><br>

获取Session属性:<s:property value="#session.ses"/><br>

获取parameters属性:<s:property value="#parameters.mes"/>
说明:我们获取这些对象都用了#,因为这些对象都是存在一般的Context Map中,而不是存在值栈中。别最后一个信息的获取是因为我们在login.jsp中增加了如下代码:
<input type="hidden" name="mes" value="the message is transfer by hidden">
关于这些scope的更多信息可以参看下表:

名称 

作用 

例子 

parameters

包含当前HTTP请求参数的Map

#parameters.id[0]作用相当于request.getParameter("id")

request

包含当前HttpServletRequest的属性(attribute)的Map

#request.userName相当于request.getAttribute("userName")

session

包含当前HttpSession的属性(attribute)的Map

#session.userName相当于session.getAttribute("userName")

application

包含当前应用的ServletContext的属性(attribute)的Map

#application.userName相当于application.getAttribute("userName")

Attr

用于按request > session > application顺序访问其属性

#application.userName相当于application.getAttribute("userName")

总结OGNL

OGNLObject Graphic Navigation Language(对象图导航语言)的缩写,它是一个开源项目。Struts2使用OGNL作为默认的表达式语言。

相对于EL表达式,它提供了平时我们需要的一些功能,如:支持对象方法调用,支持各类静态方法调用和值访问,支持操作集合对象。OGNL有一个上下文的概念,这个上下文件实质就是一个Map结构,它实现了java.utils.Map接口,在struts2中上下文的实现为ActionContext,下面是上下文的结构示意图:

当struts2接受一个请求时,会迅速创建ActionContext,ValueStack,action。然后把action存放进ValueStack,所以action的实例变量可以接受OGNL访问。

访问上下文中的对象需要使用#号标注命名空间,如#application、#session。另外OGNL会设定一个根对象,在struts2中根对象就是ValueStack值栈对象,如果要访问根对象中对象的属性,则可以省略#命名空间,直接访问该对象的属性即可。在struts2中,根对象的实现类为OgnlValueStack,该对象不是我们想象的只存放单个值,而是存放一组对象,在OgnlValueStack类里有一个List类型的变量,就是使用这个List变量来存放一组对象。在root变量(List类型)中处于第一位的对象叫栈顶对象,通常我们在Ognl表达式里直接写上属性的名称即可访问root变量里对象的属性,搜索顺序是从栈顶对象开始寻找,如果栈顶对象不存在该属性,就会从第二个对象寻找,如果没有找到就从第三个对象寻找,依次往下寻找。 注意:struts2中 ,OGNL表达式需要配合struts的标签才可以使用。

 

Struts2投影

投影,英文叫做projection,其实就是过滤器的意思,把符合条件的给过滤出来

<body>

投影(过滤)<s:property value="users.{?#this.age==1}.{age}"/>

投影:<s:property value="users.{^#this.age>1}.{age}"/>

投影:<s:property value="users.{$#this.age>1}.{age}"/>

投影:<s:property value="users.{$#this.age>1}.{age} == null"/>

</body>

执行结果:      

如果去掉.{age} <s:property value="users.{?#this.age==1}"/>

执行结果:  

如果想取出集合中的第一个元素。

<s:property value="users.{?#this.age==1}[0]"/>

执行结果:        

<s:property value="users.{^#this.age>1}.{age}"/>

“^”(小尖号)代表开头,指的是:大于一的集合那些元素里头开头的第一个,它的age值的集合

<s:property value="users.{$#this.age>1}.{age}"/>

“$”[dolar]代表结尾,指的是: 大于一的集合那些元素里头结尾的那个,它的age值的集合

<s:property value="users.{$#this.age>1}.{age} == null"/>

用来判断集合里面有没有元素。

用中括号访问元素

[]:<s:property value="[1]"/>

执行结果: 

查看Debug这个ValueStack只有两个Object,我们刚刚访问的这个Action永远在栈顶,所以如果要访问Action对象,完全可以这样写:

[]:<s:property value="[0]"/>

中括号访问的OGNL这个栈里面从上往下数的第几个元素;

 

OGNL里面[0]代表的是ognlstack从上往下从第0个位置开始往下一直到栈底的集合

 

如果要访问ognl里面的某一个成员变量比如说username,这时候如果里面有两个Action的话,第一个Action没有,它会按顺序往下找第二个Action,什么情况下会有两个Action在里面呢?

<action name="ognl" class="com.demo.struts2.ognl.OgnlAction">

            <result>/ognl.jsp</result>

        </action>

  <action name="test" class="com.demo.struts2.ognl.TestAction">

            <result type="chain">ognl</result>

        </action>

url访问: http://localhost:8080/Struts2_1900_OGNL/test.Action

 

说明了:首先栈里面被压入了test.action,其次被压入了ognl.action所以证明了如果用了服务器端跳转,它会把用到的action挨着排往里面压。

Struts2标签

Struts标签分为

1.通用标签 (重要)

2.控制标签 (重要)

3UI标签 (不重要)

4AJAX标签 (不重要)有自身的框架.不需要包装,用自身的框架

5$#%区别

标签多动手就会用了,如果程序设计得比较好很多标签都很少用,所以只讲重要的常见的。

1.通用标签

A) property

B) set(换别名用到)

C) include

D) param

E) debug

Fbean

2.控制标签

A) if else if  else

B) iterator

3UI标签(开发用的很少)

5$#%区别

$用于i18nstruts配置文件

#取得ActionContext的值

%将原来的文本串解析为ognl,对于本来就是ognl的文本不起作用。形式:%{要解析的文本串}

 

-----------------------------------------------------------------------------------------------------------------

http://localhost:8080/Struts2_2000_StrutsTags/tags.action?username=u&password=p

 

Struts.xml

<action name="tags" class="com.demo.struts2.tags.TagsAction">

            <result>/tags.jsp</result>

</action>

 

public class TagsAction extends ActionSupport {

private String password;

private String username;

public TagsAction() {

}

public String execute() {

this.addFieldError("fielderror.test", "wrong!");

return SUCCESS;

}

Getset方法。。。。

}


Property

 

<body>

<s:property value="username"/>

property取值为字符串 <s:property value="'username'"/>

property设定默认值 <s:property value="admin" default="管理员"/> 

property设定HTML<s:property value="'<hr/>'escape="false"/> 

</body>

Value的属性值究竟是OGNL还是普通字符串,得去APIparameters!

 

 

Set

 

set设定adminName(如果不设scope属性,默认为requestActionContext

 

<s:set var="adminName" value="username"/>

setrequest取值<s:property value="#request.adminName"/>

set ActionContext取值<s:property value="#adminName" />

set 设定var,范围为ActionContext:

<s:set var="adminPassword" value="password" scope="session"/>

set 使用#取值<s:property value="#adminPassword"/> 

set 从相应范围取值<s:property value="#session.adminPassword"/>

 

 

request里面也能取到adminName的值,但是在debug标签下的request没有找到adminName变量,原因是:它还没来得及把这个值放到request里面debug已经形成了,

所以判定它有没有应该取出来看一下。

 

Bean

<s:bean name=classpath>

<s:param name="name" values="duty"/>

<s:property value="name" />

相当于匿名类

</s:bean>

调用name属性 用#对象名.属性

<s:property value="#myDog.name" />

 

 

 

 

$:/user_success.jsp?t=${type} struts.xml

#:actioncontext的值

%:把文本强制解析为ognl

<s:set var="incPage" value="'/index.jsp'"/>

<s:include value="%{#incPage}"/>

 

 

 

Fielderror:

 

<s:fielderror fieldName="fielderror.test" ,theme="simple" />

 

 

 <s:subset source="myList" count="13" start="3">

        <s:iterator>

          <s:property />

        </s:iterator>

     </s:subset>

 

 

Struts 2拦截器

F:\SSH\struts-2.3.28-all\struts-2.3.28\docs\docs

Struts2-Architecture.png

struts-2.3.28-all\struts-2.3.28\src\core\src\main\java(查看源码配置的文件路径)

在前面我们已经初步使用过拦截器,下面继续细细探讨。

1.概述strust2中的拦截器

拦截器是Struts2框架的核心,它主要完成解析请求参数、将请求参数赋值给Action属性、执行数据校验、文件上传等工作。Struts2设计的灵巧性,拦截器起了关键性的作用,当需要扩展Struts2功能时,只需要提供对应拦截器,并将它配置在Struts2容器中即可;如果不需要该功能时,也只需要取消该拦截器的配置即可。   
Struts2内建了大量的拦截器,这些拦截器以name-class对的形式配置在struts-default. xml文件中,其中name是拦截器的名字,就是以后我们使用该拦截器的唯一标识;class则指定了该拦截器的实现类,如果我们定义的package继承了Struts2的默认struts-default包,则可以自由使用它下面定义的拦截器,否则必须自己定义这些拦截器。

2.自定义拦截器(工作后99%用不上,如果上班需要编写拦截器才能解决问题,100%设计的思路有问题,因为大多数拦截器已经帮你设计完了。如果用自定义拦截器那么将会与struts2绑定在一起,将来如果需要换其他框架,换不了.所以自定义拦截器不重要,理解了解原理就OK)。上班以后关注项目,分析 设计 应用,除了业务以外,所有的技术都是copy+update,就像做房子 设计好了以外都是砌砖.

自定义拦截器需要特别注意的是不要忘记引入struts2默认的拦截器。为了实现某些操作,我们可以自定义拦截器,自定义拦截器有三种方式定义。分别为实现Interceptor接口,继承抽象类AbstractInterceptor,继承MethodFilterInteceptor类。

方式一,实现Interceptor接口。

准备工作,新建struts2interceptor项目。构建一个登录环境:当我们点登录链接时,便成功登录(为了方便,这里不进行验证)。即在link.jsp页面中写如下链接:<a href="<%=request.getContextPath()%>/login.action">登录</a> 然后,我们点击此链接便可以登录。login.action在strutst.xml中的的配置如下:
<package name="interceptor"extends="struts-default">
    <action name="login" class="com.demo.action.LoginAction">

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

</action>
</package>
com.demo.action.LoginAction为了简单,com.demo.action.LoginAction总是返回SUCCESS;这样请求这个Action总会返回到.../success.jsp页面。
编写拦截器:MyInterceptor类,内容如下:
package com.demo.action;

import com.opensymphony.xwork2.ActionInvocation;

import com.opensymphony.xwork2.interceptor.Interceptor;

public class MyInterceptor implements Interceptor {

public void destroy() {

}

public void init() {

}

public String intercept(ActionInvocation invocation) throws Exception {

System.out.println("开始拦截");

String result = invocation.invoke();

System.out.println("结束拦截");

return result;

}

}
为了使用此拦截器,我们必须将此拦截器进行注册,随后再在要使用此拦截器的Action中引用。即首先在<package>中注册,内容如下:
<interceptors>

<interceptor name="myIpt" class="com.demo.action.MyInterceptor"></interceptor>

<interceptor-ref name="defaultStack"></interceptor-ref>(添加自定义拦截器后,struts2默认的拦截器会被覆盖,所以需要手动添加)

</interceptors>

注册完成后,如果我们要在login.action中使用此拦截器,只需要在<action>中增加如下内容:
<interceptor-ref name="myIpt"></interceptor-ref>

这样便成功为LoginAction配置了我们自定义的拦截器MyInterceptor,下面只需发布测试。

实例流程分析:当我们为LoginAction配置了拦截器时,并且有客户端请求此Action时,会首先被此拦截器拦住,然后执行System.out.println("开始拦截"),随后我们调用invocation.invoke()方法,它会把请求继续传递给下一个拦截器,下一个拦截器也会继续执行相应代码后再调用invoke方法继续传递,直到请求到达最后一个拦截器,它会把请求传递给Action,比如,我们这里只用到了一个拦截器,当它执行完成后,会把请求直接转交到LoginAction处理,LoginAction处理完成后,它会返回结果给MyInterceptor拦截器。

方式二、继承AbstractInterceptor抽象类

创建拦截器类MyAbstractInterceptor:主要代码如下:
package com.demo.action;
import com.opensymphony.xwork2.ActionInvocation;

import com.opensymphony.xwork2.interceptor.AbstractInterceptor;

public class MyAbstractInterceptor extends AbstractInterceptor {

public String intercept(ActionInvocation invocation) throws Exception {

System.out.println("Abs开始拦截");

String result = invocation.invoke();

System.out.println("Abs结束拦截");

return result;

}

}

然后注册此拦截器,在<interceptors>元素进行进行配置,内容如下:
<interceptor name="myAbs" class="com.demo.action.MyAbstractInterceptor"></interceptor>

随后再在LoginAction中引用此拦截器,即在<action name="login" ...>配置如下内容:
<interceptor-ref name="myAbs"></interceptor-ref>

最后发布测试。

方式三、继承MethodFilterInteceptor类

创建拦截器类MyMethodFilterInterceptor,主要代码如下:
package com.demo.action;

import com.opensymphony.xwork2.ActionInvocation;

import com.opensymphony.xwork2.interceptor.MethodFilterInterceptor;

public class MyMethodFilterInterceptor extends MethodFilterInterceptor{

protected String doIntercept(ActionInvocation invocation) throws Exception {

System.out.println("method开始拦截");

String result=invocation.invoke();

System.out.println("method结束拦截");

return result;

}

}

然后注册此拦截器,在<interceptors>元素进行进行配置,内容如下:
<interceptor name="myMet" class="com.demo.action.MyMethodFilterInterceptor">
</interceptor>

随后再在LoginAction中引用此拦截器,即在<action name="login" ...>配置如下内容:
<interceptor-ref name="myMet"></interceptor-ref>

最后发布测试。

分析:当配置到此,实质便为LoginAction配置了三个拦截器,当我们点击登录时会在控制台打印出如下语句:
开始拦截

Abs开始拦截

method开始拦截

--先执行拦截器,再执行此Action

method结束拦截

Abs结束拦截

结束拦截
其实当我们点击登录时,本来是要访问LoginAction,最后会把LoginAction的执行结果传递给访问者。但是当我们配置了拦截器时,当我们去访问Action时,会首先被拦截,随后拦截器执行一些操作后才会继续把请求传递下去。下面作图说明拦截流程:

结合现实理解:比如我们要去某楼层找某人(LoginAction)取一个资源(LoginAction处理后返回的结果,这里表现为success.jsp),(1)进楼层时会被大门保安拦截检查(第一个拦截器:MyInterceptor拦截器),(2)检查通过后再进电梯时会被电梯保安员检查(第二个拦截器:MyAbstractInterceptor拦截器),(3)检查通过后再上到某楼层会被楼层保安员检查(第三个拦截器:MethodAction拦截器

),(4)检查通过后会找到某个(LoginAction),并与某个交谈(LoginAction处理),随后某个人和我们会带着请求的资源出去,(5)出去时,会依次被楼层,电梯,大门保安员检查,最终检查通过。某个人把资源给我们(实质就是返回请求资源给客户端)。 其实拦截器的执行流程和过滤器差不多,所以我们不防用过滤器的眼光来看这些拦截器。 
注意:我们在为LoginAction配置拦截器时,都没使用默认的拦截器,是原因这里的测试可以不用,但是以后在我们使用自定义的拦截器是,一定要加上默认的拦截器,否则会导致许多不可预知的结果。
补充:从上面的图并结合代码,我们可以看出拦截器的核心过程应该是ActionInvocation对这些拦截器回调处理,下面我们建立com.demo.action.interceptor.simulation来模拟这一过程,具体的代码参源文件,在此略去。在此我们作图分析ActionInvocation的实现过程:

补充2:上面分别使用了三种方式来创建自定义的拦截器,第一种方式是最原始的实现方式,第二种方式的好处是我们可以不必重写所有的方法,较常用。第三种方式进行了扩展,下面将会展示它的扩展性能。

3. TokenInterceptor防止表单重复提交。(很少用)

由于某些原因,用户在进行类似表单提交的操作后,以为表单未被提交,会进行多次的重复提交。为了避免用户多次提交给服务器带来负荷。我们会对表单提交这样的操作进行一些处理,以告诉用户不要重复提交。下面我们建立struts2token项目,使用struts2token拦截器来实现此案例。
步骤一,编写login.jsp页面,内容如下:
<%@ page language="java" pageEncoding="UTF-8"%>

<%@ taglib uri="/struts-tags" prefix="s" %>

<html>

<body>

<form action="<%=request.getContextPath()%>/login.action>

姓名:<input type="text" name="username"><br>

密码:<input type="password" name="password"><br>

<input type="submit" value="登录">

<s:token></s:token>

</form>

</body>

</html>
说明,此登录页面中的关键技术就是使用了标签库中的<s:token></s:token>标签,它的作用就是在用户访问此页面时会生成一个sessionId,在提交时会服务器会据此验证表单是否已提交。“To set a token in your form, you should use the token tag. This tag is required and must be used in the forms that submit to actions protected by this interceptor”,这句话的大概意思就是我们必须要在提交的表单中使用这个token tag,这样提交到的Action便能配置TokenInterceptor拦截器验证表单是否重复提交。
步骤二,编写LoginAction,主要代码如下:
package com.demo.action;
public class LoginAction extends ActionSupport {

public String execute() throws Exception {

System.out.println("---->执行execute方法...");

return SUCCESS;

}

}
步骤三,struts.xml主要配置内容如下:
<struts>

<package name="tokenTest" extends="struts-default">

<action name="login" class="com.demo.action.LoginAction">

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

<result name="invalid.token">/subError.jsp</result>

<interceptor-ref name="token"></interceptor-ref>

<interceptor-ref name="defaultStack"></interceptor-ref>

</action>

</package>

</struts>

说明:在此Action下,我们配置了token拦截器,另注意到在此Action下我们还配置了一个“invalid.tokenresult,因为“This interceptor uses a fairly primitive technique for when an invalid token is found: it returns the result invalid.token, which can be mapped in your action configuration”。它的大概意思就是:提交时服务器如果根据token标签产生的sessionId判断出表单已提交,它则返回invalid.token指向的视图。比如这里,如果重复提交则会转到.../subError.jsp中去。另不要忘记了引入默认的拦截器栈。补充:关于token拦截器更多细节可以访问org.apache.struts2.interceptor.TokenInterceptor类的api说明。
步骤四,编写配置中所用到jsp页面,这些页面编写简单,在此省去。
步骤五、发布测试,请注意访问login.jsp页面时,查看源文件时会发现增加了两个隐藏域信息。
步骤六、更换拦截器:我们还可以使用tokenSession拦截器,它的功能比上面的增强,它能保证持有相同sessionId的并发请求等待第一个完成之后才能被提交处理,但是它返回的是action执行后的result.接着上例,我们只需要在配置中作如下修改:把上面的token拦截器改成<interceptor-ref name="tokenSession"></interceptor-ref> 即可。随后便可以测试,测试时会发现如果我们重复提交,它总是返回到上一次的success.jsp页面,但是它并不是经过LoginAction中的execute处理后返回(我们System.out.print语句在重复提交时并未打印出来),而是此拦截器判断出是重复后直接返回上一次提交转向的页面。

4.使用拦截器实现权限验证

为了说明此问题,我们建立struts2auth项目,流程图如下:

简短说明:当我们访问main.jsp页面,并试图通过此页面中的链接地址:note.action来访问到.../WEB-INF/note.jsp页面时,由于访问的note.action配置了拦截器,所以会被拦截,如果拦截器判断登录则可以访问,否则会跳到登录页面。如果我们从登录页面直接到main.jsp页面,再来访问note.action时,同样被拦截但是由于登录过,所以可以访问到此action对应的内容。由这里的分析可以看出关键点就登录成功时给出标志提供给拦截器判断是否成功登录。
步骤一,搭建好相关的开发环境,并准备好登录页面login.jsp,代码如下:
<form action="<%=request.getContextPath()%>/login.actionmethod="post">

姓名:<input type="text" name="username"><br>

密码:<input type="password" name="password"><br>

<input type="submit" value="登录">

</form>
步骤二,建立相应的Action:LoginAction。代码如下:
package com.demo.action;

public class LoginAction extends ActionSupport {

private String username;

Map session;

public String execute() throws Exception {

if(username.equals("admin")){

session = ActionContext.getContext().getSession();

session.put("loginSign", "loginSuccess");

return SUCCESS;

}else{

return LOGIN;

}

}
...省略usernameget/set方法

}

说明:我们这里是设定了只有登录用户名为admin时,此Action才设置登录标志。另这里获取Session对象采取的是“与Servlet解耦合的非IOC方式”。
步骤三,编写拦截器类,代码如下:
package com.demo.action.interceptor;

public class AuthInterceptor extends AbstractInterceptor {

public String intercept(ActionInvocation invocation) throws Exception {

Map session = invocation.getInvocationContext().getSession();

// session=ActionContext.getContext().getSession();

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

return "login";

} else {

String result = invocation.invoke();

return result;

}

}

}

步骤四,配置此Action相关,主要配置内容如下:
<struts>

<package name="tokenTest" extends="struts-default">

<interceptors>

<interceptor name="auth"

class="com.demo.action.interceptor.AuthInterceptor">

</interceptor>

<interceptor-stack name="authStack">

<interceptor-ref name="auth"></interceptor-ref>

<interceptor-ref name="defaultStack"></interceptor-ref> 

</interceptor-stack>

</interceptors>

<action name="login" class="com.demo.action.LoginAction">

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

<result name="login">/login.jsp</result>

</action>

 

<action name="note">

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

<result name="login">/login.jsp</result>

<interceptor-ref name="authStack"></interceptor-ref>

</action>

</package>

</struts>

说明:结合前面的一些代码来看,当我们为note.action配置了前面写所的AuthInterceptor拦截器时,如果我们要访问note.action,拦截器会首先判断是否登录,如果登录则继续把请求传递下去,如果没有登录则会返回到登录页面。
步骤五、编写相关的其它jsp页面,然后发布测试。此实例应重点是进一步掌握拦截器的配置使用。作为“实现资源权限访问”,此实例不具参考价值。

 

5.使用来MethodFilterInterceptor灵活拦截(课下了解)

步骤一、建立MethodAction,代码如下:
package com.demo.action;

import com.opensymphony.xwork2.ActionSupport;

public class MethodAction extends ActionSupport{

public String m1(){

return SUCCESS;

}

public String m2(){

return SUCCESS;

}

public String m3(){

return SUCCESS;

}

}

步骤二、注册此Action,并为此Action配置拦截器。配置内容如下:
<action name="*_*" class="com.demo.action.MethodAction" method="{2}">

<result name="success">/{2}Suc.jsp</result>

<interceptor-ref name="myMet">

</interceptor-ref>

</action>

我们为此Action配置了前面写的MyMethodFilterInterceptor拦截器,并在link.jsp中增加如下链接:
<a href="<%=request.getContextPath()%>/Method_m1.action">m1</a><br>

<a href="<%=request.getContextPath()%>/Method_m2.action">m2</a><br>

<a href="<%=request.getContextPath()%>/Method_m3.action">m3</a><br>

当点m1时会访问到m1Suc.jsp页面, 点m2、m3会分别访问到m2Suc.jsp、m3Suc.jsp页面。现在假如我们想访问m2、m3时不被拦截,我们只需修改MyMethodFilterInterceptor注册:修改内容为:
<interceptor name="myMet" class="com.demo.action.MyMethodFilterInterceptor">

<param name="excludeMethods">m2,m3</param>
</interceptor>
它的作用和增加<param name="includeMethods">m1</param>等价。上面是指定m2,m3方法调用时不被拦截,这里是指定只拦截m1。除了这种在注册拦截器时指定拦截外,还可以在引用拦截器时指定,即如下形式:
<interceptor-ref name="myMet">

<param name="excludeMethods">m2,m3</param>

<param name="includeMethods">m1</param>
</interceptor-ref>
上面的两处<param>配置是等价的,但是如果〈param〉配置冲突,谁起作用?即如果我们对m1配置了excludeMethods同时又配置了includeMethods时,谁起作用,我们可以进行这些冲突的验证。以下是验证结果:
引用配置(在Action引用拦截器时配置)时,以includeMethods的配置为准。一旦我们为拦截器使用了<param>配置,而对m1这样的方法不配置任何,就不会被拦截。但是如果不使用<param>,它们全部都要被拦截。
注册配置时(在注册拦截器时配置),情况和“引用配置”完全一样。
引用配置和注册配置冲突时,以引用配置为准。

6.使用默认的execAndWait拦截器(课下了解)

当我们进行数据库查询等相关的操作时,如果服务器负荷过重可能不能及时把数据查询出来,进而会在状态拦显示“正在打开...”,但却一直转不到相关的页面,这将给客户端带来不便,甚于很多人会因此不愿使用网站的所有服务。对此我们可以在客户提交时,马上转到一个页面,并在该页面显示“您的请求已提交,服务器正在查询,请等待...”的内容,这样客户将不会陷于无赖的等待中。 对于此要求,struts2可以轻松帮我们完成。下面新建struts2wait项目演示此实例。
建立LoginAction,代码如下:
package com.demo.action;
public class LoginAction extends ActionSupport {

public String execute() throws Exception {

Thread.sleep(5000);

return SUCCESS;

}

}

说明:为了模拟服务器负荷过重,查询时间要很长。我们在使用了线程休眠的方式。

随后配置此Action,配置的主要内容如下:

<action name="login" class="com.demo.action.LoginAction">

<interceptor-ref name="defaultStack"></interceptor-ref>

<interceptor-ref name="execAndWait"></interceptor-ref>

<result name="wait">/wait.jsp</result>

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

注意:在配置前我们先是使用了默认的拦截器,再此强调在我们为Action配置拦截器时,应该总是配上默认的拦截器。随后我们使用了execAndWait拦截器,如需要配置此拦截器,此拦截器一定要配置在最后,否则会出现一些难预知的结果。如果使用此拦截器,我们通常还会配置wait的result结果集,因为“On the initial request or any subsequent requests (before the action has completed), the wait result will be returned. The wait result is responsible for issuing a subsequent request back to the action, giving the effect of a self-updating progress meter”,大概意思就是当我们请求的Action在未执行完,就是未返回结果时,会首先把wait result返回,而在wait result所指定的页面中通常会再次发送请求给原始的Action。所以wait.jsp的主要内容如下:
<head>

<meta http-equiv="refresh" content="1;login.action">

</head>

<body> 查询请求已提交,正在查询数据,请等待... </body>

在此页面中,我们指定了每隔1秒便发送请求到login.action中去。这样,客户端便可以及时获取查询结果。结合此实例,我们简要分析流程:当我们发出请求到此Login.Action中去时,首先会被exeAndWait拦截器拦截到,这样它便跳转到wait.jsp页面,在wait.jsp页面中每隔1秒我们会继续发送此Action的请求,当再次请求到达LoginAction时,如果它已经返回,则会跳到此Action返回的页面,如果LoginAction未返回,则继续停留在wait.jsp中,再隔1秒又再次发送请求到LoginAction中去。

其实如果服务器能很快查询出结果,我们则不需要用到wait.jsp页面,我们只需在<interceptor-ref name="execAndWait"></interceptor-ref>中增加如下一段配置:
<param name="delay">6000</param> 这样便延迟请求到达wait.jsp页面,这样当请求到达时它会在LoginAction中执行6秒时间再到wait.jsp,而6秒LoginAction足以执行完并返回结果,所以当拦截器

执行时首先检查到此Action已经返回结果。则拦截器会直接用此返回页面,如果此时发现LoginAction并未执行完,它便会把wait resutl指定的页面返回。需要说明的是,通常我们设定的延迟最多一秒,这里为了演示,设置的很长。图示此拦截器原理:
 
关于此拦截器的详细的配置及文档说明可以参看ExecuteAndWaitInterceptor类的api信息。

 

Struts 2框架的文件上传和下载(了解

首先建立struts2UpDownLoad项目,搭建好struts2基本的开发环境。

1. 上传实例

2. 步骤一:upload.jsp代码如下:
<form action="upload" method="post" enctype="multipart/form-data">

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

<input type="submit" value="上传">

<input type="reset" value="取消">

</form>
注意:在form标签中我们用到了enctype实体,这是上传时必须用到得。
步骤二,建立struts.xml。对upload.action的配置如下:
<action name="upload" class="com.demo.action.UploadAction">

<param name="savePath">img</param>

<result>/upSuc.jsp</result>

<result name="input">upload.jsp</result>

<interceptor-ref name="defaultStack">

<param name="fileUpload.maximumSize">300000</param>

<param name="fileUpload.allowedTypes">

image/bmp,image/jpeg,image/gif

</param>

</interceptor-ref>

</action>

在这里唯一需要说明的是<interceptor-ref>下的参数问题,在以前如果要为某个特定的拦截器传递参数需要在<interceptor>下配置pararm参数,在此处我们用.形式来配置fileUpload拦截器的参数。这用做即可以保证默认的拦截器栈起作用,也可以向fileUpload拦截器传递参数。第一个参数是限制上传图片的大小(除了可以这样限制图片大小,也可以配置一个常量的方法来限制上传文件的大小,配置的内容为<constant name="struts.multipart.maxSize" value="文件大小"/>),第二个参数是限制上传图片的格式只能为bmp,pjpeg,gif关于这些参数可以参看fileupload拦截器对应类的api文档。

另还需注意:在action下的“<param name="savePath">img</param>”代码可以为UploadAction的savePath字段传递值,这样作的好处是在项目正式发布后,我们可以通过修改struts.xml中的配置来灵活给savePath赋值。而如果直接在java源代码中初始化savePath的值,在项目运行后就不能简单修改。这种用法主要是为了给客户提供一个灵活可配的特定初始化方式。
步骤三、编写UploadAction,主要代码如下:
package com.demo.action;

 

import java.io.*;

import org.apache.struts2.ServletActionContext;

import com.opensymphony.xwork2.ActionSupport;

 

public class UploadAction extends ActionSupport {

private static final long serialVersionUID = 4223737080271920987L;

private String savePath;

private File file;//名称要与表单中的名字一致

private String fileFileName;//名字要用上面的名+XX

private String fileContentType; //名字要用上面的名+XX

 

public String execute() throws Exception {

String path = ServletActionContext.getServletContext().getRealPath(

savePath);

String savaFileName = path + "\\" + fileFileName;

// System.out.println(savaFileName);

 

BufferedInputStream bis = null;

BufferedOutputStream bos = null;

try {

bis = new BufferedInputStream(new FileInputStream(file));

bos = new BufferedOutputStream(new FileOutputStream(savaFileName));

 

byte[] buf = new byte[(int) file.length()];

int len = 0;

while ((len = bis.read(buf)) != -1) {

bos.write(buf, 0, len);

}

} catch (Exception e) {

e.printStackTrace();

} finally {

if (bis != null)

bis.close();

if (bos != null)

bos.close();

}

return SUCCESS;

}

 

public String getSavePath() {

return savePath;

}

 

public void setSavePath(String savePath) {

this.savePath = savePath;

}

 

public File getFile() {

return file;

}

 

public void setFile(File file) {

this.file = file;

}

 

public String getFileFileName() {

return fileFileName;

}

 

public void setFileFileName(String fileFileName) {

this.fileFileName = fileFileName;

}

 

public String getFileContentType() {

return fileContentType;

}

 

public void setFileContentType(String fileContentType) {

this.fileContentType = fileContentType;

}

 

}

说明:其实上传的难点就是在此action的处理上。首先是从配置文件中读取文件的保存路径,然后联合fileFileName命名规则是上传的文件对应的字段名+FileName,如果要得到上传文件的类型,固定的写法应是上传的文件对应的字段名+ContentType,比如这里应为fileContentType来确定完整的保存路径,并最终为创建BufferedOutputStream作准备。BufferedInputStream是通过前台upload.jsp页面传递的file构建。特别要注意处理流,如果书写不当可能会使上传文件循环写入,使得硬盘容量不够。还要注意对流的关闭问题。 补充:关于文件的操作可以使用commons-io.jar包的FileUtils类下的copyFile方法来进行文件的拷贝,比如这里调用copyFile方法(file,要保存的目录)

上传成功后,upSuc.jsp的主要内容如下:
<body>

<h4>上传成功,以下是你刚上传的图片:</h4>
<img src="<%=request.getContextPath() %>/<s:property value="savePath+'/'+fileFileName"/>"> <br>

保存路径为:<s:property value="savePath+'/'+fileFileName"/>

 </body>

说明:当上传成功后,会显示上传的图片。

扩展实例:如果想上传多个文件,可以在Action中使用File[] files来接受上传的文件(jsp中对应的上传文件的参数均应为files)。对应的上传文件的名字,使用String[] fileFileName。然后循环files数组保存File文件对象。

2.下载实例

下载页面的doload.jsp的主要代码如下:
<a href="download.action?downloadName=img/a.bmp">下载图片</a><br>

 <br>
对应的download action配置如下:

<action name="download" class="com.demo.action.DownloadAction">

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

<!-- -->

<param name="contentType">image/gif</param>

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

<param name="contentDisposition">attachment;filename="Struts2.gif"</param>

</result>

</action>
说明:

contentType属性用来指定下载文件的类型,同样可以在Tomcat6.0.20//conf//web.xml文件中查看MIME Type Mappings

type类型指明了结果集为流类型,并且为流类型结果集配置了参数,

inputName指定流的来源,这里来源为targetFile,所以在下面的Action中有getTargetFile方法。

contentDisposition用来设定显示的文件名,这是在点击下载链接时显示在提示框中的文件名,同时它的值的filename=" "所设定的name值也是下载到本地之后的文件的名字,并且它的写法是固定的filename=" ",这是由HTTP协议所规范的

attachment无论是什么类型的文件,哪怕是TXT,它也会弹出一个下载框,供用户选择打开或下载

DownloadAction的主要代码如下:
package com.demo.action;

public class DownloadAction extends ActionSupport {

private String downloadName;

public String execute() throws Exception {

return SUCCESS;

}

 

public InputStream getTargetFile(){

return ServletActionContext.getServletContext().getResourceAsStream(downloadName);

}

 

public void setDownloadName(String downloadName) {

this.downloadName = downloadName;

}

}
说明:下载实例在此略作了解,具体可以借助apache组织提供的上传下载开源项目理解。

Struts 2的验证框架

注意:要想实现校验,action必须继承自ActionSupport类。

1.基于手工编码的校验

我们建立struts2validate项目 ,其中reg.jsp页面主要代码如下:

<body>

  <s:head/>

<h3>注册页面</h3>

<s:form method="post" action="reg" >

<s:bean name="com.demo.action.AgeAction" id="aa"></s:bean>

<s:textfield name="user.username" label="用户名"/>

<s:property value="errors.user.username"/>

<s:password name="user.password" label="密码"/>

<s:password name="user.password2" label="确认密码"/>

<s:select list="#aa.ageMap" name="user.age" label="年龄" headerValue="填写真实年龄"  headerKey="0"/>

<s:reset value="重置" align="left" />

<s:submit value="注册" align="left"/>

</s:form>

</body>
说明:<s:head/>可以用来对验证信息进行一些美化效果处理,另在此页面中我们用到了一个AgeAction用来动态生成“年龄”表单项,在前面的表单标签中已用过类似的做法。AgeAction的代码如下:

package com.demo.action;

public class AgeAction extends ActionSupport {

private Map<Integer, String> ageMap;

public AgeAction() {

ageMap = new HashMap();

for (int i = 1; i <= 120; i++) {

ageMap.put(new Integer(i), i + "");

}

}

...省略ageMapget/set方法

}

Reg action的配置如下:

<package name="validate" extends="struts-default">

<action name="reg" class="com.demo.action.RegAndLoginAction" method="reg">

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

<result name="login">/reg.jsp</result>

</action>
</package>

根据配置,我们来看它的对应Action: RegAndLoginAction,代码如下:

package com.demo.action;

public class RegAndLoginAction extends ActionSupport {

private User user;

public String reg() throws Exception {

if (user.getUsername() == null || user.getUsername().equals("")) {

this.addFieldError("user.username", "用户名不能为空");

} else if (!Pattern.matches("^[a-zA-Z][a-zA-Z0-9_]{3,14}$", user.getUsername())) {

this.addFieldError("user.username", "用户名只能是以字母开头,后面可以跟字母、数字或下滑线,长度只能是4-15");

} else if (user.getPassword() == null || user.getPassword().equals("")) {

this.addFieldError("user.password", "密码不能为空");

} else if (!user.getPassword().equals(user.getPassword2())) {

this.addFieldError("user.password2", "两次输入的密码不一致,请重新输入");

} else if (user.getAge() < 16) {

this.addFieldError("user.age", "未满16岁,不能注册");

}

 

if (this.hasFieldErrors()) {

return LOGIN;

}

System.out.println("reg success....");

return SUCCESS;

}

...省略userget/set方法

}
说明:当reg.jsp提交给此Action对应的reg方法处理时,它会调用addFieldError把错误信息加到FiledError中去,关于这点,我们可以在前台reg.jsp页面中用<s:debug>调试时,可以看到值栈中的此Action对象中的fieldErrors对应着我们添加的错误信息,因此这点也就为我们取出验证信息提供一个参考,即是说我们可以取出此验证信息,对它进行美化处理,而不是按struts2默认来显示。 后面,我们接着对登录页面用login方法进行了类似的验证(在此省略),所以此action取名为regAndLoginAction.
补充:当我们把login.jsp页面的验证写完后,可以发现reglogin这两个方法显示相当的繁琐,对此我们可以专门把验证分别放在validateRegvalidateLogin方法中去。我们新建一个Action来演示,新的RegAndLogin2Action主要代码如下:
package com.demo.action;

public class RegAndLogin2Action extends ActionSupport {

private User user;

 

@Override

public void validate() {

System.out.println("校验的统一,对所有方法进行校验:这里可以放一些公共的验证");

}

 

public void validateReg() {

...省略,对reg方法进行验证

}

 

public void validateLogin() {

...省略,对login方法进行验证

}

 

public String reg() throws Exception {

System.out.println("reg success....");

return SUCCESS;

}

 

public String login() throws Exception {

System.out.println("login success....");

return SUCCESS;

}
...省略userget/set方法

}

说明:当reg.jsp提交给此Action对应的reg方法处理时,它会首先调用此reg方法专属的验证方法valiadteReg(注意取名规则:validate+方法名<首字母大写>),此方法验证完成后,会调用validate方法,此方法完成后才会调用reg方法。因此一般情况下,我们会把一些公共的验证放在validate方法中,而这些所有的验证方法也只进行验证处理,并把错误信息封装到fieldError字段中(或者其它字段)。reg这些真正执行的方法只进行一些其它处理(比如把注册信息写进数据库)。测试时需要修改把前面的配置注释掉,写上下面的配置:
<action name="login" class="com.demo.action.RegAndLogin2Action"method="login">

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

<result name="input">/login.jsp</result>

</action>

<action name="reg" class="com.demo.action.RegAndLogin2Action" method="reg">

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

<result name="input">/reg.jsp</result>

</action>
说明:配置中有一个input result的配置,因为带有validate的方法进行验证时,如果验证失败,会返回input所对应的result结果集。

简析校验流程

(1)类型转换器请求参数执行类型转换,并把转换后的值赋给action中属性。

(2)如果在执行类型转换过程中出现异常,系统会将异常信息保存到ActionContext,conversionError拦截器将异常信息添加到fieldErrors里,不管类型转换是否出现异常都会进入第(3)步。

(3)系统通过反射技术调用action中的validateXxx()方法

(4)再调用action中的validate()方法

(5)经过上面4步,如果系统中的fieldErrors存在错误信息(即存放错误信息的集合size大于0),系统自动将请求转发至名为in    put的视图。如果系统中的fieldErrors没有任何错误信息,系统将执行action中的处理方法。

注意:经过以上过程的分析,可以知道如果类型转换失败,也会到input视图。

2.基于XML配置形式的校验

新建struts2validateXML项目,在此项目中,基本的代码和上面的struts2validate项目相似,只是在上一个项目中我们在Action的具体方法中进行了验证处理,现在先修改RegAndLoginAction的代码如下:
package com.demo.action;

public class RegAndLoginAction extends ActionSupport {

private User user;

...省略userget/set方法

}

下面我们在action所在的包下建立一个对此Action进行校验的xml文件,文件名为:RegAndLoginAction-validation.xml,取名原则就是actionClassName-validation.xml 。它会对此Action中的所有方法进行校验。主要代码如下:

参考  struts-2.3.28/docs/docs/validating-input.html(表头有问题
<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE validators PUBLIC "-//Apache Struts//XWork Validator 1.0.2//EN"

"http://struts.apache.org/dtds/xwork-validator-1.0.2.dtd">

 

<validators>

 

<field name="user.username">

<field-validator type="requiredstring">

<message>用户名不能为空</message>

</field-validator>

<field-validator type="regex">

<param name="regex">^[a-zA-Z][a-zA-Z0-9_]{3,14}$</param>

<message>

用户名只能是以字母开头,后面可以跟字母、数字或下滑线,长度只能是4-15位

</message>

</field-validator>

</field>

 

<field name="user.password">

<field-validator type="requiredstring">

<message>密码不能为空</message>

</field-validator>

</field>

</validators>

进行此配置,相当于在RegAndLoginAciton中增加用validate ()方法进行验证。如果我们想对某个方法进行验证,配置文件应取名为actionClassName-ActionName-validation.xml,比如我们对reg方法进行验证,在前面用到validateReg方法,这里只需增加RegAndLoginAction-reg-validation.xml配置文件即可,它的作用和validateReg方法相同,在此省略此配置文件内容。
关于验证的配置文件中用到的验证类型可以参看文档或者叁看压缩包中的配置参照文件,下面对校验器类型进行简单说明:

Required-必须校验器:要求field的值不能为null

Requiredstring-必须字串校验器:不能为null,用长度大于0,默认情况下会对字串去前后空格

int、[long、short、double]:整数值[long型、短整形、double型]型值必须在指定范围。参数min指定最小值,参数max指定最大值

date-日期校验器:日期校验类型,符合日期格式,用可以使用min/max来指定日期范围

expression-OGNL表达式校验器:expression参数指定ognl表达式,该逻辑表达式基于值栈进行求值,返回true时校验通过,否则不通过,该校验器不可用在字段校验器风格的配置中

fieldexpression-字段ognl表达式校验器:要求field满足一个ognl表达式,expression参数指定ognl表达式,该逻辑表达式基于值栈进行求值,返回true校验通过,否则不通过

email-邮件地址校验器:非空用为合法的邮件地址

url-网址校验器:非空用为合法的url地址

visitor-复合属性校验器:它指定一个校验文件用于校验复合属性中的属性

conversion-转换校验器:指定在类型转换失败时,提示的错误信息

stringlength-字符器长度校验器:要求字段必须在指定的范围内,否则校验失败。minLength参数指定最小长度,maxLength参数指定最大长度。Trim参数指定校验field之前是否去除字串前后的空格

regex-正则表达式校验器:校验字段是否与expression参数指定的正则表达式匹配。caseSensitive参数指定进行匹配时是否区分大小写,默认为true,即区分大小写。

补充:基于xml校验的一些特点

当为某个Action提供了ActionClassName-validation.xml和ActionClassName-ActionName-validation.xml两种规则的校验文件时,系统会按下面的顺序寻找校验文件:(1)ActionClassName-validation.xml (2)ActionClassName-ActionName-validation.xml

系统寻找到第一个校验文件时还会继续搜索后面的校验文件,当探索到所有校验文件时,会把校验文件里的所有校验规则汇总,然后全部应用于action方法的校验。如果两个校验文件中指定的校验规则冲突,则会只使用后面文件中的校验规则。

当action继承了另一个action,父类action的校验文件会先被搜索到。

假定UserAction继承BaseAction:

<action name="user" class="com.demo.action.UserAction" method="execute">访问上面的action,系统会先搜索父类的校验文件:BaseAction-validation.xml,BaseAction-user-validation.xml,接着搜索子类的校验文件:UserAction-validation.xml,UserAction-user-validation.xml.应用于上面action校验规则为四个文件的总和。


国际化

尽管国际化不是重点内容,但是也有必要了解它的使用。在struts2中国际化有三种级别:分别是针对某个Actionaction级别,针对packagepackage级别,针对webappwebapp级别。下面我们建立struts2i18n项目来演示国际化在struts2中的使用。

1.action级别下的国际化2. .配置package的资源文件3. app级别的资源文件

演示app级别的资源文件

步骤一、首先是建立login.jsp及LoginAction,由于它们经常使用,在此省去它们的代码。
步骤二、建立资源文件,由于LoginAction在com.demo.action包中,所以我们应在com.demo.action包下我们建立两个资源文件:一个是中文kevin_zh_CN.properties、一个是英文kevin_en_US.properties。注意它们的名字相对固定,前面与Action的名字相同,后面是语言和国家代码。
英文资源文件内容如下:
login.username=username:

login.password=password:

login.login=login

welcome.msg=welcome\:{0}

 

中文资源文件

login.username=用户名:

login.password=密码:

login.login=登录

welcome.msg=欢迎你\:{0}

 

需要特别注意:我们应使用Myeclipse自带的MyEclipse properties Editer编辑器来打开此资源文件,并在properties视图下进行编辑,这样它会把中文进行编码(我们切换到source视图下可以看到经编码后的中文)。 这一步非常重要,否则会出现乱码。
步骤三,修改login.jsp中的内容:
  <body>

  <form action="/Login-login" method="post">

    <s:textfield key="login.username"></s:textfield>

  <s:property value="%{(getText('login.username'))}"/> <input name="username" />

  <s:property value="getText('login.password')"/><input name="password" type="password" />

  <input type="submit" value="<s:property value="getText('login.login')"/>" />

  </form>

  <s:debug></s:debug>

  <a href="<%=path %>/lang?request_locale=en_US">en</a>

  <a href="<%=path %>/lang?request_locale=zh_CN">cn</a>

  

  </body>

说明:对资源文件的引用,我们采取了两种方式:有的是通过在label中使用%{getText('资源文件中的key')}这样的形式,有的是通过key=资源文件中的key这种形式。

 

 

随后再在struts.xml中配置如下内容:
<constant name="struts.devMode" value="true"></constant>

<constant name="struts.custom.i18n.resources" value="kevin"></constant>

    <package name="admin" namespace="/" extends="struts-default" >

   <action name="login">

   <result>/Login-input.jsp</result>

   </action>

   <action name="lang" class="com.demo.action.LangAction">

       <result>/Login-input.jsp</result>

       </action>

</package>

加载资源文件的方式
(1)加载全局资源文件: <constant name="struts.custom.i18n.resources" value="kevin"/>
(2)包范围资源文件 :为Struts2指定包范围资源文件的方法是,在包的根路径下建立多个文件名为package_language_country.properties的文件,一旦建立了
    这个系列的国际化资源文件,应用中处于该包下的所有Action都可以访问该资源文件。需要注意的是上面的包范围资源文件的baseName就是package
不是Action所在的包名。
(3)Action范围资源文件:在Action类文件所在的路径建立多个文件名为ActionName_language_country.properties的文件。
(4)临时指定资源文件:<s:i18n.../>标签的name属性指定临时的国际化资源文件

 

 

配置文件参数的使用:
<a href="<%=request.getContextPath() %>/login!goLogin.action">登录</a>

直接访问Action中的方法 格式:doX(大写)xxx ---- ActionName!x(小写)xxx.action  注意此方法和前面二.7中相关方法的区别。 我们通过此Action跳转到login.jsp这样便能成功访问到login.jsp页面。
步骤五、在success.jsp中使用资源文件,主要内容如下:
<s:text name="login_suc">

<s:param value="%{username}"></s:param>

</s:text>

说明:在前面的资源文件中,我们配置了login_suc=Welcome {0},其中{0}表示占位参数,这里我们使用<s:param>来给此参数赋值。
步骤六、测试:在ie的internet选项中改变语言实现国际化的访问。

2.配置package的资源文件(课后了解)

同样在建立com.demo.action包下建立两个资源文件(package级别的资源文件名必须以package开头):取名为:package_zh_CN.properties,它的内容为:pack=pack属性值 package_en_US.properties,它的内容为:pack=packageAttributeValue 

然后再在login.jsp页面中增加如下内容:
<h4>测试包资源文件</h4>

<s:text name="pack"></s:text>

这样便完成了package级别的资源文件配置,最后发布测试。

说明:action级的资源文件优先级别最高,app最低。Pack级别的资源文件可作用于同一个包,app级别的资源文件可作用于当前项目。

补充在jsp页面中直接访问某个资源文件,struts2为我们提供了i18n标签,使用此标签我们可以在类路径下直接从某个资源文件中获取国际化数据,而无需任何配置:

<s:i18n name="XXX"> --xxx为类路径下资源文件的基名

<s:text name="">

<s:param></s:param>

</s:text>

</s:i18n>

而如果要访问的资源文件在类路径的某个包下(如action或package级别的资源文件),可以这样访问:

<s:i18n name="com/demo.action/资源文件基名">--com.demo.action为包名

4.使用资源文件的原理 

我们建立ReadResourceFileTest类,代码如下:

package com.demo.action;

import java.util.Locale;

import java.util.ResourceBundle;

public class ReadResourceFileTest {

public static void main(String[] args) {

ResourceBundle rb=ResourceBundle.getBundle("com.demo.action.LoginAction", Locale.US);

System.out.println(rb.getString("login_suc"));

}

}

补充:在Action类(必须继承自ActionSupport)中获取资源文件的值的方法,可以使用如下代码:

String value = this.getText("资源文件的键名");  

//获取资源文件的对应的值。如果想给资源文件中的占位符赋值,可以使用getText的重载方法。

ActionContext.getContext().put("XXX",value);//存放在request范围,供jsp获取此值

 

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值