Struts应用实例

一 、第一个应用实例
1.简要介绍
Struts最早是作为Apache Jakarta项目的组成部分,项目的创立者希望通过对该项目的研究,改进和提高JavaServer Pages 、Servlet、标签库以及面向对象的技术水准。Structs 框架的核心是一个弹性的控制层, Struts的目的是为了减少在运用MVC设计模型来开发Web应用的时间。MVC:它除了能在C层发挥巨大的作用外,在其它层也提供了相应的支持。
2.实例
说明:在此不会对实例作任何解释,只是一步一步写出它的最终运行效果:当我们在登录页面的用户框中输入“struts”时跳到成功登录页面,否则跳到登录失败的页面。

步骤一,搭建环境:下载struts-1.3.10-all.zi后解压,在lib目录下可以得到相关的jar文件,随后建立一个名为myStruts1.x的web项目,再把struts的jar文件导入到该项目(提示:通常我们会把一些常用的jar包先添加到用户库,在项目要用到这些jar包时,直接从用户库导入。)
步骤二,编写我们自己的AcitionForm:LoginForm,它很像一个javeBean,代码如下:
package com.asm;
import org.apache.struts.action.ActionForm;
public class LoginForm extends ActionForm {
private String username;
private String passwrord;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPasswrord() {
return passwrord;
}
public void setPasswrord(String passwrord) {
this.passwrord = passwrord;
}
}

步骤三,编写我们自己的Action:LoginAction,主要重写execute了方法,代码如下:
package com.asm;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
public class LoginAction extends Action {
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response)
throws Exception {
LoginForm loginForm = (LoginForm) form;
if (loginForm.getUsername().equals(“struts”)) {
return mapping.findForward(“loginSuccess”);
} else {
return mapping.findForward(“loginFailure”);
}
}
}

步骤四,编写两个配置文件:web.xml和struts-config.xml
(一)WEB-INF/web.xml文件:

<?xml version="1.0" encoding="UTF-8"?>
...省略文档声明
login.jsp action org.apache.struts.action.ActionServlet config /WEB-INF/struts-config.xml 0 action *.do (二)WEB-INF/ struts-config.xml文件: <?xml version="1.0" encoding="iso-8859-1"?> ...省略文档声明
<action-mappings>
	<action path="/login" type="com.asm.LoginAction" name="loginForm">
		<forward name="loginSuccess" path="/right.jsp"></forward>
		<forward name="loginFailure" path="/error.jsp"></forward>
	</action>
</action-mappings>
>>步骤五、编写三个jsp页面:login.jsp、right.jsp、error.jsp login.jsp主要代码: 用户名: br> 密 码:
right.jsp主要代码: 登录成功! error.jsp主要代码: 登录失败! >>步骤六:发布到服务器,测试。 说明:如果测试出现错误,请仔细检查配置文件。

二、分析struts的执行流程
1.流程图

2.分析过程:结合流程图
(1)当启动服务器后,首先会读配置文件,并加载ActionServlet这个总控Servlet(配置了0级加载)。 (2)当访问login.jsp时会把请求发送到服务器并返回这个login.jsp页面,当在login.jsp提交登录信息到login.do时,web服务器会首先把这个.do的请求交给ActionServelt这个总控处理(在web.xml文件中我们对总控配置了监听.do的请求)
(3)ActionServlet总控会根据

config
/WEB-INF/struts-config.xml
这些参数配置信息,读取到struts-config.xml文件,参配置文件后把这个ActionForm传递给LoginAction。
(4)当控制权交由LoginAction后,它会执行execute方法,并最终返回一个ActionForward对象,而这个ActionForward对象的返回是通过调用findForward方法并参照struts-config.xml文件中的

(5)当ActionServlet接受到这个ActionForward,实际上已包含了所要返回的页面及信息,然后返回这些页面信息给客户端。
3.分析配置文件struts-config.xml:
它的根元素为,在根元素下主要包括和两个子元素。下面分别说明:
:主要是用来配置ActionForm的子类,比如这里我们为LoginForm配置了如下内容:请特别留意这个name属性,因为它会被Action用到
:主要是用来配置Action的子类,这里我们为LoginAction进行了配置,注意的是在中配置了name属性,它的值和相关的Form的name值一样,后面再作更详细的解释。在子元素下还配置了跳转页面,因为在Action类中通常会用到这些配置好的跳转页面。
4.尝试使用错误法
(一)在action配置中去掉name属性或者写成与Form中的name属性不同的值
(二)不配置form,不配置action。
(三)login.jsp不提交给login.do,而提交给其它的.do
(四)把login.jsp中的的name属性值改成其它的值

三、struts的工作原理
1.读取配置
Struts框架总控制器(ActionServlet)是一个Servlet,我们在web.xml文件中配置成启动的Servlet。同时配置了struts-config.xml并在web.xml中把此文件作为一个参数配置给总控ActionServlet。也即是说,struts-config.xml最终会被总控ActionServlet使用。总控读取struts-config.xml后并为不同的struts模块初始化ModuleConfig对象(如ActionConfig、ControlConfig、FormBeanConfig、ForwardConfig、MessageResourcesConfig对象)。
2.发送请求
用户提交表单或调用URI向Web应用程序提交一个请求,请求的数据用HTTP协议上传给Web服务器
3.填充Form
结合上例,当派发的请求为*.do时,web服务器会转交给ActionServlet处理(Servlet处理模式)。同样,在交由ActionServlet处理时,会读取到下的配置struts-config.xml。从此配置文件中找到Action,而中的属性name属性指定了这个Action所关取的ActionForm. ActonForm被实例化并用HTTP请求的数据填充其属性,并且保存在SevletContext(request或session)中,这样它们就可以被Action调用。 总结:当递交一个*.do的请求时,总控ActionServlet根据请求数据来填充Form。
4.派发请求
完成Form填充后,ActionServlet会继续把这个请求及这个ActionForm(可以想成一个Bean)一并传给Action中的execute()方法。(可以在此方法中看到一个参数:ActionForm form. 而且在此方法中我们使用了LoginForm loginForm = (LoginForm)form;来进行强制转换,其目的就是为了得到ActionForm的子类的对象,以便后面可以直接获取这个实例所保存的属性)
5.处理业务
简单的说,就是Action类(具体表现为它的子类)调用其它业务逻辑模块进行相关处理。处理完成后会返回一个ActionForwad对象(此对象对应一个具体的jsp页面或另一个Action)给总控。
6.查找响应
当返回ActionForward对象给总控时,总控会根据这个返回的对象来找到一个对应的资源对象,通常是一个具体的jsp页面。
7.处理返回响应
把上一步找到的jsp页面处理结果返回给客户端。
8.再总结配置文件struts-config.xml
(1).下的每个就对应于一个ActionForm子类(用type属性指明所对应的ActionForm)
(2).下的每一个就对应于一个Action子类,同样用type属性指明了对应的Action,并让name属性来关联相应的ActionForm。 特别要注意在元素中必须配置path属性,且要以“/”来开头。在login.jsp中的action=…/login.do,/login和Action配置的path属性对应,“.do”和总控的Servlet映射配置相对应。 可以这样理解:在提交一个.do请求时只能被告知请求要转交给总控处理,而加上/login后,便能保证总控会把请求派发给相应的Action。
9.开发流程
(1)搭建环境 (2)编写Form并注册 (3)编写Action并注册 (4)编写jsp页面 (5)发布测试
说明:由于它们在开发中相互有依赖,所以建议在开发前最好有一个整体的构思图。

四、使用Dao的struts实例
1.需求分析及设计
设计此实例的作用一方面是为了加强对struts的进一步熟悉,另一方面是为了结合MVC的模式来感知软件开发的分工。 本实例的作用是当访问reg.jsp页面并填写相关信息提交后可以实现最终把数据添加到数据库。 简要的流程图如下:

开发列表:
Jsp页面View Servlet Cotroller Model

主要内容: reg.jsp
addSuccess.jsp
addFailure.jsp
ActionServlet
AddUserAction
AddUserForm User
UserDao
UserDaoImpl
说明事项: Action=”…/add.do” 特别注意配置文件 采用模拟方式
2.编写代码发布测试
AddUserFrom主要代码:
package com.asm;
import org.apache.struts.action.ActionForm;
public class AddUserForm extends ActionForm {
private String username;
private String password;
…省略get/set
}
注册–配置文件:

AddUserAction主要代码:
package com.asm;
public class AddUserAction extends Action {
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response)
throws Exception {
AddUserForm addUserForm = (AddUserForm) form;

	// 为调用业务逻辑代码做准备
	User user = new User();
	user.setUsername(addUserForm.getUsername());
	user.setPassword(addUserForm.getPassword());

	// 调用业务逻辑:
	UserDao ud = new UserDaoImpl();
	boolean addSuc = ud.addUser(user);

	// 根据业务逻辑处理结果返回
	String returnkeyWord = "addSuccess";
	if (!addSuc)
		returnkeyWord = "addFailure";
	return mapping.findForward(returnkeyWord);
}

}
注册–配置文件




reg.jsp主要代码:

用户名:

密 码:




逻辑块代码:
User实体类:
package com.asm;
public class User {
private String username;
private String password;
…省略get/set
}
}
UserDao接口:
package com.asm;
public interface UserDao {
boolean addUser(User user);
}
UseDaoImpl实现UserDao:
package com.asm;
public class UserDaoImpl implements UserDao {
public boolean addUser(User user) {
return false;
//模拟返回
}
}
分析:主要看AddUserAction,它调用了业务逻辑代码。这样可以实现分工编码:业务逻辑Model模块可以由其它的业务专业编码员编写,而我们在业务逻辑没有完成时,可以仿本例中模拟方式来检验struts所负责的C块能正确地被调用。
3.结合使用Hibernate
在addUser这个新的web项目下结合hibernate简要重写完成了此例。

五、struts关键组件
1.ActionServlet
ActionServlet:继承自javax.servlet.http.httpServlet类,称为中心控制器(也简称总控),它充分利用了servlet特点处理客户端请求(典型的配置是处理.do的请求)。它接受服务器转发的请求,填充Form,并派发请求给Action,接受ActionForward对象查找返回响应。在实例中我们为ActionServlet配置了如下的初始化参数:

config
/WEB-INF/struts-config.xml

如果我们没有上面的配置,struts默认会在…/WEB-INF下找struts-config.xml。它的工作原理:先查看是否有名为”config”的,如果有便用它对应的来作为参数给ActionServlet使用,如果没有便使用默认加载(即加载…/WEB-INF的struts-config.xml)。所以当我们只把上面的config改成“config1“,相当于认为没有使用名为”config“的,从而它会使用加载默认的配置。如果我们把/WEB-INF下的struts-config.xml改成”struts.xml”的名字,则一定要用config来配置,即/WEB-INF/struts.xml
总结:如果使用了config配置,的值一定是struts核心配置文件的名字(比如,前面多次用到的struts-config.xml文件就是struts核心配置文件,它主要用来配置Action和ActionForm的子类)。 如果不配置(一种情况是:完全没有及它包含的内容;另一路情况是:值不为config),便会使用默认的“…/WEB-INF/struts-config.xml”,所以一旦选择了不配置,那么struts的核心配置文件的名字就能是“struts-config.xml”。
2.Action [subClass]
(1)Action什么时候初始化? 当发出该action请求时初始化(而并非在读取配置时候)。测试方法:在LoginAction中增加如下内容:
public LoginAction() {
System.out.println(“检验什么时候初始化此Action”);
}
(2)每个Action只会被初始化一次,也就意味着所有的请求共享一个Action实例,所以Action是线程不安全的。因而也就引了一个安全性编程的问题,怎样实现安全编程:1.注意不要使用实例变量或者类变量共享只是针对某个请求的数据;2.注意资源操作的同步性。

(3)Action的不安全性应用:比如统计此Action被调用的次数。
package com.asm;
public class CountAction extends Action {
private Integer count=0;
//必须为count赋值:原因后面会同步count,如果不赋值,则同步锁的对象将为null,将会出现异常
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response)
throws Exception {
synchronized (count) {
count++;
}
PrintWriter out = response.getWriter();
out.println(“count=” + count);
return null;
}
}
配置:
说明:注意在execute方法中用到return null;这样就不会把请求转交到另一个页面,因此我们可以输出相应的数据到/count.do页面。后面将会对返回进行详细的讨论。
3.ActionMapping
在前面的LoginAction的execute方法中增加如下代码:
String actionName = mapping.getName();
String actionPath = mapping.getPath();
String actionType=mapping.getType();
System.out.println(“ActionName=” + actionName + “\t ActionPath=”
+ actionPath+"\t ActionType="+actionType);

	String[] forwardNames = mapping.findForwards();
	for (String forwardName : forwardNames) {
		ActionForward actionForward = mapping.findForward(forwardName);

System.out.println(actionForward+"\n"+actionForward.getName() + “—”+ actionForward.getPath());
}
当访问login.jsp发出该Action的请求时,控制台出现如下结果:
ActionName=loginForm ActionPath=/login ActionType=com.asm.LoginAction
ForwardConfig[name=loginSuccess,path=/right.jsp,redirect=false,module=null,extends=null,catalog=null,command=null]
loginSuccess—/right.jsp
ForwardConfig[name=loginFailure,path=/error.jsp,redirect=false,module=null,extends=null,catalog=null,command=null]
loginFailure—/error.jsp
然后结合前面对此Action的配置:




分析:在配置文件中即是一个org.apache.struts.action.ActionMapping类。path属性通过此类对象调用getPath()方法得到。同样name属性可以调用getName()方法得到 。而在此配置下的也就是一个org.apache.struts.action.ActionForward类。而在上面的测试中,可以通过mapping.findForward()方法来得到一个ActionForward对象。
进一步测试:当我们在上面继续增加一句:mapping.setPath("/other"); 再来请求该Action时,会报“Configuration is frozen”这样的异常。尽管我们可以自己来设定这个path不再为“/login”,但是作为struts类库设计者并不希望我们在程序中来设定这个path,而是希望通过配置文件来指定此path,所以当我们企图修改这个path,struts会报错来告诉我们此属性不应在程序中修改。由于此方法本身会被struts自身的其它的类调用,所以不能设为私有。这样就不可避免被客户端程序员调用,因此struts采取了报异常来解决此问题,这种思想是值得我们借鉴得。
4.ActionForward
(1).中有两个常用属性:name作为Action类中返回的值的一个索引,结合path属性可以查找到跳转到的页面。
(2).前面已经提到即是org.apache.struts.action.ActionForward类. 从这个类的api说明中我们可以知道redirect这个属性,此属性可以让我们灵活地选择跳转方式。 redirect:默认设为false,根据RequestDispatcher.forward 来进行跳转,此时应为path指定一个相对于当前应用的相对路径。 如果在配置中设为它true,则会采取HttpServletResponse.sendRedirect()来跳转,此时应为path指定一个绝对路径来实现服务器外部跳转。为了体现此属性,我们进行以下测试性配置:





结论:(1)设置redirect为true也是可以成功地完成RequestDispatcher.forward的服务器内部跳转。(2)如果要想用到HttpServletResponse.sendRedirect()这种跳转,除了要设置redirect的属性值为true外,还应为path属性指定一个绝对的路径(比如http://www.qq.com 注意不能省略http://协议头)(3)设定redirect为true时,发现省略path中的“/”,也能成功完成跳转
5.ActionForm [subClass]
(1).ActionForm是控制层的组件
While ActionForm beans often have properties that correspond to properties in your Model beans, the form beans themselves should be considered a Controller component. As such, they are able to transfer data between the Model and View layers.
(2)怎样完成填充ActionForm(有点javaBean的味道)

  1. 检查Action的映射,确定Action中已经配置了对应的ActionForm的映射
  2. 根据name属性,查找form bean的配置信息。
  3. 检查Action 中form bean的使用范围(request|session 默认为session,也即是说中的scope属性这两种属性值可配)确定此范围下,是否存在form bean的实例。
  4. 不存在这个实例,则重新构建一个实例,并且通过setAttribute()方法保存在scope所指定的作用范围内。存在则不需此步 补充说明:在这一步setAttrubute(“参数名-来自或者是中的name属性值”,“值就是ActionForm子类对象”)。 Action中的execute()方法中的参数ActionForm form也是这里设置的值。
  5. ActionForm bean的reset()方法被调用。
  6. 调用对应的setter()方法进行相关属性的赋值。实质完成了对ActionForm bean的填充。
  7. 如果中设置了validate属性为true(默认为true),会调用相应的validae方法进行校验,能正确校验则控制器将ActionForm作为参数传递给Action中的execute方法执行。
    说明:上面提到的reset和validate方法必须经过重写才会有实质性的作用。关于reset()方法的具体用处,可以参照api
    测试:在ActionForm中(实质是在它的子类中)增加它的构造方法,reset方法,validate方法, setServlet方法及在相关的setXXX方法都使用用System.out.println()打印信息来验证它们的执行顺序。根据打印的结果可以作如下图示说明:

测试:在ActionForm的子类中,对属性的设置取决于setXXX而与其中的属性变量名无关。比如,在表单中有一username参数,在提交后,我们通过setUsername来获取它的值。原理是:一旦提交表单,setUsername()方法会根据set后面的Username来关联http请求中username所对应的值作为这个方法的参数。可以通过对ActionForm子类中的相关进行修改来验证。总结:只要是http请求中的参数和ActionForm子类中的setXxx对应相等即可实现填充。
(3)使用监听器监听ActionForm的储存过程:
步骤一、编写监听器类,主要代码如下:
package com.asm.listener;
public class AttributeListener implements HttpSessionAttributeListener,
ServletRequestAttributeListener {
public void attributeAdded(HttpSessionBindingEvent event) {
String attName = event.getName();
if (event.getValue() instanceof LoginForm) {
System.out.println("************************************");
System.out.println(" add Session Attribute: \t attributeName="
+ attName + “\t attributeVlaue=” + event.getValue());
System.out.println("************************************");
}
}
public void attributeRemoved(HttpSessionBindingEvent event) {
System.out.println("************************************");
System.out.println(“Remove the session attribute: " + event.getName()
+ “\t” + event.getValue());
System.out.println(”************************************");
}
public void attributeReplaced(HttpSessionBindingEvent event) {
System.out.println("************************************");
System.out.println(“Replace the session attribute: " + event.getName()
+ “\t” + event.getValue());
System.out.println(”************************************");
}
public void attributeAdded(ServletRequestAttributeEvent event) {
String attName = event.getName();
if (event.getValue() instanceof LoginForm) {
System.out.println("************************************");
System.out.println(" add Request Attribute: \t attributeName="
+ attName + “\t attributeVlaue=” + event.getValue());
System.out.println("************************************");
}
}
public void attributeRemoved(ServletRequestAttributeEvent event) {
System.out.println("************************************");
System.out.println(“Remove the Request attribute : " + event.getName()
+ “\t” + event.getValue());
System.out.println(”************************************");
}
public void attributeReplaced(ServletRequestAttributeEvent event) {
System.out.println("************************************");
System.out.println(“Replace the Request attribute : " + event.getName()
+ “\t” + event.getValue());
System.out.println(”************************************");
}
}
步骤二、在web.xml中注册此监听器:

com.asm.listener.AttributeListener

说明:当tomcat启动时,提交该Action请求时会首次加载LoinForm并被这里监听器类add方法监听到,当第二次请求时,会调用replace时来修改此属性的值。 改变配置文件中scope的属性值,用此监听器可以证明到中的scope的默认属性值为“session”。
(4)Action中验证
在LoginAction中增加如下代码:
LoginForm loginFormInScope = null;
if (mapping.getScope().equals(“request”)) {
loginFormInScope = (LoginForm) request.getAttribute(“loginForm”);
} else {
loginFormInScope = (LoginForm) request.getSession().getAttribute(
“loginForm”);
}
if (loginFormInScope == form) {
System.out.println(“scope中的Form与参数form是同一个”);
}
最终证明存到scope中的LoginForm和参数中的form是同一个。
六、总结属性:
说明:此属性位于下,它实质是对应于org.apache.struts.action.ActionMapping类。因此它的属性都可以在该类的字段中找到。 值得一提的是此如果取名成将更助于理解。比如在下设计的的取名就很合理。

  1. path属性:…
  2. name/attribute属性:它们都是用来关联ActionForm类名。但是实质性起作用(实质性起作用的意思是在Action类的execute()方法中的ActionForm参数所关联的类是此属性所指定的类)的是attribute,而通常情况下我们不设此属性,因为它会自动把name属性的值获取作为它本身的值attribute,也即是说attrubute缺省值为name属性的值
  3. type属性:Action [subClass]完整类名
  4. scope属性:ActionForm bean的存储范围,我们可以通过以下代码scope的默认属性为session:
  5. validate属性:此属性是用来控制是否进行校验。缺省值true,即进行校验。但是我们在ActionForm子类中如果没重写此方法,它会调用ActionForm类本身的方法,而ActionForm类中的此方法只是返回一个null值,这样校验总是通过。因而要想实质性地校验,必须重写此方法。 如果我们不想校验,可以直接设其属性为false,这样即使我们重写了validate方法,也不会调用此方法进行校验。即是说不会执行validte方法中的内容。 说明:校验通常会依赖校验方法中的ActionMapping mapping参数来进行一些校验性操作。补充说明:如果validate返回null,或者返回的ActionErrors对象没有错误信息页面(an ActionErrors object with no recorded error messages)
  6. input属性:前提是valiate=true,这样即是说Action所对应的ActionForm要校验,如果校验失败,则返回input所配置的页面。
    public ActionErrors validate(ActionMapping mapping, HttpServletRequest request) { System.out.println("validate method “);
    ActionErrors errors = new ActionErrors();
    // 注意以下两句:如果注释掉以下两句,校验也无实质性的作用,即顺利通过校验
    ActionMessage message = new ActionMessage(“errors”);
    errors.add(“errors”, message);
    return errors; }
    然后,在中增加validate=“true” input=”/error.jsp"这两个属性。意为:如果该action所引用的ActionForm校验出错,将会返回input属性所指定的页面。 强调说明:要想input起作用,这里validate必须设为true,当然也可以不设,因为validate缺省的就是true. 另外如果这里验证失败则会返回到指定页面(这里指跳到error.jsp页面), LoginAction中的代码将不能得到执行。这里采取的是无论怎样都会验证失败,所以LoginAction中的代码将不能得到执行。

七、全局跳转:
在元素下有一个元素。此元素就是用来配置全局跳转的。比如进行如下配置:



进行此配置,只要在Action的execute方法中返回的是return mapping.findForward(“glbal”);便能让页面跳转到global.jsp页面。理解实质:就是把全局下的配置信息增加到所有的下。比如这里就是把增加成:


八、struts核心类

  1. ActionServlet
  2. ActionForm
  3. Action
  4. ActionForward
  5. ActionMapping
    知道这5个核心类后,我们可以试着来写一个简易的struts,以加深struts原理的理解。其中的核心实现就是ActionServlet。

九、struts标签库分析
1.使用bean标签
(1)使用准备
bean (struts-bean.tld) uri: http://struts.apache.org/tags-bean 如何获取此uri:
方式一是通过“struts-taglib-版本号.jar”中META-INF下的tld文件夹下相关的tld文件获取。
方式二是通过官方提供的文档查找到taglib可以获取关于所有标签的详细信息。
(2)define标签
Define a scripting variable based on the value(s) of the specified bean property.
实例1: BeanTagTest.jsp 主要内容如下:
<%@ page language=“java” pageEncoding=“utf-8”%>
<%@ taglib uri=“http://struts.apache.org/tags-bean” prefix=“bean”%>

...省略 <% String user = "jack"; request.setAttribute("username", user); %> newUser: 执行效果如下: newUser: jack 分析:首先应使用“taglib”来导入要使用的标签的uri等信息。 id:定义这个新的bean的id值以便后面引用(比如在bean:write中就用到了这个name属性值) name:旧的bean的存储名字(此名字就是request中设定的“username”属性) scope:指定旧的bean的查找范围,默认是PageContext。即是说会在此范围内查找旧bean存储。 toscope:指定新bean的存储范围,默认也是page。即只能在当前页面引用。 说明:关于以上属性,可以分别进行测试。需要说明的是,不建议指定旧bean的查找范围,因为默认会依次到page,request,session,application查找。但是如果我们要查找指定的范围的旧bean,则可以设定scope属性。 强调两点:1.指定scope查找旧bean的范围为当前页,应用scope=”pageContext”,而不是scope=”page” 2.一旦指定了scope属性,默认的查找方式就不起作用了。 实例二:BeanTagTest2.jsp 主要内容如下: <%@ page language="java" import="java.util.*" pageEncoding="utf-8" import="com.asm.User" isELIgnored="false"%> <%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean"%> ...省略 <% User user = new User(); user.setUsername("jack"); user.setBirthday(new Date()); request.setAttribute("user", user); %> user userName: ${pageScope.userName}
user birthday: ${pageScope.userDate}
user: ${pageScope.newUser}
输出信息: user userName: jack user birthday: Mon Sep 28 10:26:24 CST 2009 user: com.asm.User@10fba68 分析:测试前,请确保servlet默认支持el,如果默认不支持,请使用“isELIgnored="false"”,另注意导入要使用的User类。property:有了实例一的知识,这里就能很好理解这个属性,上例存储的是一个字串对象。而这里存储的是一个一般的bean对象,如果只凭id,而不设property它将打印出这个类的hash码信息。而用property设定这个bean类的属性,它将返回的是这个设定属性的值。 补充:如果在一个define标签中定义两个property属性,后定义的将会覆盖前property属性。 User.java就是在“使用Dao的struts实例中”定义的user (3)write标签 Render the value of the specified bean property to the current JspWriter. 实例三,在实例二的基础上增加如下内容: 使用bean:write
username:
user: 输入信息: 使用bean:write username: jack user: com.asm.User@1baa8d8 说明:name为关联的bean对象名,property意为bean对象的属性,scope指定查找bean的范围。 (4)message标签 Render an internationalized message string to the response 步骤一,在com.asm包下建立三个资源文件:myResources.properties、myResources_zh.properties、myResources_ar_OM.properties。其中myResources.properties的内容如下: greeting=welcome you username=userName password=password login=login 其它两个文件的内容只是后面的值不同。 在struts-config中添加配置信息:特别要注意parameter指定基名包括包名 页面中使用: :
	<bean:message bundle="keyOne" key="password" />
	:<input type="password" name="password	"><br>
	<input type="submit" value="<bean:message bundle="keyOne" key="login"/>">
</form>
说明:资源文件,一般写在一个包下。比如上面的写在com.asm包中。而myRescources为资源文件的基名,后面加的_为具体的国际化国家语言代码。配置文件中的parameter参数用来指定基名。它会结合浏览器的语言信息来选择合适的资源文件。举例,如果我们在浏览器中选择[工具][IE选项][常规][语言]中设定为“ar-om”,这样结合parameter指定的基名+ar-om便会找到myResources_ar_OM.properties资源文件(注意要大写OM),另外还要注意资源文件所用的连接符是“_”。 配置文件中的key是为这个资源文件取的一个名字,以便后文件直接引用。 jsp页面中,key为资源文件的键名,它会根据key所指定的键名自动找到键值。 补充说明:最初写这个的时候是新建了一个项目,所以忘记了在web.xml文件中配置ActionServlet,后来一运行发现“Module 'null' not found.”。 强调:这里主要是说明使用标签库好像没有用到ActionServlet实现控制,但实际上我们在struts-config.xml文件中配置了资源文件信息是要被总控读到才能去执行jsp中bean:message标签,所以仍然要正确配置总控“ActionServlet”。总结:一用到struts就不要忘记了把总控配置到web.xml中去 。 另外需要特别说明:浏览器设置语言为中文时,存在乱码待解决。 eclipse中一般文件的默认编码:比如在这里我们希望中文件资源文件myResources_zh.properties的键值能在eclipse进行中文编译。而在eclipse下properties文件的默认编码为ISO-8859-1,所以不能直接在eclipse下把中文字存储在文件中(工程所在的目录用txt打开是可以保存汉字但又会存在乱码),但我们可以修改编码为UTF-8。步骤:[windows][preferences][General][Content Types][Text][java Properties File],然后会看到Default Encdoing,设置成“UTF-8”,点Update即可。其它的一般文件默认编码修改可以仿此。 2.使用logic标签 实例一,LogicTagTest.jsp主要内容: <%@ page language="java" pageEncoding="utf-8" isELIgnored="false"%> <%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic"%> <% String[] names = { "jack", "tom", "lily" }; request.setAttribute("names", names); %> 使用logic:iterate实现迭代:
${name}      输出信息: 使用logic:iterate实现迭代: jack tom lily 分析:要求所迭代的对象必须是:must be an Iterator, a Collection, a Map (whose values are to be iterated over), or an array。 Id、name属性相当于foreach中的for(id:name)。 实例二、LogicTagTest2.jsp主要内容: <%@ page language="java" pageEncoding="utf-8" import="com.asm.Student,java.util.List,java.util.ArrayList" isELIgnored="false"%> <%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic"%> bean test <% Student s0 = new Student(); s0.setAge(18); s0.setName("mary"); String[] sint0 = { "coding", "shopping", "sing" }; s0.setInterest(sint0);
	Student s1 = new Student();
	s1.setAge(18);
	s1.setName("jack");
	String[] sint1 = { "play Game", "coding", "sing" };
	s1.setInterest(sint1);

	Student s2 = new Student();
	s2.setAge(18);
	s2.setName("troy");
	String[] sint2 = { "play football", "jump", "sing" };
	s2.setInterest(sint2);

	List students = new ArrayList();
	students.add(s0);
	students.add(s1);
	students.add(s2);
	request.setAttribute("ss", students);
%>
<body>
	student message:		<br>
	<logic:iterate id="s" name="ss" scope="request" offset="0" indexId="i"
		length="3">
		学号:${i} &nbsp;&nbsp;  ${s.name} ( ${s.age}) &nbsp;&nbsp;&nbsp;&nbsp;
		<logic:iterate id="ins" name="s" property="interest">
			${ins}
		</logic:iterate>
		<br>
	</logic:iterate>
</body>
输出信息: student message: 学号:0 mary ( 18) coding shopping sing 学号:1 jack ( 18) play Game coding sing 学号:2 troy ( 18) play football jump sing 分析:Student类就相当于一个bean,通过上面的内容,也不难知道它的定义。在此实例中的内层迭代主要展示了通过name+property共同指定迭代对象。其中offset指定迭代对象开始的索引(默认为0开始),length为总的迭代次数(如果超过最大迭代长度则按最大迭代),indexId为索引值,比如我们在上面把索引值赋给了变量i,在每次迭代后i会增加。我们通过el表达式输出i。 3.其它标签参考文档 十 、使用DynaActionForm自动填充Form 1.引入动态Form 在前面我们已经知道,ActionForm是用来获取表单提交过来的信息。而为了具体的处理,我们必须写一个ActionForm的子类作为表单数据的存储bean。 其实,我们也可以把数据填充到struts自带的动态From。这样就可以不必写ActionForm,但是却要为这个DynaActionFrom写好配置文件。 2.实例演示:新建项目dynaForm reg.jsp页面内容如下: username:
password:
age:
birthday:
web.xml配置内容如下: 和以前的配置一样,主要配置ActionServlet总控 struts-config.xml主要配置内容:
<action-mappings>
	<action path="/reg" type="com.asm.RegAction" name="dyna">
		<forward name="suc" path="/suc.jsp"></forward>
	</action>
</action-mappings>
分析此配置:下的type不再是我们自己写的Form,而是struts的DyanActionForm。后面的是对这个动态From进行的属性配置。注意的是里面的type属性必须是一个完整的包名,不能是简单类型(比如int类型,必须写成Integer)。 理解这个配置:把DynaActionForm想成就是我们自己写的ActionForm,而用来设定这个bean的属性就行了。 RegAction内容如下: package com.asm; public class RegAction extends Action { public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { DynaActionForm dyna = (DynaActionForm) form; String username = (String) dyna.get("username"); String password = dyna.getString("password"); int age = (Integer) dyna.get("age"); java.sql.Date birthdaySql = (java.sql.Date) dyna.get("birthday"); java.util.Date birthday = birthdaySql;
	System.out.println("success:");
	System.out.println("usrename=" + username + "\t password=" + password
			+ "\t age=" + age + "\t birthday=" + birthday);
	return mapping.findForward("suc");
}

}
说明:转成DynaActionForm后对象后,它调用get方法返回的是一个对象Object,由于bean中本身存储的是我们前面在中写的对象类型。所以它进行强制转换成我们所写的对象类型。 补充说明:关于它的其它操作,可以参照DynaActionForm类的Api
reg.jsp内容如下:
<%@ page language=“java” pageEncoding=“UTF-8” isELIgnored=“false”%>

${dyna.map.username} ${dyna.map.password} ${dyna.map.birthday} 最后,我们选择无论输入任何信息都会跳到此reg.jsp页面,在reg.jsp页面中我们利用el表达式来获取存储的dyna对象的相关属性,需要注意的是这里引用dyna对象,名字来源: 中的name属性值。而map是必须的,因为动态form会的对象属性会被存成一个map形式,所以加上map引用。至于为什么要用map,这里只须记住,动态form的属性访问必须加map就行,而至于原因可以参考文档或查阅相关资料。非动态form不需要加map,可以在前面实例的基础上加上一些代码测试。 十一、struts中的异常处理: 1.引入异常处理 从前面的操作中我们知道了Action中的execute()方法进行相关的逻辑处理后,最终会返回一个ActionForward,而返回的这个对象,就是通过mapping.findForward(returnUri)这样的形式并查找相关的配置找到真正的返回页面。 但如果不是返回这个对象呢,如果抛出一个异常呢?其实通过一些操作,我们知道,execute的返回有三种形式。结合前面的struts的工作原理来分析,我们知道它的返回可以是:1.停留在本页面中(提交的*.do中),返回null 2.返回一个异常 3.返回ActionForward并结合配置页面真正处理(最主要的方式)。 如果方法要求有返回值且方法声明抛出异常时,其实也是可以返回(抛出)一个异常,而不返回本身所要求的返回类型。 2.实例演示:新建项目strutsException LoginForm代码如下: package com.asm; public class LoginForm extends ActionForm { private String username; private String password; ...省略get/set方法 } MyException代码如下: package com.asm; public class MyException extends Exception { public MyException(String mes) { super(mes); } } LoginAction代码如下: package com.asm; public class LoginAction extends Action { public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { LoginForm lf = (LoginForm) form; if ("exception".equals(lf.getUsername())) { throw new MyException("输入了exception,出现异常"); } else { return mapping.findForward("other"); } } } 分析:当我们在登录页面login.jsp中输入“exception”时会抛出异常,这时根据配置文件会跳到相应的页面,输入其它信息,跳到other所指向的页面。 struts-config.xml配置文件内容: web.xml省略,内容就是配置总控ActionServlet
<action-mappings>
	<action path="/login" type="com.asm.LoginAction" name="loginForm">
		<exception type="com.asm.MyException" key="mes"
			path="/ex.jsp">
		</exception>
		<forward name="other" path="/other.jsp"></forward>
	</action>
</action-mappings>
<message-resources parameter="com.asm.myResources"></message-resources>
分析配置文件:重点看配置,结合LoninAction来看,当抛出MyException异常时,它会到该action所配置的对应异常下找到path所指向的路径。比如这里配置的意思是当有com.asm.MyException被抛出时,跳到ex.jsp页面。对配置说明,在Action中抛出一个异常对象,它会根据type类型来关联。如果关联到这个配置就根据path来跳转。 其中key是配置的资源文件的键名,它会在资源文件中查找键值,比如在ex.jsp(代码见下)中用到标签来打印这个键值信息。 需要说明的是,errors能直接打印键值信息,是因为exception设定了跳转页面为ex.jsp并向这个页面传了key。可以这样理解,当异常匹配后,它会把key所关联到的资源文件信息送给它所指向的jsp文件,这样这个jsp文件就可以直接用errors打印这个异常信息。 属性配置说明:path:出现异常后的跳转页面,key:异常信息的键,对应的值在资源文件中,type:所要处理的异常类型。 ex.jsp页面主要内容 <%@ page language="java" pageEncoding="UTF-8"%> <%@ taglib uri="http://struts.apache.org/tags-html" prefix="html"%> 输入"exception",出现异常:

返回 Login.jsp主要内容: userName:
password:
资源文件myResources.properties内容(在com.asm包中): mes=invalidation:exceptions(Resources File) 当输入exception时,执行效果如下: 输入"exception",出现异常: invalidation:exceptions(Resources File) 返回

十二、struts中的插件Plugin:引入hibernate
1.实例准备步骤
步骤一、新建名为“strutsPlugin”的项目,并把hibernate jar包及struts jar包导入该该项目。
步骤二、在src目录下建立hibernate.cfg.xml文件,实现对hibernate的主配置。
步骤三、编写HibernateUtil.java文件,备用。内容如下:
package com.asm;
public class HibernateUtil {
private static SessionFactory sf;
private static Configuration cf;
private HibernateUtil() {
}
public static SessionFactory openSessionFactory(String config) {
System.out.println("…");
cf = new Configuration();
cf.configure(config);
sf = cf.buildSessionFactory();
return sf;
}
public static Session getSession() {
return sf.openSession();
}
}
步骤四、编写插件类:HibernatePlugin 主要内容如下:
package com.asm;
public class HibernatePlugin implements PlugIn {
private String hibernateConfigFileName = null;
public void destroy() {
System.out.println(“destroy method invoked”);
System.out.println(“destroy method invoked over”);
}
public void init(ActionServlet servlet, ModuleConfig config)
throws ServletException {
System.out.println(“init method invoked”);
HibernateUtil.openSessionFactory(hibernateConfigFileName);
System.out.println(“init method over”);
}
public void setHibernateConfigFileName(String hibernateConfigFileName) {
System.out.println(“set method invoked”);
this.hibernateConfigFileName = hibernateConfigFileName;
}
public String getHibernateConfigFileName() {
return hibernateConfigFileName;
}
}
步骤五、编写struts-config.xml文件(web.xml文件主要完成主控ActionServlet配置)





重启服务器或重新加载该项目到服务器均会执行。效果如下:
set method invoked
init method invoked

init method over
2.分析配置文件及插件说明
执行步骤:当服务启动时,总控会在struts-config目录下查找元素,如果找到则对插件执行初始化,即先参考子元素执行setXXX方法,再执行init方法进行初始化,最后会执行destory方法。在hibernate的使用中,最为耗时的就是读取配置文件和打开Session,在本例中主要实现了在服务启动时自动完成读取hibernate的配置和打开SessionFactory备用。强调说明:property指定的名字为HibernatePlugin中的setXXX方法的名字XXX首字小写。 测试时,只须重新加载项目到服务器,它便会去执行初始化。
十三、ForwardAction实现统一开发及目录屏蔽
1.说明:在以前的开发中,我们知道WEB-INF目录下的文件是不能被访问的,因为服务器对这个目录实现了保护。如果别人可以访问你的配置,这将存在相当大的安全隐患,所以服务器对配置目录实现了屏蔽保护。以前我们通过配置servlet来访问WEB-INF目录下的文件,过程较繁琐。现在有了struts,将简化这一过程。
2.在web.xml中给ActionServlet总控配置的是以.do的形式访问。在WEB-INF目录下建立两个文件分别test.jsp和test2.jsp。在WebRoot根目录下建立MyJsp.jsp文件,并在struts-config.xml作如下配置便可以.do形式来访问了。



配置完成后,输入上下文加“previa.do”、“previa2.do”或root.do便可以分别访问test.jsp、test2.jsp或MyJsp.jsp了。 这样做的目的:实现文件保护,实现文件块统一访问。关于此实现的参数及详细内容可以结合它相关的类 org.apache.struts.actions.ForwardAction及官方文档说明

十四、DispatchAction统一Action:
数据库的操作,实质就是CRUD四种操作。如果要针对这四种操作分别写Action,将要写四个Action,但struts提供的DispathchAction能够把多个Action集中起来,然后通过参数分别派发给相应的方法执行。 实例如下:

步骤一,创建DataAction类.内容如下:
package com.asm;
public class StudentAction extends DispatchAction {
public ActionForward addStudent(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response) throws Exception {
PrintWriter out=response.getWriter();
out.println(“add Student successs”);
return null;
}

public ActionForward deleteStudent(ActionMapping mapping, ActionForm form,
		HttpServletRequest request, HttpServletResponse response) throws Exception {
	PrintWriter out=response.getWriter();
	out.println("delete Student successs");
	return null;
}

public ActionForward updateStudent(ActionMapping mapping, ActionForm form,
		HttpServletRequest request, HttpServletResponse response) throws Exception {
	PrintWriter out=response.getWriter();
	out.println("update Student successs");
	return null;
}

public ActionForward queryStudent(ActionMapping mapping, ActionForm form,
		HttpServletRequest request, HttpServletResponse response) throws Exception {
	PrintWriter out=response.getWriter();
	out.println("query Student successs");
	return null;
}	

}

步骤2,配置struts-config.xml如下:

步骤3,测试访问:在浏览器中输入…/student.do?crud=updateStudent.将会引起StudnetAction类中updateStudent方法执行。 如果要想访问deleteStudent()方法,只须将最后参数值改为deleteStudent即可。其它的类似。
注意事项:步骤一中所写的DispatchAction子类StudentAction中的四个方法必须与DispatchAction类中的execute方法同参(顺序个数类型均相同)同返回值,可以试下不同的报错情况。步骤二中的配置重要的就是parameter参数,它的值是任意的。步骤三中crud=“StudentAction中的方法名”。 另外特别要注意子类不能覆盖DispatchAction的exectue方法,因为在DispatchAction的execute方法中所做的工作就是根据传递的参数来分别调用它的子类相应的方法。
思考:1.不传参数直接访问会报什么样的错 2.在StudentAction中可以直接把out对象声明成一个全局变量吗? 3.注意这里的没有配置name属性来关联ActionForm。 4.如果此action关联了actionForm,这意味着所有的方法都会依赖于这个ActionForm,但是如果这些方法中有的方法并不会依赖于任何ActionForm,或者说依赖的ActionForm并不相同,怎么办?解决方法是要么都不使用ActionForm(比如这里的例子),要么就只是把使用相同的actionForm的action归结到一个action中。
总结:这种派发的关键是传递参数,而参数的传递取决于配置中parameter及DispatchAction子类的方法名。 过程 .do请求派发给总控,总控再派发给StudentAction,根据参数再分别调用相应的方法。
补充:在上面的思考第4点问题中,还有一个解决方案就是为同一个Action配置不同的映射,针对上例需要做的修改为:(1)修改StudentAcion类:修改让它继承自MappingDispatchAction类,(2)struts-config.xml配置大致这样的:

现在为parameter配置的是方法名。
访问AddStudent方法时,请求地址为:…/stuAdd.do 。
访问delStudent方法时,请求地址为 :…/stuDel.do?会带actionForm相关的参数(也可以是表单隐藏传递),在struts2中也有类似MappingDispatchAction这样的做法。 注意:DispathcAction被设计成抽象类,但是它的它的子类MappingDispathchAction并未设计成抽象类。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

weixin_44468045

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值