本章要点

—  Struts 2的下载和安装

— 纯手工创建一个Web应用

— 纯手工创建一个Struts 2应用

— 实现Struts 2的Action

— 配置Struts 2的Action

— 在Action中访问HttpSession

— 在JSP中输出Action的返回值

— 使用Struts 2的表单标签

— 程序国际化初步

— 数据校验初步

前面已经简要介绍了Struts 2的起源,以及Struts 2的两个前身:Struts 1和WebWork,并详细对比了Struts 2和Struts 1的差异,对比了Struts 2和WebWork的差异,而且指出:Struts 2是WebWork的升级,而不是Struts 1的升级。

虽然Struts 2提供了与Struts 1的兼容,但已经不是Struts 1的升级。对于已有Struts 1开发经验的开发者而言,Struts 1的开发经验对于Struts 2并没有太大的帮助;相反,对于已经有WebWork开发经验的开发者而言,WebWork的开发经验对Struts 2的开发将有很好的借鉴意义。

下面将以一个Struts 2的HelloWorld应用为例,介绍Strust 2 MVC框架如何拦截用户请求,如何调用业务控制器处理用户请求,并介绍Action处理结果和资源之间的映射关系。

本HelloWorld应用是一个简单的应用:用户进入一个登录页面,允许用户输入用户名、密码,如果用户输入的用户名和密码符合要求,则进入一个欢迎页面;如果用户输入错误,则进入一个提示页面。当用户提交表单时,本应用会有基本的数据校验。

2.1  下载和安装Struts 2框架

下面我们从下载、安装Struts 2开始,慢慢开始体验Struts 2 MVC框架的魅力。

笔者写本书的时候,Struts 2已经发布了其产品化GA(General Availability)版,其实最新的产品化GA版是Struts 2.06,故本书的所有应用都是基于该版本的Struts 2。建议读者下载Struts 2.06版,而不是下载最新的Beta版,如果Struts 2有最新的GA版,读者也可以下载更新的GA版,相信不会有太大差异。

下载和安装DWR请按如下步骤进行。

 登录http://struts.apache.org/download.cgi#Struts206站点,下载Struts 2的最新GA版。在Struts 2.06下有如下几个选项:

—  Full Distribution:下载Struts 2的完整版。通常建议下载该选项。

—  Example Applications:下载Struts 2的示例应用,这些示例应用对于学习Struts 2有很大的帮助,下载Struts 2的完整版时已经包含了该选项下全部应用。

—  Blank Application only:仅下载Struts 2的空示例应用,这个空应用已经包含在Example Applications选项下。

—  Essential Dependencies:仅仅下载Struts 2的核心库,下载Struts 2的完整版时将包括该选项下的全部内容。

—  Documentation:仅仅下载Struts 2的相关文档,包含Struts 2的使用文档、参考手册和API文档等。下载Struts 2的完整版时将包括该选项下的全部内容。

—  Source:下载Struts 2的全部源代码,下载Struts 2的完整版时将包括该选项下的全部内容。

—  Alternative Java 4 JARs:下载可选的JDK 1.4的支持JAR。下载Struts 2的完整版时将包括该选项下的全部内容。

通常建议读者下载第一个选项:下载Struts 2的完整版,将下载到的Zip文件解压缩,该文件就是一个典型的Web结构,该文件夹包含如下文件结构:

—  apps:该文件夹下包含了基于Struts 2的示例应用,这些示例应用对于学习者是非常有用的资料。

—  docs:该文件夹下包含了Struts 2的相关文档,包括Struts 2的快速入门、Struts 2的文档,以及API文档等内容。

—  j4:该文件夹下包含了让Struts 2支持JDK 1.4的JAR文件。

—  lib:该文件夹下包含了Struts 2框架的核心类库,以及Struts 2的第三方插件类库。

—  src:该文件夹下包含了Struts 2框架的全部源代码。

 将lib文件夹下的Struts2-core-2.0.6.jar、xwork-2.0.1.jar和ognl-2.6.11.jar等必需类库复制到Web应用的WEB-INF/lib路径下。当然,如果你的Web应用需要使用Struts 2的更多特性,则需要将更多的JAR文件复制到Web应用的WEB-INF/lib路径下。如果需要在DOS或者Shell窗口下手动编译Struts 2相关的程序,则还应该将Struts2-core-2.0.6.jar和xwork-2.0.1.jar添加到系统的CLASSPATH环境变量里。

 提示  大部分时候,使用Struts 2的Web应用并不需要利用到Struts 2的全部特性,因此没有必要一次将该lib路径下JAR文件全部复制到Web应用的WEB-INF/lib路径下。

 编辑Web应用的web.xml配置文件,配置Struts 2的核心Filter。下面是增加了Struts 2的核心Filter配置的web.xml配置文件的代码:

<?xml version="1.0" encoding="GBK"?>

<!-- web-app是Web应用配置文件的根元素,指定Web应用的Schema信息 -->

<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.

com/xml/ns/j2ee/web-app_2_4.xsd">

<!-- 定义Struts 2的FilterDispatcher的Filter -->

<filter>

<!-- 定义核心Filter的名字 -->

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

<!-- 定义核心Filter的实现类 -->

<filter-class>org.apache.Struts2.dispatcher.FilterDispatcher

</ filter-class>

</filter>

<!-- FilterDispatcher用来初始化Struts 2并且处理所有的Web请求 -->

<filter-mapping>

<filter-name>Struts2</filter-name>

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

</filter-mapping>

</web-app>

经过上面3个步骤,我们已经可以在一个Web应用中使用Struts 2的基本功能了,下面将带领读者进入Struts 2 MVC框架的世界。

2.2  从用户请求开始

Struts 2支持大部分视图技术,当然也支持最传统的JSP视图技术,本应用将使用最基本的视图技术:JSP技术。当用户需要登录本系统时,用户需要一个简单的表单提交页面,这个表单提交页面包含了两个表单域:用户名和密码。

下面是一个最简单的表单提交页面,该页面的表单内仅包含两个表单域,甚至没有任何动态内容,实际上,整个页面完全可以是一个静态HTML页面。但考虑到需要在该页面后面增加动态内容,因此依然将该页面以jsp为后缀保存。下面是用户请求登录的JSP页面代码:

<%@ page language="java" contentType="text/html; charset=GBK"%>

<html>

<head>

<title>登录页面</title>

</head>

<body>

<!-- 提交请求参数的表单 -->

<form action="Login.action" method="post">

<table align="center">

<caption><h3>用户登录</h3></caption>

<tr>

<!-- 用户名的表单域 -->

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

</tr>

<tr>

<!-- 密码的表单域 -->

<td>密&nbsp;&nbsp;码:<input type="text" name="password"/></td>

</tr>

<tr align="center">

<td colspan="2"><input type="submit" value="登录"/><input

type="reset" value="重填" /></td>

</tr>

</table>

</form>

</body>

</html>

正如前面介绍的,该页面没有包含任何的动态内容,完全是一个静态的HTML页面。但我们注意到该表单的action属性:login.action,这个action属性比较特殊,它不是一个普通的Servlet,也不是一个动态JSP页面。可能读者已经猜到了,当表单提交给login.action时,Struts 2的FilterDispatcher将自动起作用,将用户请求转发到对应的Struts 2 Action。

 注意  Struts 2 Action默认拦截所有后缀为.action的请求。因此,如果我们需要将某个表单提交给Struts 2 Action处理,则应该将该表单的action属性设置为*.action的格式。

该页面就是一个基本的HTML页面,在浏览器中浏览该页面,看到如图2.1所示的界面。

整个页面就是一个标准的HTML页面,整个单独的页面还没有任何与用户交互的能力。下面我们开始动手创建一个Struts 2的Web应用。

2.3  创建Struts 2Web应用

Struts 2的Web应用就是一个普通的Web应用,然后增加Struts 2功能,该应用就可以充分利用Struts 2的MVC框架了。

2.3.1  创建Web应用

笔者一直相信:要想成为一个优秀的程序员,应该从基本功练起,所有的代码都应该用简单的文本编辑器(包括EditPlus、UtraEdit等工具)完成。笔者经常见到一些有两三年开发经验的程序员,一旦离开了熟悉的IDE(集成开发环境,如Eclipse、JBuilder等),完全不能动手写任何代码。而他们往往还振振有词:谁会不用任何工具来开发?

实际上,真正优秀的程序员当然可以使用IDE工具,但即使使用VI(UNIX下无格式编辑器)、记事本也一样可以完成非常优秀的项目。笔者对于IDE工具的态度是:可以使用IDE工具,但绝不可依赖于IDE工具。学习阶段,千万不要使用IDE工具;开发阶段,才去使用IDE工具。

 提醒  对于IDE工具,业内有一个说法:IDE工具会加快高手的开发效率,但会使初学者更白痴。

为了让读者更加清楚Struts 2应用的核心,笔者下面将“徒手”建立一个Struts 2应用。

建立一个Web应用请按如下步骤进行。

 在任意目录新建一个文件夹,笔者将以该文件夹建立一个Web应用。

 在第1步所建的文件夹内建一个WEB-INF文件夹。

 进入Tomcat,或任何Web容器内,找到任何一个Web应用,将Web应用的WEB-INF下的web.xml文件复制到第2步所建的WEB-INF文件夹下。

 修改复制的web.xml文件,将该文件修改成只有一个根元素的XML文件,修改后的web.xml文件代码如下:

<?xml version="1.0" encoding="GBK"?>

<!-- web-app是Web应用配置文件的根元素,指定Web应用的Schema信息 -->

<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.

com/xml/ns/j2ee/web-app_2_4.xsd">

</web-app>

 在第2步所建的WEB-INF路径下,新建两个文件夹:classes和lib,它们分别用于保存单个*.class文件和JAR文件。

经过上面步骤,已经建立了一个空Web应用。将该Web应用复制到Tomcat的webapps路径下,该Web应用将可以自动部署在Tomcat中。

将2.2节所定义的JSP页面文件复制到第1步所建的文件夹下,该JSP页面将成为该Web应用的一个页面。该Web将有如下文件结构:

Struts2qs

|-WEB-INF

|     |-classes

|     |-lib

|     |-web.xml

|-login.jsp

上面的Struts2qs是Web应用所对应文件夹的名字,可以更改;login.jsp是该Web应用下JSP页面的名字,也可以修改。其他文件夹、配置文件都不可以修改。

启动Tomcat,在浏览器中浏览2.2节定义的JSP页面,将看到如图2.1所示的页面。

2.3.2  增加Struts 2功能

为了给Web应用增加Struts 2功能,只需要将Struts 2安装到Web应用中即可。在Web应用中安装Struts 2框架核心只需要经过如下三个步骤。

 修改web.xml文件,在web.xml文件中配置Struts 2的核心Filter。

 将Struts 2框架的类库复制到Web应用的WEB-INF/lib路径下。

 在WEB-INF/classes下增加struts.xml配置文件。

下面是增加了Struts 2功能后Web应用的文件结构:

Struts2qs

|-WEB-INF

|     |-classes(struts.xml)

|     |-lib(commons-logging.jar,freemarker.jar,ognl.jar,struts2-core.jar,xwork.jar)

|     |-web.xml

|-login.jsp

在上面的文件结构中,lib下Struts 2框架的类库可能有版本后缀。例如commons-logging.jar,可能是commons-logging-1.1.jar;struts2-core.jar可能是struts2-core-2.0.6.jar。

修改后的web.xml文件在2.1节已经给出了,故此处不再赘述。

此处需要给读者指出的是,Struts 2的Web应用默认需要Java 5运行环境,需要Web容器支持Servlet API 2.4和JSP API 2.0。如果读者需要使用更低版本的Java运行时环境,则需要使用Struts 2框架的JDK 1.4支持。为了简单起见,笔者建议读者使用Java 5运行时环境,使用Tomcat 5.5或者更高版本。

 注意  Struts 2应用默认需要Java 5运行时环境,需要支持Servlet API 2.4和JSP API 2.0的Web容器。

2.4  实现控制器

前面介绍MVC框架时,已经指出:MVC框架的核心就是控制器。当用户通过2.2节的页面提交用户请求时,该请求需要提交给Struts 2的控制器处理。Struts 2的控制器根据处理结果,决定将哪个页面呈现给客户端。

2.4.1  实现控制器类

Struts 2下的控制器不再像Struts 1下的控制器,需要继承一个Action父类,甚至可以无需实现任何接口,Struts 2的控制器就是一个普通的POJO。

实际上,Struts 2的Action就是一个包含execute方法的普通Java类,该类里包含的多个属性用于封装用户的请求参数。下面是处理用户请求的Action类的代码:

//Struts 2的Action类就是一个普通的Java类

public class LoginAction

{

//下面是Action内用于封装用户请求参数的两个属性

private String username;

private String password;

//username属性对应的getter方法

public String getUsername()

{

return username;

}

//username属性对应的setter方法

public void setUsername(String username)

{

this.username = username;

}

//password属性对应的getter方法

public String getPassword()

{

return password;

}

//password属性对应的setter方法

public void setPassword(String password)

{

this.password = password;

}

//处理用户请求的execute方法

public String execute() throws Exception

{

//当用户请求参数的username等于scott,密码请求参数为tiger时,返回success

字符串

//否则返回error字符串

if (getUsername().equals("scott")

&& getPassword().equals("tiger") )

{

return "success";

}

else

{

return "error";

}

}

}

上面的Action类是一个再普通不过的Java类,该类里定义了两个属性:username和password,并为这两个属性提供了对应的setter和getter方法。除此之外,该Action类里还包含了一个无参数的execute方法——这大概也是Action类与POJO唯一的差别。实际上,这个execute方法依然是一个很普通的方法,既没有与Servlet API耦合,也没有与Struts 2 API耦合。

 提示  表面上看起来,该Action的两个属性只提供了对应的setter和getter方法,很难理解请求参数在什么时候赋值给该Action的属性,事实上,因为Struts 2的拦截器机制,它们负责解析用户的请求参数,并将请求参数赋值给Action对应的属性。

2.4.2  配置Action

上面定义了Struts 2的Action,但该Action还未配置在Web应用中,还不能处理用户请求。为了让该Action能处理用户请求,还需要将该Action配置在struts.xml文件中。

前面已经介绍过了,struts.xml文件应该放在classes路径下,该文件主要放置Struts 2的Action定义。定义Struts 2 Action时,除了需要指定该Action的实现类外,还需要定义Action处理结果和资源之间的映射关系。下面是该应用的struts.xml文件的代码:

<?xml version="1.0" encoding="GBK"?>

<!-- 指定Struts 2配置文件的DTD信息 -->

<!DOCTYPE struts PUBLIC

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

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

<!-- struts是Struts 2配置文件的根元素 -->

<struts>

<!-- Struts 2的Action必须放在指定的包空间下定义 -->

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

<!-- 定义login的Action,该Action的实现类为lee.Action类 -->

<action name="Login" class="lee.LoginAction">

<!-- 定义处理结果和资源之间映射关系。 -->

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

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

</action>

</package>

</struts>

上面映射文件定义了name为login的Action,即:该Action将负责处理向login.action URL请求的客户端请求。该Action将调用自身的execute方法处理用户请求,如果execute方法返回success字符串,请求将被转发到/welcome.jsp页面;如果execute方法返回error字符串,则请求被转发到/error.jsp页面。

2.4.3  增加视图资源完成应用

经过上面步骤,这个最简单的Struts 2应用几乎可以运行了,但还需要为该Web应用增加两个JSP文件,两个JSP文件分别是error.jsp页面和welcome.jsp页面,将这两个JSP页面文件放在Web应用的根路径下(与WEB-INF在同一个文件夹下)。

这两个JSP页面文件是更简单的页面,它们只是包含了简单的提示信息。其中welcome.jsp页面的代码如下:

<%@ page language="java" contentType="text/html; charset=GBK"%>

<html>

<head>

<title>成功页面</title>

</head>

<body>

您已经登录!

</body>

</html>

上面的页面就是一个普通的HTML页面,登录失败后进入的error.jsp页面也与此完全类似。

在如图2.1所示页面的“用户名”输入框中输入scott,在“密码”输入框中输入tiger,页面将进入welcome.jsp页面,将看到如图2.2所示的页面。

对于上面的处理流程,可以简化为如下的流程:用户输入两个参数,即username和password,然后向login.action发送请求,该请求被FilterDispatcher转发给LoginAction处理,如果LoginAction处理用户请求返回success字符串,则返回给用户welcome.jsp页面;如果返回error字符串,则返回给用户error.jsp页面。

图2.3显示了上面应用的处理流程。

图2.3  HelloWorld应用的处理流程

2.5  改进控制器

通过前面介绍,读者已经可以完成简单的Struts 2的基本应用了,但还可以进一步改进应用的Action类,例如该Action类可以通过实现Action接口,利用该接口的优势。前面应用的Action类没有与JavaBean交互,没有将业务逻辑操作的结果显示给客户端。

2.5.1  实现Action接口

表面上看起来,实现Struts 2的Action接口没有太大的好处,仅会污染该Action的实现类。事实上,实现Action接口可以帮助开发者更好地实现Action类。下面首先看Action接口的定义:

public interface Action

{

//下面定义了5个字符串常量

public static final String SUCCESS = "success";

public static final String NONE = "none";

public static final String ERROR = "error";

public static final String INPUT = "input";

public static final String LOGIN = "login";

//定义处理用户请求的execute抽象方法

public String execute() throws Exception;

}

在上面的Action代码中,我们发现该Action接口里已经定义了5个标准字符串常量:SUCCESS、NONE、ERROR、INPUT和LOGIN,它们可以简化execute方法的返回值,并可以使用execute方法的返回值标准化。例如对于处理成功,则返回SUCCESS常量,避免直接返回一个success字符串(程序中应该尽量避免直接返回数字常量、字符串常量等)。

因此,借助于上面的Action接口,我们可以将原来的Action类代码修改为如下:

//实现Action接口来实现Struts 2的Action类

public class LoginAction implements Action

{

//下面是Action内用于封装用户请求参数的两个属性

private String username;

private String password;

//username属性对应的getter方法

public String getUsername()

{

return username;

}

//username属性对应的setter方法

public void setUsername(String username)

{

this.username = username;

}

//password属性对应的getter方法

public String getPassword()

{

return password;

}

//password属性对应的setter方法

public void setPassword(String password)

{

this.password = password;

}

//处理用户请求的execute方法

public String execute() throws Exception

{

//当用户请求参数的username等于scott,密码请求参数为tiger时,返回success

字符串

//否则返回error的字符串

if (getUsername().equals("scott")

&& getPassword().equals("tiger") )

{

return SUCCESS;

}

else

{

return ERROR;

}

}

}

对比前面Action和此处的Action实现类,我们发现两个Action类的代码基本相似,除了后面的Action类实现了Action接口。因为实现了Action接口,故Action类的execute方法可以返回Action接口里的字符串常量。

2.5.2  跟踪用户状态

前面的Action处理完用户登录后,仅仅执行了简单的页面转发,并未跟踪用户状态信息——通常,当一个用户登录成功后,需要将用户的用户名添加为Session状态信息。

为了访问HttpSession实例,Struts 2提供了一个ActionContext类,该类提供了一个getSession的方法,但该方法的返回值类型并不是HttpSession,而是Map。这又是怎么回事呢?实际上,这与Struts 2的设计哲学有关,Struts 2为了简化Action类的测试,将Action类与Servlet API完全分离,因此getSession方法的返回值类型是Map,而不是HttpSession。

虽然ActionContext的getSession返回的不是HttpSession对象,但Struts 2的系列拦截器会负责该Session和HttpSession之间的转换。

为了可以跟踪用户信息,我们修改Action类的execute方法,在execute方法中通过ActionContext访问Web应用的Session。修改后的execute方法代码如下:

//处理用户请求的execute方法

public String execute() throws Exception

{

//当用户请求参数的username等于scott,密码请求参数为tiger时,返回success字符串

//否则返回error的字符串

if (getUsername().equals("scott")

&& getPassword().equals("tiger") )

{

//通过ActionContext对象访问Web应用的Session

ActionContext.getContext().getSession().put("user" , getUsername());

return SUCCESS;

}

else

{

return ERROR;

}

}

上面的代码仅提供了Action类的execute方法,该Action类的其他部分与前面的Action类代码完全一样。在上面的Action类通过ActionContext设置了一个Session属性:user。为了检验我们设置的Session属性是否成功,我们修改welcome.jsp页面,在welcome.jsp页面中使用JSP 2.0表达式语法输出Session中的user属性。下面是修改后的welcome.jsp页面代码:

<%@ page language="java" contentType="text/html; charset=GBK"%>

<html>

<head>

<title>成功页面</title>

</head>

<body>

欢迎,${sessionScope.user},您已经登录!

</body>

</html>

上面的JSP页面与前面的JSP页面没有太大改变,除了使用了JSP 2.0语法来输出Session中的user属性。关于JSP 2.0表达式的知识,请参看笔者所著的《轻量级J2EE企业应用实战》一书的第2章。

在如图2.1所示页面的“用户名”输入框中输入scott,在“密码”输入框中输入tiger,然后单击“登录”按钮,将看到如图2.4所示的页面。

在上面登录成功的页面中,已经输出登录所用的用户名:scott,可见在Action通过ActionContext设置Session是成功的。

2.5.3  添加处理信息

到目前为止,Action仅仅控制转发用户请求,JSP页面并未获得Action的处理结果。对于大部分Web应用而言,用户需要获得请求Action的处理结果,例如,在线购物系统需要查询某个种类下的商品,则Action调用业务逻辑组件的业务逻辑方法得到该种类下的全部商品,而JSP页面则获取该Action的处理结果,并将全部结果迭代输出。

下面将为应用增加一个Action,该Action负责获取某个系列的全部书籍。为了让该Action可以获取这系列的书籍,我们增加一个业务逻辑组件,它包含一个业务逻辑方法,该方法可以获取某个系列的全部书籍。

下面是系统所用的业务逻辑组件的代码:

public class BookService

{

//以一个常量数组模拟了从持久存储设备(数据库)中取出的数据

private String[] books =

new String[]{

"Spring2.0宝典" ,

"轻量级J2EE企业应用实战",

"基于J2EE的Ajax宝典",

"Struts,Spring,Hibernate整合开发"

};

//业务逻辑方法,该方法返回全部图书

public String[] getLeeBooks()

{

return books;

}

}

上面的业务逻辑组件实际上就是MVC模式中的Model,它负责实现系统业务逻辑方法。理论上,业务逻辑组件实现业务逻辑方法时,必须依赖于底层的持久层组件,但此处的业务逻辑组件则只是返回一个静态的字符串数组——因为这只是一种模拟。

 注意  此处的业务逻辑组件只是模拟实现业务逻辑方法,并未真正调用持久层组件来获取数据库信息。

在系统中增加如下Action类,该Action类先判断Session中user属性是否存在,并且等于scott字符串——这要求查看图书之前,用户必须已经登录本系统。如果用户已经登录本系统,则获取系统中全部书籍,否则返回登录页面。

新增的Action类的代码如下:

public class GetBooksAction implements Action

{

//该属性并不用于封装用户请求参数,而用于封装Action需要输出到JSP页面信息

private String[] books;

//books属性的setter方法

public void setBooks(String[] books)

{

this.books = books;

}

//books属性的getter方法

public String[] getBooks()

{

return books;

}

//处理用户请求的execute方法

public String execute() throws Exception

{

//获取Session中的user属性

String user = (String)ActionContext.getContext().getSession().

get("user");

//如果user属性不为空,且该属性值为scott

if (user != null && user.equals("scott"))

{

//创建BookService实例

BookService bs = new BookService();

//将业务逻辑组件的返回值设置成该Action的属性

setBooks(bs.getLeeBooks());

return SUCCESS;

}

else

{

return LOGIN;

}

}

}

通过上面的Action类,我们发现Action类中的成员属性,并不一定用于封装用户的请求参数,也可能是封装了Action需要传入下一个JSP页面中显示的属性。

 提示  Action中的成员属性,并一定用于封装用户的请求参数,也可能是封装了Action需要传入下一个页面显示的值。实际上,这些值将被封装在ValueStack对象中。

当我们的控制器需要调用业务逻辑方法时,我们直接创建了一个业务逻辑组件的实例,这并不是一种好的做法,因为控制器不应该关心业务逻辑组件的实例化过程。比较成熟的做法可以利用工厂模式来管理业务逻辑组件;当然,目前最流行的方式是利用依赖注入——这将在后面章节里介绍。

 注意  实际项目中不会在控制器中直接创建业务逻辑组件的实例,而是通过工厂模式管理业务逻辑组件实例,或者通过依赖注入将业务逻辑组件实例注入控制器组件。

该Action处理用户请求时,无需获得用户的任何请求参数。将该Action配置在struts.xml文件中,配置该Action的配置片段如下:

<!-- 定义获取系统中图书的Action,对应实现类为lee.GetBooksAction -->

<action name="GetBooks" class="lee.GetBooksAction">

<!-- 如果处理结果返回login,进入login.jsp页面 -->

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

<!-- 如果处理结果返回success,进入showBook.jsp页面 -->

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

</action>

当用户向getBooks.action发送请求时,该请求将被转发给lee.GetBooksAction处理。

2.5.4  输出处理信息

如果用户没有登录,直接向getBooks.action发送请求,该请求将被转发到login.jsp页面。如果用户已经登录,getBooks.action将从系统中加载到系统中的所有图书,并将请求转发给showBook.jsp页面,因此showBook.jsp页面必须负责输出全部图书。

下面笔者将以最原始的方式:JSP脚本来输出全部图书。

 注意  在实际应用中,几乎绝对不会使用笔者这种方式来输出Action转发给JSP输出的信息,但笔者为了让读者更清楚Struts 2标签库在底层所完成的动作,故此处使用JSP脚本来输出全部图书信息。

当Action设置了某个属性值后,Struts 2将这些属性值全部封装在一个叫做struts.valueStack的请求属性里。

 提示  读者可能感到奇怪:笔者是如何知道Struts 2将这些属性值封装在struts.valueStack请求属性里的?这一方面与编程经验有关,另一方面可以通过查看Struts 2的各种文档,最重要的一点是可以在showBook.jsp页面中通过getAttributeNames方法分析请求中的全部属性。

为了在JSP页面中输出需要输出的图书信息,我们可以通过如下代码来获取包含全部输出信息的ValueStack对象。

//获取封装输出信息的ValueStack对象

request.getAttribute("struts.valueStack");

上面代码返回一个ValueStack对象,该对象封装了全部的输出信息。该对象是 Struts 2使用的一个ValueStack对象,可以通过OGNL表达式非常方便地访问该对象封装的信息。

从数据结构上来看,ValueStack有点类似于Map结构,但它比Map结构更加强大(因为它可以根据表达式来查询值)。Action所有的属性都被封装到了ValueStack对象中,Action中的属性名可以理解为ValueStack中value的名字。

大致理解了ValueStack对象的结构后,我们可以通过如下代码来获取Action中设置的全部图书信息。

//调用ValueStack的fineValue方法查看某个表达式的值

vs.findValue("books");

理解了上面关键的两步,整个JSP页面的代码就比较容易了解了。下面是showBook.jsp页面的代码:

<%@ page language="java" contentType="text/html; charset=GBK">

<% @page import="java.util.*,com.opensymphony.xwork2.util.*"%>

<html>

<head>

<title>作者李刚的图书</title>

</head>

<body>

<table border="1" width="360">

<caption>作者李刚的图书</caption>

<%

//获取封装输出信息的ValueStack对象

ValueStack vs = (ValueStack)request.getAttribute("struts.valueStack");

//调用ValueStack的fineValue方法获取Action中的books属性值

String[] books = (String[])vs.findValue("books");

//迭代输出全部图书信息

for (String book : books)

{

%>

<tr>

<td>书名:</td>

<td><%=book%></td>

</tr>

<%}%>

</table>

</body>

</html>

不可否认,上面JSP页面的代码是丑陋的,而且难以维护,因为里面镶嵌了大量的Java脚本。但它对于读者理解Struts 2如何处理封装在Action的ValueStack却很有帮助。

在浏览器中向getBooks.action发送请求,将看到如图2.5所示的页面。

通过上面页面,我们看到JSP页面已经输出了Struts 2控制器的返回信息。上面整个过程,已经完全包括了Struts 2框架的3个部分:视图、控制器和模型。

图2.5  在JSP页面中输出Action的返回信息

2.6  改进视图组件

通过前面的几节的介绍,我们已经明白了Struts 2 MVC框架的基本数据流,已经完成了Struts 2应用中模型、控制器、视图3个组件的开发。但应用中的视图组件:JSP页面非常丑陋,特别是输出Action返回信息的JSP页面,使用了大量的Java脚本来控制输出,下面将会使用Struts 2的标签来改善整个应用视图组件。

2.6.1  改善输出页面

为了控制输出Struts 2的ValueStack中封装的值,Struts 2提供了大量的标签。其中比较常用的标签有:

—  if:该标签支持标签体,如果if标签里判断的表达式返回真,则输出标签体内容。

—  else:该标签不能独立使用,它需要与if标签结合使用,如果if标签内判断的表达式返回假,则输出该标签里的标签体。

—  iterator:主要用于迭代输出某个集合属性的各个集合元素。

—  property:该标签用于输出指定属性值。

关于Struts 2标签库更深入的使用,第10章还会深入介绍,故此处不再详细讲解。通过使用上面的几个标签,替换showBook.jsp页面中的Java脚本,修改后的showBook.jsp页面代码如下:

<%@ page language="java" contentType="text/html; charset=GBK" %>

<!-- 导入Struts 2的标签库 -->

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

<html>

<head>

<title>作者李刚的图书</title>

</head>

<body>

<table border="1" width="360">

<caption>作者李刚的图书</caption>

<!-- 迭代输出ValueStack中的books对象,其中status是迭代的序号 -->

<s:iterator value="books" status="index">

<!-- 判断序号是否为奇数 -->

<s:if test="#index.odd == true">

<tr style="background-color:#cccccc">

</s:if>

<!-- 判断迭代元素的序号是否不为偶数 -->

<s:else>

<tr>

</s:else>

<td>书名:</td>

<td><s:property/></td>

</tr>

</s:iterator>

</table>

</body>

</html>

上面的JSP页面使用了Struts 2的标签库,因此必须在JSP页面的首部添加taglib指令,该taglib指令用于导入标签库。

 提示  如果需要使用某个标签库中的标签,则必须在页面的开始导入该标签库。

页面中使用Struts 2的iterator标签迭代输出ValueStack中的books数组,并为每个数组元素定义了一个序号:index。通过判断序号是否为奇数,如果行序号为奇数,则输出一个有背景色的表格行;否则输出一个无背景色的表格行。

在浏览器中再次向getBooks.action发送请求(发送请求之前,必须先登录本系统),将看到如图2.6所示的界面。

图2.6  使用Struts 2标签改善后的输出界面

上面页面的输出效果与图2.5并没有太大的不同,只是使用不同颜色来分隔了记录行。这也得益于Struts 2标签库的简洁。

关键在于2.5.4节中的JSP页面代码与本节页面代码的差异:前面JSP页面使用了大量的Java脚本,让整个页面的代码看起来非常凌乱,降低了可阅读性、可维护性。但本页面中仅使用Struts 2脚本控制输出,完全消除了页面中的Java脚本,降低了该页面的后期维护成本。

2.6.2  使用UI标签简化表单页面

前面已经提到过,Struts 2的一个重要组件就是标签库。Struts 2标签库中不仅提供了前面所示的基本控制、数据输出等功能,还提供了非常丰富的UI组件,除了提供系列的主题相关标签外,还提供了一系列的表单相关的标签。

Struts 2为常用表单域都提供了对应的标签,下面是常用的表单域标签。

—  form:对应一个表单元素。

—  checkbox:对应一个复选框元素。

—  password:对应一个密码输入框。

—  radio:对应一个单选框元素。

—  reset:对应一个重设按钮。

—  select:对应一个下拉列表框。

—  submit:对应一个提交按钮。

—  textarea:对应一个多行文本域。

—  textfield:对应一个单行文本框。

关于这些界面相关的标签,同样将在第10章详细介绍。下面将使用Struts 2的表单相关标签简化用户登录的login.jsp页面,修改的login.jsp页面的代码如下:

<%@ page language="java" contentType="text/html; charset=GBK"%>

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

<html>

<head>

<title>登录页面</title>

</head>

<body>

<!-- 使用form标签生成表单元素 -->

<s:form action="Login">

<!-- 生成一个用户名文本输入框 -->

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

<!-- 生成一个密码文本输入框 -->

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

<!-- 生成一个提交按钮 -->

<s:submit value="登录"/>

</s:form>

</body>

</html>

将该页面与前面的表单页面进行对比,我们发现该页面的代码简洁多了。因为使用了Struts 2的表单标签,定义表单页面也更加迅速。在浏览器中浏览该页面,看到如图2.7所示的界面。

图2.7  使用Struts 2表单标签后的表单页

当然,Struts 2的标签还有许多功能,此处先不详述,本书的第10章将会详细介绍Struts 2标签的用法。

2.7  完成程序国际化

因为一个企业应用经常需要面对多区域的用户,因此,程序国际化是一个企业应用必须实现的功能。Struts 2提供了很好的程序国际化支持。

2.7.1  定义国际化资源文件

Struts 2的程序国际化支持建立在Java程序国际化的基础之上,关于Java程序的国际化笔者将在第9章简要介绍。此处不会详细介绍,但我们要明白一个概念:程序国际化的设计思想是非常简单的,其主要思想是:程序界面中需要输出国际化信息的地方,我们不要在页面中直接输出信息,而是输出一个key值,该key值在不同语言环境下对应不同的字符串。当程序需要显示时,程序将根据不同的语言环境,加载该key对应该语言环境下的字符串——这样就可以完成程序的国际化。

图2.8显示了程序国际化的示意图。

图2.8  程序国际化示意图

从图2.8可以看出,如果需要程序支持更多的语言环境,只需要增加更多语言资源文件即可。

为了给本应用增加程序国际化支持(支持英文和中文),则应该提供两份语言资源文件。下面是本应用所使用的中文语言环境下资源文件的代码。

loginPage=登录页面

errorPage=错误页面

succPage=成功页面

failTip=对不起,您不能登录!

succTip=欢迎,${0},您已经登录!

viewLink=查看作者李刚已出版的图书

bookPageTitle=作者李刚已出版的图书

bookName=书名:

user=用户名

pass=密  码

login=登录

因为该资源文件中包含了非西欧字符,因此必须使用native2ascii命令来处理该文件。将上面文件保存在WEB-INF/classes路径下,文件名为“messageResouce.properties”。保存该文件后,必须使用native2ascii命令来处理该文件,处理该文件的命令格式为:

native2ascii messageResouce.properties messageResouce_zh_CN.properties

上面命令将包含非西欧字符的资源文件处理成标准的ASCII格式,处理完成后生成了一份新文件:messageResouce _zh_CN.properties文件。这个文件的文件名符合资源文件的命名格式,资源文件的文件名命名格式为:

basename_语言代码_国家代码.properties

当请求来自简体中文的语言环境时,系统将自动使用这种资源文件中的内容输出。

 注意  对于包含非西欧字符的资源文件,一定要使用native2assii命令来处理该文件,否则将看到一堆乱码。

除此之外,还应该提供如下英文语言环境的资源文件。

loginPage=Login Page

errorPage=Error Page

succPage=Welcome Page

failTip=Sorry,You can't log in!

succTip=welcome,{0},you has logged in!

viewLink=View LiGang\'s Books

bookPageTitle=LiGang\'s Books

bookName=BookName:

user=User Name

pass=UserPass

login=Login

将上面资源文件保存在WEB-INF/classes路径下,文件名为“messageResouce_en_ US.properties”。当请求来自美国时,系统自动使用这份资源文件的内容输出。

2.7.2  加载资源文件

Struts 2支持在JSP页面中临时加载资源文件,也支持通过全局属性来加载资源文件。通过全局属性加载资源文件更简单,本应用使用全局属性加载Struts 2国际化资源文件。

加载资源文件可以通过struts.properties文件来定义,本应用的struts.properties文件仅有如下一行代码:

//定义Struts 2的资源文件的baseName是messageResource

struts.custom.i18n.resources=messageResource

在struts.properties文件中增加上面的代码定义后,表明该应用使用的资源文件的baseName为“messageResouce”——这与我们前面保存资源文件的baseName是一致的。

Struts 2默认加载WEB-INF/classes下的资源文件,在上一节中,我们就是将资源文件保存在该路径下的。如果将该资源文件保存在WEB-INF/classes的子目录下,例如保存在WEB-INF/classes/lee路径下,则需要修改struts.properties中的定义如下:

//定义Struts 2的资源文件的baseName是messageResource,且文件放在WEB-INF/

classes/lee路径下

struts.custom.i18n.resources=lee.messageResource

2.7.3  输出国际化信息

为了让程序可以显示国际化信息,则需要在JSP页面中输出key,而不是直接输出字符串常量。

Struts 2提供了如下两种方式来输出国际化信息:

—  <s:text name="messageKey"/>:使用s:text标签来输出国际化信息。

—  <s:property value="%{getText("messageKey")}"/>:使用表达式方式输出国际化信息。

因此,我们再次修改表现层的JSP页面,使用国际化标签输出国际化信息。修改后的showBook.jsp页面代码如下:

<%@ page language="java" contentType="text/html; charset=GBK"%>

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

<html>

<head>

<!-- 使用s:text输出国际化信息 -->

<title><s:text name="bookPageTitle"/></title>

</head>

<body>

<table border="1" width="360">

<!-- 使用s:text输出国际化信息 -->

<caption><s:text name="bookPageTitle"/></caption>

<s:iterator value="books" status="index">

<s:if test="#index.odd == true">

<tr style="background-color:#cccccc">

</s:if>

<s:else>

<tr>

</s:else>

<td><s:text name="bookName"/></td>

<td><s:property/></td>

</tr>

</s:iterator>

</table>

</body>

</html>

我们发现,上面的JSP页面不再包含任何直接字符串,而是全部通过<s:text name="..."/>来输出国际化提示。

再次在浏览器浏览该页面,将看到与图2.7相同的界面。

重新设置浏览者所在的语言/区域选项,设置语言/区域选项请先进入“控制面板”,在控制面板中单击“区域和语言选项”,进入如图2.9所示的对话框。

如果我们选择“英语(美国)”选项,然后单击“确定”按钮,将设置本地的语言环境为美国英语。

再次向服务器请求login.jsp页面,将看到如图2.10所示的页面。

图2.9  设置语言/区域选项                  图2.10  程序国际化的效果

如果我们使用FireFox浏览器来浏览该页面时,发现依然显示中文界面——这是因为FireFox的语言环境并不受Windows系统的控制。为了让FireFox也使用美国英语环境,单击FireFox浏览器菜单栏中的“工具”菜单,选择“选项”菜单项,将出现“选项”对话框,单击“高级”按钮,将看到如图2.11所示的界面。

在如图2.11所示的对话框中单击“选择”按钮,将出现“语言和字符编码”对话框,在该对话框下面的下拉列表框中选择“英语/美国 [en-us]”选项,如图2.12所示,然后单击下拉框右边的“添加”按钮,将添加了“英语/美国”的语言环境。并在该对话框上面的列表框中选择“英语/美国 [en-us]”,然后单击“上移”按钮,将该选项移至最上面,让整个页面优先使用英语/美国的环境。

图2.11  设置FireFox的语言环境         图2.12  设置FireFox的语言环境为英语/美国

再次使用FireFox浏览器浏览login.jsp页面,将看到该页面变成了英文界面。

2.8  增加数据校验

在上面应用中,即使浏览者输入任何用户名、密码,系统也会处理用户请求。在我们整个HelloWorld应用中,这种空用户名、空密码的情况不会引起太大的问题。但如果数据需要保存到数据库,或者需要根据用户输入的用户名、密码查询数据,这些空输入可能引起异常。

为了避免用户的输入引起底层异常,通常我们会在进行业务逻辑操作之前,先执行基本的数据校验。

2.8.1  继承ActionSupport

ActionSupport类是一个工具类,它已经实现了Action接口。除此之外,它还实现了Validateable接口,提供了数据校验功能。通过继承该ActionSupport类,可以简化Struts 2的Action开发。

在Validatable接口中定义了一个validate()方法,重写该方法,如果校验表单输入域出现错误,则将错误添加到ActionSupport类的fieldErrors域中,然后通过OGNL表达式负责输出。

为了让Struts 2增加输入数据校验的功能,改写程序中的LoginAction,增加重写validate方法。修改后的LoginAction类代码如下:

//Struts 2的Action类就是一个普通的Java类

public class LoginAction

{

//下面是Action内用于封装用户请求参数的两个属性

private String username;

private String password;

//username属性对应的getter方法

public String getUsername()

{

return username;

}

//username属性对应的setter方法

public void setUsername(String username)

{

this.username = username;

}

//password属性对应的getter方法

public String getPassword()

{

return password;

}

//password属性对应的setter方法

public void setPassword(String password)

{

this.password = password;

}

//处理用户请求的execute方法

public String execute() throws Exception

{

//当用户请求参数的username等于scott,密码请求参数为tiger时,返回success

字符串

//否则返回error的字符串

if (getUsername().equals("scott")

&& getPassword().equals("tiger") )

{

return "success";

}

else

{

return "error";

}

}

//完成输入校验需要重写的validate方法

public void validate()

{

//如果用户名为空,或者用户名为空字符串

if (getUsername() == null || getUsername().trim().equals(""))

{

//添加表单校验错误

addFieldError("username", "user.required");

}

//当密码为空,或者密码为空字符串时,添加表单校验错误

if (getPassword() == null || getPassword().trim().equals(""))

{

addFieldError("password", "pass.required");

}

}

}

上面的Action类重写了validate方法,该方法会在执行系统的execute方法之前执行,如果执行该方法之后,Action类的fieldErrors中已经包含了数据校验错误,请求将被转发到input逻辑视图处。

为了在校验失败后,系统能将视图转入input处,必须在配置该Action时配置input属性。下面是修改后login Action的配置片段:

<!-- 定义login的Action -->

<action name="Login" class="lee.LoginAction">

<!-- 定义input的逻辑视图名,对应login.jsp页面 -->

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

<!-- 定义error的逻辑视图名,对应error.jsp页面 -->

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

<!-- 定义welcome的逻辑视图名,对应welcome.jsp页面 -->

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

</action>

对比上面的Action配置与前面的Action配置,我们发现该Action配置片段中增加了input逻辑视图的配置,该逻辑视图映射到login.jsp页面。

前面已经提到:当用户提交请求时,请求得到execute方法处理之前,先会被validate方法处理,如果该方法处理结束后,Action的fieldErrors里的校验错误不为空,请求将被转发给input逻辑视图。如果我们不输入用户名、密码而直接提交表单,将看到如图2.13所示的界面。

图2.13  输入校验的界面

看到这里也许读者觉得非常神奇:我们仅仅在Action添加了数据校验错误,并未在输入页面输出这些校验错误信息,但图2.13所示的页面,却可以看到页面已经输出了这些校验信息——这是因为Struts 2的标签,上面的JSP页面中表单使用的并不是HTML表单,而是用了<s:form .../>标签,Struts 2的<s:form ... />标签已经具备了输出校验错误的能力。

 提示  Struts 2的<s:form .../>默认已经提供了输出校验错误的能力。

但上面的程序还存在一个问题:校验信息的国际化。查看上面的Action类代码发现:重写validate方法时,如果发生校验失败的问题,校验错误的提示信息是以硬编码方式写死了——这就失去了国际化的能力。

实际上,ActionSupport类已经提供了国际化信息的能力,它提供了一个getText(String key)方法,该方法用于从资源文件中获取国际化信息。为了让校验信息支持国际化,再次改写Action里的validate方法,改写后的validate方法代码如下:

//执行数据校验的validate方法

public void validate()

{

//如果用户名为空,或者为空字符串

if (getUsername() == null || getUsername().trim().equals(""))

{

//添加校验错误提示,使用getText方法来使提示信息国际化

addFieldError("username", getText("user.required"));

}

if (getPassword() == null || getPassword().trim().equals(""))

{

addFieldError("password", getText("pass.required"));

}

}

在上面的validate方法中,添加校验错误提示时,并不是直接给出了错误提示的字符串,而是调用了getText方法来获取错误提示。因为在Action中,使用getText方法来获取了两个国际化提示:user.required和pass.required,因此应该在国际化资源文件中添加这两条提示信息。

 提示  ActionSupport增加了让提示信息国际化的能力,ActionSupport提供的getText方法可以根据资源文件加载获得国际化提示信息。

此时,如果没有任何输出,直接提交登录表单,将看到如图2.14所示的界面。

图2.14  国际化数据校验的错误提示

2.8.2  使用Struts 2的校验框架

上面的输入校验是通过重写ActionSupport类的validate方法实现的,这种方法虽然不错,但需要大量重写的validate方法——毕竟,重复书写相同的代码不是一件吸引人的事情。

类似于Struts 1,Struts 2也允许通过定义配置文件来完成数据校验。Struts 2的校验框架实际上是基于XWork的validator框架。

下面还是使用原来的Action类(即不重写validate方法),却增加一个校验配置文件,校验配置文件通过使用Struts 2已有的校验器,完成对表单域的校验。Struts 2提供了大量的数据校验器,包括表单域校验器和非表单域校验器两种。

本应用主要使用了requiredstring校验器,该校验器是一个必填校验器——指定某个表单域必须输入。

下面是校验规则的定义文件:

<?xml version="1.0" encoding="GBK"?>

<!-- 指定校验规则文件的DTD信息 -->

<!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator

1.0.2//EN"

"http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd">
<!-- 校验规则定义文件的根元素 -->

<validators>

<!-- 校验第一个表单域:username -->

<field name="username">

<!-- 该表单域必须填写 -->

<field-validator type="requiredstring">

<!-- 如果校验失败,显示user.required对应的信息 -->

<message key="user.required"/>

</field-validator>

</field>

<!-- 校验第二个表单域:password -->

<field name="password">

<field-validator type="requiredstring">

<!-- 如果校验失败,显示pass.required对应的信息 -->

<message key="pass.required"/>

</field-validator>

</field>

</validators>

定义完该校验规则文件后,该文件的命名应该遵守如下规则:

ActionName-validation.xml:其中ActionName就是需要校验的Action的类名。

因此上面的校验规则文件应该命名为“LoginAction-validation.xml”,且该文件应该与Action类的class文件位于同一个路径下。因此,将上面的校验规则文件放在WEB-INF/classes/lee路径下即可。

当然,在struts.xml文件的Action定义中,一样需要定义input的逻辑视图名,将input逻辑视图映射到login.jsp页面。

如果不输入用户名、密码而提交表单,将再次看到如图2.14所示的界面。在这种校验方式下,无需书写校验代码,只需要通过配置文件指定校验规则即可,因此提供了更好的可维护性。

2.9  本章小结

本章以一个HelloWorld应用为例,简要介绍了Struts 2 MVC框架的基本流程,从Action类基本流程控制讲起,详细介绍了如何开发一个Struts 2应用。本章的后面部分在基本Struts 2应用基础上,介绍了一些Struts 2的深入应用,包括在Action中访问HttpSession状态,将Action处理结果传回JSP页面显示,本应用也综合应用了Struts 2的标签库、数据校验、程序国际化等常用功能。通过阅读本章的内容,读者应该对Struts 2框架有一个大致的掌握。

Struts2框架学习第二章——Struts2下的HelloWorld的更多相关文章

  1. Struts2框架学习第一章——Struts2概述

    本章要点 —  Web应用的发展 —  Model 1和Model 2 —  MVC思想 —  MVC模式的优势 — 常用MVC框架及其特征 —  Struts 1的基本结构及其存在的问题 —  We ...

  2. Artech的MVC4框架学习——第二章URL路由

    总结:HttpModule 和HttpHandler是Asp.net管道的两个重要组件.请求最终处理通过HttpHandler完成.MVC就是通过名为MvcHandler自定义HttpHandler现 ...

  3. Struts2框架学习(三) 数据处理

    Struts2框架学习(三) 数据处理 Struts2框架框架使用OGNL语言和值栈技术实现数据的流转处理. 值栈就相当于一个容器,用来存放数据,而OGNL是一种快速查询数据的语言. 值栈:Value ...

  4. Struts2框架学习(二) Action

    Struts2框架学习(二) Action Struts2框架中的Action类是一个单独的javabean对象.不像Struts1中还要去继承HttpServlet,耦合度减小了. 1,流程 拦截器 ...

  5. Struts2框架学习(一)

    Struts2框架学习(一) 1,Struts2框架介绍 Struts2框架是MVC流程框架,适合分层开发.框架应用实现不依赖于Servlet,使用大量的拦截器来处理用户请求,属于无侵入式的设计. 2 ...

  6. Java之struts2框架学习

    Java之struts2框架学习 About Struts2 Struts也是一款MVC框架 , Struts2是Struts的下一代产品,是在Struts1和WebWork的技术基础上进行了合并的全 ...

  7. oracle学习 第二章 限制性查询和数据的排序 ——03

    这里.我们接着上一小节2.6留下的问题:假设要查询的字符串中含有"_"或"%".又该如何处理呢? 開始今天的学习. 2.7  怎样使用转义(escape)操作符 ...

  8. C#高级编程 (第六版) 学习 第二章:C#基础

    第二章 基础 1,helloworld示例: helloworld.cs using System; using System.Collections.Generic; using System.Li ...

  9. [ SSH框架 ] Struts2框架学习之二

    一.Struts2访问Servlet的API 前面已经对 Struts2的流程已经执行完成了,但是如果表单中有参数如何进行接收又或者我们需要向页面保存一些数据,又要如何完成呢?我们可以通过学习 Str ...

随机推荐

  1. flask请求和应用上下文

    关于WSGI WSGI(全称Web Server Gateway Interface),是为 Python 语言定义的Web服务器和Web应用程序之间的一种简单而通用的接口,它封装了接受HTTP请求. ...

  2. FindBugs——帮助查找隐藏的bug

    FindBugs 1.什么是FindBugs FindBugs 是一个静态分析工具,它检查类或者 JAR 文件,将字节码与一组缺陷模式进行对比以发现可能的问题.有了静态分析工具,就可以在不实际运行程序 ...

  3. Push h.264 rawdata to rtmp server

    Push h.264 rawdata to rtmp server /* The MIT License (MIT) Copyright (c) 2013-2015 SRS(ossrs) Permis ...

  4. django xadmin app models 注册

    在app下新建adminx.py文件 # -*- coding: utf-8 -*- # 作者:神秘藏宝室 # 日期:2018/12/28 22:07 import xadmin from .mode ...

  5. linux上scp远程复制的使用

    一.实例展示 两台机器IP分别为:A.104.238.161.75,B.43.224.34.73. ------把别的服务器上的资源拿到自己的上边 在A服务器上操作,将B服务器上/home/lk/目录 ...

  6. XVII Open Cup named after E.V. Pankratiev Stage 14, Grand Prix of Tatarstan, Sunday, April 2, 2017 Problem J. Terminal

    题目:Problem J. TerminalInput file: standard inputOutput file: standard inputTime limit: 2 secondsMemo ...

  7. Pivot 和 Global 的一些总结

      相信大家一定有在 Unity 編輯器上看到這樣功能 這邊會跟大家說明這項功能有什麼用處     Pivot  意思為,輔助編輯的工具標示,會顯示在第一個選取的物件身上. Center 意思為,輔助 ...

  8. iconnect

    https://iconnect.infosysapps.com/vpn/index.html

  9. 无密码ssh操作步骤备忘

    需求:A机器无密码登陆到B机器 1.A机器执行   ssh-keygen -t rsa  ,在~/.ssh/下生成id_rsa 和  id_rsa.pub两个文件,其中id_rsa.pub是公匙 2. ...

  10. oracle lpad 函数使用介绍

    函数介绍 lpad函数从左边对字符串使用指定的字符进行填充.从其字面意思也可以理解,l是left的简写,pad是填充的意思,所以lpad就是从左边填充的意思. 语法格式如下: lpad( string ...