好了,今天开始第二章,O(∩_∩)O哈哈~,今天这一章主要讲Struts 2的运行环境和原理,及工作流程,后面长长的文章就当是看API把,哈哈。
第一部分 Struts 2框架开发入门
2.1.1 MVC基本思想
MVC把应用程序分成3大基本模块:模型(Model,即M)、视图(View,即V)和控制器(Controller,即C),它们(三者联合即MVC)分别担当不同的任务。下图显示了这几个模块各自的职能及相互关系。
传统的Java EE开发采用JSP+Servlet+JavaBean的方式来实现MVC(如【实例1.1】),但它有一个缺陷:程序员在编写程序时必须继承HttpServlet类、覆盖doGet()和doPost()方法,严格遵守Servlet代码规范编写程序,形如:
有些代码跟程序无关,但还是必须要程序员实现接口(比如Servlet的API,这就很烦了,其实这些代码每个Servlet都要写,还没实际逻辑),为了屏蔽这种不必要的复杂性,减少工作量,可以用Struts 2框架,哈哈.
用Struts 2实现的MVC系统与传统的用Servlet编写的MVC系统相比,两者在结构上的区别如下图所示。
可见,其区别是:用Struts 2框架代替Servlet的部分作为控制器,业务处理则由用户自定义的Action去实现,与Struts2的控制核心相分离,降低了系统中各部分组件的耦合度,和编程难度.
2.1.3 简单Struts 2开发
其实之前我们有个简单的Servlet开发的项目,后续也会用到,我会把这个项目传到gitee上.
1.加载Struts 2包
登录http://struts.apache.org/,下载Struts 2,本书使用的是Struts 2.5.13,其官方下载页面如图:将下载的文件struts-2.3.16.3-all.zip解压缩,得到文件夹包含的目录结构如图2.3所示,这是一个典型的Web结构。
大部分的时候,使用Struts 2的Java EE应用并不需要用到Struts 2的全部特性,开发Struts 2程序只需用到lib下的9个jar包,包括:
(1)传统Struts 2的5个基本类库。
struts2-core-2.5.13.jar
ognl-3.1.15.jar
log4j-api-2.8.2.jar
freemarker-2.3.23.jar
(2)附加的4个库。
commons-io-2.5.jar
commons-lang3-3.6.jar
javassist-3.20.0-GA.jar
commons-fileupload-1.3.3.jar
将它们一起复制到项目的\WebRoot\WEB-INF\lib路径下,右击项目名,从弹出菜单中选择“Refresh”按钮刷新即可,加载成功的项目工程目录树如图2.4所示。
其中,主要类描述如下。
struts2-core-2.5.13.jar:Struts 2.5的主框架类库。
ognl-3.1.15.jar:OGNL表达式语言。
log4j-api-2.8.2.jar:管理程序运行日志的API接口。
freemarker-2.3.23.jar:所有的UI标记模板。
2.2.1 Struts 2工作原理
长话短说:
(1)客户端提交一个(HttpServletRequest)请求。
(2)请求被提交到一系列(主要是3层)的过滤器(Filter),如(ActionContextCleanUp、其他过滤器、FilterDispatcher)。
(3)FilterDispatcher接收到请求后,询问ActionMapper是否需要调用某个Action来处理这个(HttpServletRequest)请求,如果ActionMapper决定需要调用某个Action,FilterDispatcher则把请求的处理交给ActionProxy。
(4)ActionProxy通过Configuration Manager(struts.xml)询问框架的配置文件,找到需要调用的Action类(该Action类一般是程序员自定义的处理请求的类)。
(5)ActionProxy创建一个ActionInvocation实例,同时ActionInvocation通过代理模式调用Action。但在调用之前,ActionInvocation会根据配置加载Action相关的所有Interceptor(拦截器)。
(6)一旦Action执行完毕,ActionInvocation负责根据struts.xml中的配置找到对应的返回结果Result。
这是官方文档总结下来的,初学者看了肯定很头疼啊,那么下面就结合我们上面的实例来总结一个容易理解的Struts 2开发的项目的运行流程!
总结其运行流程如下:
(1)浏览器请求:“http://localhost:8080/bookManage/login.jsp”,发送到web应用服务器(如Tomcat等)。
(2)容器接收到了login.action的请求,根据web.xml中的配置,服务器将包含.action后缀的请求转到"org.apache.struts2.dispatcher.FilterDispatcher“类进行处理。
(3)框架在struts.xml配置文件中查找名为"login”的action对应的类。框架初始化该Action(就是对数据进行了封装,并数据放入栈中),并且执行该Aciton类的execute方法(默认的,可以设置),该方法可以执行一些数据处理等操作,然后返回结果(广义上的结果)。
(4)框架检查配置以查看返回成功时对应的页面,框架告诉容器来获得请求返回的结果页面main.jsp。
在Struts 2框架中,Action类的调用是通过代理类ActionProxy来完成的,代理类再创建一个ActionInvocation对象,来调用程序猿自定义的Action类,但在调用之前会先加载有关的Interceptor(拦截器),而Struts 2框架就是通过一系列拦截器来工作的!
2.3 简单Struts 2开发Dome
理论讲的再好,不如上手个实际项目,通过一个Deme让我们了解下Struts2的流程控制,可能有些地方不懂,看看代码就行,后面教程会详细讲解的!
2.3.2. 配置web.xml
Struts 2框架需要在项目web.xml文件中配置,代码如下:
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">
代码加粗部分即是Struts2.5特有的。
2.3.3. 实现控制器Action
基于Struts 2框架的Java EE应用程序使用自定义的Action(控制器)来处理深层业务逻辑,完成用户想要完成的功能。本例定义名为“login”的控制器,判断登录用户名和密码的正确性。在项目src下建立包org.action,在包里创建LoginAction类,LoginAction.java代码所示:
package org.action;
import java.util.*;
import org.model.*;
import org.dao.*;
import com.opensymphony.xwork2.*;
public class LoginAction extends ActionSupport{
private Login login;
//处理用户请求的 execute 方法
public String execute() throws Exception{
//该类为项目与数据的接口(DAO接口),用于处理数据与数据库表的一些操作
LoginDao loginDao = new LoginDao();
Login l = loginDao.checkLogin(login.getName(), login.getPassword());
if(l!=null){ //如果登录成功
//获得会话,用来保存当前登录用户的信息
Map session = ActionContext.getContext().getSession();
session.put("login", l); //把获取的对象保存在 Session 中
return SUCCESS;//验证成功返回字符串SUCCESS(此时 Session 中已有用户对象)
}else{
return ERROR; //验证失败返回字符串ERROR
}
}
//属性 login 的 get/set 方法,记住这里
public Login getLogin() {
return login;
}
public void setLogin(Login login) {
this.login = login;
}
}
2.3.4. 配置struts.xml
在编写好Action(控制器)的代码之后,还需要进行配置才能让Struts 2识别Action。在src下创建文件struts.xml(注意文件位置和大小写),输入如下的配置代码:
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
struts.xml一定不要弄错地方哦:
2.3.5. 编写JSP
本例login.jsp(登录页)、main.jsp(欢迎主页)这两个JSP文件均使用Struts 2的标签进行了重新改写。登录页login.jsp,代码为:
<%@ page language="java" pageEncoding="gb2312"%>
<%@ taglib prefix="s" uri="/struts-tags"%>
| 登录名 |
| 密 码 |
|
|
欢迎主页main.jsp,代码如下:
<%@ page language="java" pageEncoding="gb2312"%>
<%@ taglib prefix="s" uri="/struts-tags"%>
好了,这里我们运行,效果图还是和上一篇文章的一样,这个Demo只是让我们认识一下Struts2的工作流程,下面我们会详细讲解Struts2的配置,也是对这个Demo的问题释疑!因为下面要写的东西很多(虽然很枯燥,但是要好好看哦,下一章很重要的),这样也让大家对Struts2更加了解!
第二部分 Struts 2基础
2.3 Struts2的控制器Action类
2.3.1 使用ActionSupport
Struts2的核心功能就是Action。对于开发人员来说,使用Struts2框架,主要编码工作就是编写Action类。一般情况下我们的Action类都会继承这个ActionSupport类,ActionSupport类实现了5个接口:
1.Action接口 实现了5个常量及一个execute()方法,如下:
如果Action类继承了ActionSupport类,就可以直接应用这几个常量,比如在【我们前面的实例】中的Action代码:
2.Validateable接口:该接口提供了一个validate()方法用于校验表单数据,在实际应用中只要在Action类中重写该方法即可,该方法是在执行execute()方法之前执行的。
3.ValidationAware接口:该接口定义了一些方法用来对Action执行过程中产生的信息进行处理。例如,该接口中提供了addFieldError(String fieldname,String errorMessage)方法用来在验证出错时保存错误信息。
4.TextProvider接口:该接口中提供了一系列getText()方法可以用key来获得相应的value值,用于获得对应的国际化信息资源。
5.LocaleProvider接口:该接口提供了一个getLocale()方法,用于国际化时获得语言/地区信息。
2.3.2 使用Action传值
Action可以通过其属性获取页面上表单文本框中用户输入的值,在运行login.jsp时候,Struts2框架会根据页面的文本框名(代码:
但是如果字段比较多的话,就需要在Action中写很多属性,增加了代码的耦合性,非常麻烦也,这个时候可以用模型传值的方法,要把字段封装成一个类,并生成其get和set方法,就是通常说的JavaBean了。因【实例】的项目中已编写好了JavaBean类Login.java,须在Action中改变写法:
而且在前端页面中也要改变用法,改为“模型对象名.属性名”:
注意:在实际项目开发中,当项目的“.java文件-需重新编译”或者一些配置文件如 “.xml文件-需框架重新读”经过修改后,一定要重启Tomcat服务器才会生效,而前端页面“login.jsp”则刷新页面即可。
2.3.3 Action访问ServletAPI
Struts2中没有与任何的ServletAPI相关联,这样大大降低了程序的耦合性,但是有时候我们的程序必须要用这些:如“request、response、application、session”等,为此,Struts2提供了两种方法访问ServletAPI:
1.通过ActionContext
ActionContext类提供了一个静态的getContext()方法来获得ActionContext对象,然后根据其对象来获得一些Servlet API的对象。例如:
由于“request”和“response”比较特殊,也是在开发中经常会用到的,所以Struts 2提供了专门的类来获取,即“ServletActionContext”:
2.通过实现*Aware接口
例如,要获得Application对象,Action类就可以编写如下:
2.3.3 Action返回结果
在一个Action类中,有时会返回多个结果,如判断一件事情,如果为真就返回SUCCESS,否则返回ERROR(或"error")。在【实例】中,对DAO接口执行checkLogin()方法获取到Login对象l的值进行判断,然后返回不同结果:
这里判断l对象不为空(数据库中有这个用户信息)就返回成功,然后根据配置文件的返回跳转到欢迎页面,如果l为空则返回出错页面,所以还要在struts.xml文件中配置两种不同的返回结果跳转到的页面,如下:
2.3.3 在Action中定义多方法
如果程序中功能越来越多,那就要定义越来越多的Action类,所以一般不采取这样的方式,而是把相关的功能定义在同一个Action类中,用多个方法来实现不同的功能。例如,在LoginAction类中定义两个方法:
2.4 解密Struts 2框架的程序文件
2.4.1 web.xml文件
所有过滤器必须实现java.Serlvet.Filter接口,这个接口中含有3个过滤器类必须实现的方法:
init(FilterConfig):这是Servlet过滤器的初始化方法。
doFilter(ServletRequest,ServletResponse,FilterChain):这个方法完成实际的过滤操作。FilterChain参数用于访问过滤器链上的下一个过滤器。
destroy():Servlet容器在销毁过滤器实例前调用该方法。
过滤器类编写完成后,必须在web.xml中进行配置,格式如下:
过滤器的关联方式有3种:与一个URL关联、与一个URL目录下的所有资源关联、与一个Servlet关联。
2.4.2 struts.xml文件
Struts 2.5.13版本下的struts.xml的大体格式如下:
几个常用的子标签:
include:用于导入其他xml配置文件。
constant:配置一些常量信息。
package:配置包信息。
bean:由容器创建并注入的组件。
2.4.3 struts.properties文件
该文件用于用户单独需要更改的默认设置之类的,放在src/下,Struts框架会默认加载该文件,一些常用配置:
2.5 Struts 2配置详解
一个简单的struts.xml文件解析图:
2.5.1
name:该属性是必需的,对应请求的Action的名称
class:该属性不是必需的,指明处理类的具体路径,如“org.action.LoginAction”。
method:该属性不是必需的,若Action类中有不同的方法,该属性指定请求对应应用哪个方法。
converter:该属性不是必需的,指定Action使用的类型转换器(类型转换内容会在类型转换部分讲解)。
在一般情况下,都会为
2.在
这个*的意思就相当于定义变量,{1}就是使用变量,form中的action传递的参数就给了*。所以传入regist就是*为regist,而{1}就是使用了*,所以{1}也为regist。不仅方法可以使用通配符这样匹配,返回的值也可以。例如,如果应用regist方法返回“error”时就跳转到“regist.jsp”界面。
使用通配符可以很大程度地减少struts.xml的配置内容,但是可以发现,在编写时也会对Action类中的方法命名有限制,必须和请求名称对应,返回视图的名称也同样要对应。比如上面例子的意思就是:传入regist.action的请求,则会交给org.action.LoginAction类中的regist方法来处理,如果返回error,则进入regist.jsp页面。
3.访问Action类中方法的其他方式
仍以前面的LoginAction为例,
这样配置完全不知道要访问LoginAction类中的哪个方法,但是可以在请求中指明,请求的form表单要改为:
其中,“login!regist.action”中“!”前面的“login”对应
该方法是在请求中指定应用Action类中的哪个方法,还有一种办法是在提交按钮中设置的,
4.使用默认类
若
2.5.2
Struts 2中支持多种结果类型如下:
1.dispatcher类型--转发:属于同一请求,可以传递参数,浏览器地址栏不变化。
定义该类型时,物理视图为JSP页面,并且该JSP页面必须和请求信息处于同一个Web应用中。还有一点值得注意的是,请求转发时地址栏不会改变,也就是说,属于同一请求,所以请求参数及请求属性等数据不会丢失,该跳转类似于JSP中的“forward”。从前面的例子中也可以看出,跳转到“main.jsp”页面后,仍可以取出“name”的值。在应用该类型时,一般都会省略不写。配置该类型后,
location:指定请求处理完成后跳转的地址,如“/main.jsp”。
parse:指定是否允许在location参数值中使用表达式,如“/main.jsp?name=${name}”,在实际运行时,这个结果信息会替换为用户输入的“name”值,该参数默认值是true。
2.redirect类型--重定向:不同请求,不可传参数,浏览器地址会发生变化。
该结果类型可以重定向到JSP页面,也可以重定向到另一个Action。该类型是与dispatcher类型相对的,当Action处理用户请求结束后,将重新生成一个请求,转入另一个界面。例如,在【实例】中,当用默认值“dispatcher”时,请求完成,转向“main.jsp”界面,如图所示。
可以发现,请求没变,还是“login.action”,但页面已经跳转到“main.jsp”,并且可以取出“name”的值--注意这里仍是解释的dispatcher类型。如果把
则请求完成,重定向到main.jsp欢迎主页,如图2.8所示,可以看出URL已变为“main.jsp”。
配置redirect类型后,
3.redirectAction类型:也是重定向,多用于重定向到一个新的Action。
actionName:该参数指定重定向的action名。
namespace:该参数指定需要重定向的action所在的命名空间(命名空间会在后面讲解)。
此段代码释义:有regist.action请求,由org.action.LoginAction的regist的方法处理,若返回值为success,则重定向到/test2/login.action,然后。。。。
4.chain类型
前面的redirect及redirectAction虽然都可以重定向到另外的action,但是它们都不能实现数据的传递,在重定向过程中,请求属性等都会丢失,这样有的时候就不利于编程了。chain可以跳转到另外的action而且数据不丢失,通过设置chain类型,可以组成一条action链,action跳转可以共享数据的原理是处于同一个action链的action都共享同一个值栈,每个action执行完毕后都会把数据压入值栈,如果需要就直接到值栈中取。
5.全局结果
假如都返回到同一页面,而且在不同的action请求中都会用到,那么配置局部结果就显得冗余了。所以,Struts 2提供了全局结果的配置,例如,如果返回“error”,都跳转到错误页面:
2.5.3
1.可配置的属性
(1)name属性:该属性必须指定,代表包的名称,由于struts.xml中可以定义不同的
(2)extends属性:该属性是可选的,表示当前定义的包继承其他的包,继承了其他包,就可以继承其他包中的action、拦截器等。由于包信息的获取是按照配置文件中的先后顺序进行的,所以父包必须在子包之前被定义。
(3)namespace属性:该属性是可选的,用来指定一个命名空间,如在前面讲redirectAction类型时已经用到了,定义命名空间非常简单,只要指定“namespace="/*"”即可,其中“*”是我们自定的,如果直接指定“"/"”,表示设置命名空间为根命名空间。如果不指定任何namespace,则使用默认的命名空间,默认的命名空间为“" "”,也就是空。当指定了命名空间后,相应的请求也要改变,例如:
而且请求就不能是“login”,而必须改为“user/login”。当Struts 2接收到请求后,会将请求信息解析为namespace名和action名两部分,然后根据namespace名在struts.xml中查找指定命名空间的包,并且在该包中寻找与action名相同的配置,如果没有找到,就到默认的命名空间中寻找与action名称相同的配置,如果再没找到,就给出错误信息。
(4)abstract属性:该属性是可选的,如果定义该包是一个抽象包,则该包不能包含
2.可配置的标签
下一章讲的就是Struts2的标签库了,稍微有意思了那么一丢丢,哈哈,别急,慢慢学,学的东西多了,才有能力做好玩的程序嘛,n(*≧▽≦*)n,加油↖(^ω^)↗,共勉。
写文不易,点个赞⑧。
本系列所有教程均上传至码云,链接如下:
https://gitee.com/jahero/bookManage
