MyEclipse7.5快速半手工开发SSH2(Struts2+Spring+Hibernate)应用之“超光速”教程
1. 新建一个Web Project,命名为:struts2_template
2. 导入样例程序struts2-blank-2.1.8.war中lib下的包
(暂时只需要这几个,网上提醒不能随便加包!):
commons-fileupload-1.2.1.jar
commons-io-1.3.2.jar
freemarker-2.3.15.jar
ognl-2.7.3.jar
struts2-core-2.1.8.jar
xwork-core-2.1.6.jar
如果出现类似Unable
to load configuration. - bean - jar:file:
的错误,一般可能是导入的jar包有问题了。但是,当你更正了问题之后
错误还出现!这是因为你没有将发布到tomcat中的应用移除干净!!
方法是:退出MyEclipse,到workspace文件夹中,找到原来的工程文件夹,删除之;
然后再到workspace的
.metadata\.me_tcat\webapps
文件夹中,找到相应的发布文件夹也删除之,然后重新启动MyEclispse就可以了。
提醒:如果你做了许多工程,后来删除了。。。。其实都还在
.metadata/.me-tcat/webapps下面!!!如果他们有错,就会影响到你后来的工程!
3. 在web.xml中加入支持struts2的filter,并将mapping设为/*
Filter的名字叫“struts准备与执行”,就是strutsPrepareAndExecute,敲入前面 几个字母系统就会找到了。
-------------------- 到此为止,我们的工程已经可以支持struts2了。下面是实验 --------------
4. 在src目录中拷贝一个struts.xml作为书写配置的起点
(注意!和以前版本不同,是src目录!)
一个最简单的配置文件包括下面内容:
<?xml
version="1.0" encoding="UTF-8" ?>
<!DOCTYPE
struts PUBLIC
"-//Apache Software Foundation//DTD
Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<package name="struts2_template" extends="struts-default">
<action
name="HelloWorld" class="example.HelloWorld">
<result
name="gotoIndex">/index.jsp</result>
</action>
<!-- Add actions here -->
</package>
<!-- Add packages here -->
</struts>
|
其中重要的其实就一个:action的执行和导航。上面的
<action name="HelloWorld"
class="example.HelloWorld">
表示,当从URL请求名为HelloWorld的action时候,实际执行的是example.HelloWorld类。
附注:类的分包:在src下面建包即可。
<result
name="gotoIndex">/index.jsp</result>
表示当HelloWorld返回字符串“gotoIndex”时候,跳转到/index.jsp。
5. 下面建一个实现上面action的类。
先在src下面建一个package,名为example;
下面建一个HelloWorld类,继承ActionSupport(在com.opensymphony.xwork2中)
里面就一个函数:
public String execute() throws Exception {
System.out.println("Hello from
www.WarpSpeedEnglish.cn");
return "gotoIndex";
}
6. 运行一下试试,访问URL为/HelloWorld.action
浏览器还是显示index.jsp,因为这个action自动跳到index.jsp了,
但console窗口却出现了提示,说明该action已经运行。
=========
好了,我们的action已经配置成功了 ===========
7. 下面我看看表单的提交;表单当然是放在jsp文件中。
我们将jsp文件放到WebRoot下面(!这才是网站的根目录!)。
作为实验,我们建一个子目录login来存放我们的样例表单login.jsp;
(遗憾的是现在还没有理想的可视化的编辑struts2表单的IDE)
一个最基本的jsp文件如下:
<%@ page contentType="text/html;
charset=UTF-8" %>
<%@ taglib prefix="s"
uri="/struts-tags" %>
<html>
<body>
</body>
</html>
也就是说,最上面两行最关紧要!拷下即可。
然后,使用DreamWeaver之类,编写相应的表单:
下面是使用Eclipse自动生成的带表单的一个简单页面:
------------------------------------------------------------------------
<!DOCTYPE
HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>www.WarpSpeedEnglish.cn</title>
<meta http-equiv="keywords"
content="keyword1,keyword2,keyword3">
<meta http-equiv="description"
content="this is my page">
<meta
http-equiv="content-type" content="text/html;
charset=UTF-8">
<!--<link rel="stylesheet"
type="text/css" href="./styles.css">-->
</head>
<body>
<form name="f1"
id="f1" action="" method="post">
<table border="0">
<tr>
<td>Login:</td>
<td><input
type="text" name="login"
id="login"></td>
</tr>
<tr>
<td>Password:</td>
<td><input
type="password" name="password"
id="password"></td>
</tr>
<tr>
<td colspan="2"
align="center"><input type="submit"></td>
</tr>
</table>
</form>
</body>
</html>
-----------------------------------------------------------------------------
我们做一下修改即可将它变成struts2的jsp表单页面:
(1)第一行替换成struts2要求的那两行<%@...
(2)替换所有表单字段。例如:
<form name="f1"
id="f1" action="" method="post">
替换成:
<s:form
action="Login">
注意:这里的action="Login"指明要提交给一个名为Login的action
<input type="text"
name="login" id="login">
换成:
<s:textfield
name="login" />
<input
type="password" name="password"
label="password">
替换成
<s:password
name="password" />
<input
type="submit">
替换成:
<s:submit/>
惊奇小发现:我在MyEclipse7.5中发现一个Web Page Editor,用这个编辑器可以插入struts2的标签!
虽然还很是primitive,但总是好多了,。。。
好了,改名存盘为login.jsp。
8. 我们的action还没有呢!好办,将HelloWorld.java拷贝粘贴一个出来Login.java:
package example;
import com.opensymphony.xwork2.ActionSupport;
public class Login extends ActionSupport {
public String execute() throws Exception {
return "toIndex";
}
}
再做这样一些工作:
(1)把表单字段写出来:
private String login;
private String password;
(2)菜单Source->Generate
Getters & Setters生成所有的getter和setter方法。
注意了啊!看似简单,实际上内涵极为深奥:这里的setter可是struts2“注入”调用的
(当我们执行execute()的时候,表单字段已经设置好了!)
(3)修改执行函数,把表单的输入打印到控制台(console,这样打印没什么用处,只是试验一下而已):
public String execute() throws Exception {
System.out.println(login
+ ":" + password);
return "gotoIndex";
}
9. 好了,现在东西都有了,只要配置一下struts.xml,就可以让表单login.jsp提交给Login.action了:
拷贝一份原来的action, 修改一下:
<action name="Login"
class="example.Login">
<result
name="gotoIndex">/index.jsp</result>
</action>
10. 部署并运行看看
---hmm, 还可以,console打印出来输入的值,页面跳到index.jsp了。。。但是!
好像输入表单本身的现实有些怪!为什么呢?
看看页面源码:怎么多出来许多table标签呢?
-----原来,默认模式下,struts2会将你的form标签自动加上table标签!
可是我们已经做好了table了,美工过了!
对策:简单:类似下面那样加上这样theme属性即可:
<s:textfield name="login"
theme="simple" />
要求struts使用simple主题,它就不多事了。
11. 数据的显示:
Struts将action处理的数据(包括表单提交给action的数据)和应用(Application)范围内的数据
都存储在一个叫做“值栈”(valueStack)的地方;有很多种方法可以从这里取出值来:
我们将struts.xml中Login.action的配置改一下,让它转到logok.jsp页面:
<action name="Login" class="example.Login">
<result name="gotoOk">/login/logok.jsp</result>
</action>
然后做一个logok.jsp, 放到WebRoot/login目录下,
其内容主要是在body标签中间,加上:
<s:text
name="login"/><hr/>
<s:property name="login"
/><hr/>
${login}<hr/>
这样3行代码;
运行一下试试。可以看到,三种方法都可以将输入的用户名显示出来。
12. 老革命的老问题:汉字问题
试试输入一个汉字,显示出来的是乱码。
解决:方法很多;不过既然我们要整合Spring, 不如现在就使用Spring的解决模式:
13. 加入Spring支持:
在工程名上点鼠标右键,在菜单中选择“MyEclipse”->“Add
Spring Capabilities”;
选中所有Spring开头的包;
选择“拷贝支持包到lib目录”;另注意:
(1)注意将applicationContext.xml放到WEB-INF/目录下;
(2)增加一个listener:ContextLoaderListener
(3)删除掉重复的包:asm和cglib
14. 解决汉字乱码问题:
在web.xml中,增加一个filter:
org.springframework.web.filter.CharacterEncodingFilter
并设置两个参数:
encoding=UTF-8
forceEncoding=true
设置其mapping为/*
再输入汉字试试,没问题了吧!
(没有告诉你的小秘密:如果你还是出错误,那么在web.xml的filter配置中,
看看是否encoding filter放到了struts
filter的后面...提到(map到)前面来!)
15. 增加Hibernate支持
现在干脆将Hibernate的支持一并加入:
只需要注意几点:选中前面三个包;拷贝到lib目录;使用Spring的配置文件。
16. 启动数据库。
我们使用MyEclipse自带的“德贝(Derby)”数据库:
Window->Show->DB
Browser中,双击MyEclipse Derby即可启动数据库。
17. 逆向工程生成ORM对象和DAO类
在Derby数据库中,找到CLASSICCARS, 右击CUSTOMER表,选择Hibernate 逆向工程
选择Java src folder(我们以前已经创建了一个src/hib目录,就用它)
选中三个复选框,但不生成抽象类。
Finish!
18. 看看我们完成了什么:
hib目录下面生成了许多类,其中最重要的是CustomerDAO,我们可以通过其中的
getFromApplicationContext(ApplicationContext
ctx)
静态方法得到它的一个实例,就可以开始数据操作了!
19. 将HelloWorld.java复制一个到ListCustomers.java
然后修改其execute方法:
public String
execute() throws Exception {
ApplicationContext ct = WebApplicationContextUtils
.getWebApplicationContext(ServletActionContext
.getServletContext());
CustomerDAO dao =
CustomerDAO.getFromApplicationContext(ct);
List<Customer>
list = dao.findAll();
int n =
list.size();
for (int i = 0; i < n; i++) {
Customer
c = list.get(i);
String name = c.getCustomername();
String phone = c.getPhone();
System.out.println(name
+ ":" + phone);
}
return "gotoCustomerList";
}
|
直接从浏览器地址栏访问一下这个action:
http://localhost:8080/ListCustomers
会发现客户的信息已经打印到console中了,只是由于没有配置导航,所以页面显示404错误。没关系,下面fix这个问题。
遗留问题:
网上有说:action当然不应该封装业务逻辑,但是action是controller,比如在
一个action里面可能会由几个Business Service的方法组成一个流程,
如果仅对service的方法实现事务控制,那如果流程中某一个service
的方法出现了问题,流程中之前调用的其他service怎么回滚呢。
20. 在页面view显示结果
先在struts.xml中配置导航:
<action name="ListCustomers"
class="example.ListCustomers">
<result name="gotoCustomerList">/login/customerlist.jsp</result>
</action>
上面导航要求控制跳转到customerlist.jsp,因此我们需要在login子目录中,新建一个customerlist.jsp,其内容可先从原来的页面中拷贝过来。
核心代码:
<s:iterator
value="list" >
name:
<s:property value="customername" /><br/>
city:
${city}<br/>
address:
<s:text name="addressline1"/><p>
</s:iterator>
|
这里面,我们尝试了几种不同的表现方法。
首先,是<s:iterator>标签。它的作用是遍历由value指定的“集合型”对象(一般为Map或List类型);
然后是<s:property>标签,它的作用是将iterator中的对象的属性取出来。<s:property value="customername"
/>的作用相当于调用list的getCustomername()方法。
从上面的代码中可以看出,除了使用<s:property>之外,我们还尝试了另外两种标签方法,一是<s:text>,它可以生成一个国际化的信息文本;一个是${city},好象是OGNL表达式语言。细节问题以后研究。
网上说:既然是struts2,就尽量用struts2的标签。
又说:OGNL是主流,和<s:…>配合使用
注意:!上面的代码有一个重大问题!实际上它的运行结果是空,不显示任何东西!
为什么呢?
因为:看看上面execute()方法中的一个变量声明(带下划线的那句):
List<Customer> list = dao.findAll();
这个变量(属性)就是我们在<s:iterator>标签中要迭代(循环)的那个对象。但是,迭代的对象必须是action类的属性!而且必须有getter方法!
所以,ListCustomers.java类中必须先声明一个属性:
private List<Customer> list;
然后,再加入一个list的getter方法:
public List<Customer> getList(){
return list;
}
(而在execute()内部的list前面的类型声明需要去掉。)
一个实例:简单新闻发布
1. 创建一个表:表名:MSG
字段:id,integer, primary key
Au,
varchar, 40
Msg,
varchar, 255
2. hibernate逆向工程(注意第二页要选hibernate type,即使用hibernate数据类型,assigned)
3. 设计流程(struts.xml):
表单页面index.jsp包含输入表单和数据显示两个部分
action类aaa.java用来接受表单数据。
Action将数据写入数据库后再回去(跳转)向index.jsp
<action name="aaa" class="aaa">
<result name="gotoIndex">/index.jsp</result>
<result name="input">/index.jsp</result>
</action>
|
4. 建struts2表单,其中msg是新闻对象
<s:form action="aaa"
method="post" theme="simple">
ID:<s:textfield name="msg.id"
value="0"></s:textfield>
MSG:<s:textfield name="msg.msg"></s:textfield>
AU:<s:textfield name="msg.au"></s:textfield>
<s:submit></s:submit>
</s:form>
5. 在aaa.java中使用属性msg来接收表单数据。
写入数据的“三板斧”是:
1) 获取应用程序上下文
2) 获取DAO对象实例
3) 获取Session对象并启动事务处理
//1
ApplicationContext
ct = WebApplicationContextUtils
.getWebApplicationContext(ServletActionContext
.getServletContext());
//2
MsgDAO dao =
MsgDAO.getFromApplicationContext(ct);
//3
Session s =
dao.getSessionFactory().openSession();//HibernateSessionFactory.getSession();////
Transaction tx = s.beginTransaction();
//////////////////////下面是正是操作数据库
try {
dao.attachDirty(msg);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//////////////////////最后是收场:完成事务过程
tx.commit();
|
6. 运行程序看看,数据是否已经写入表中
7. 如果你希望在页面显示写入的数据,则首先在aaa.java中加入一个查询:
List<Msg>
list;
list
= dao.findAll();
这样就可以将所有内带到页面了!!!
8. index.jsp需要加一些内容以显示action带来的数据:
<s:iterator
value="list">
Id:<s:text
name="id"/><br/>
Message:<s:text
name="msg"/><br/>
by: <s:text
name="au"/><p></p>
</s:iterator>
9.game
over
10.更多问题:关于自动提交(auto commit)问题:Hibernate配置(applicationContext.xml)中的数据源(DataSource)配置中加入自动提交的设置,如下
<property
name="defaultAutoCommit" value="true"></property>
即可不需要action中加入Session, Transaction的语句!
更多问题之2:关于自增字段:如果表中含有自增字段,请在逆向工程中使用使用identity策略
例如,我们可以在SQL Editor中使用如下语句创建一个表:其中的主键id就是auto increment的,但在SQL中使用的是“as identity”。
create table "CLASSICCARS"."USERINFO"(
"ID" BIGINT not null generated always as identity,
"NAME" VARCHAR(255) not null unique,
"PASSWORD" VARCHAR(30) not null,
"EMAIL" VARCHAR(44) not null,
primary key ("ID")
);
输入数据校验问题:
最简单的输入数据校验方法,
是重写validate()方法(当然,你的action需要继承Struts的ActionSupport)。
例如,在页面中,使用如下表单:
<s:textfield name="u.name"
label="用户名"></s:textfield>
<s:textfield
name="u.password" label="密码"></s:textfield>
那么,如果我们想捕捉没有输入密码的错误,则在action中加入这样的语句即可:
public void validate(){
if(u.getPassword().length()==0){
addFieldError("u.password", "你忘了密码了!");
}
}
需要注意,你必须在struts.xml中定义input页面(错误输出页面)。例如,如果你想就在输入页面的本身显示错误信息的话,可以这样写:假设你的表单在myform.jsp, 那么struts.xml中应包含这样的语句:
<result
name="input">myform.jsp</result>
如果你希望将错误输出定向到别的页面,除了要修改上面的配置语句外,还需要在错误输出页面内加上
<s:fielderror/>
这个标签是“表单字段输入错误”标签,它负责显示字段的校验错误信息,也就是你在action中使用addFieldError()函数要显示的信息。(当然,如果你使用了别的错误信息函数,例如,addActionError()或者addActionMessage(),则需要在错误输出页面加上相应的显示标签,例如<s:actionerror/>和<s:actionmessage/>)。
使用XML文件进行输入校验
上面的校验方式,需要在action的代码中进行。如果在系统完成之后,校验规则又有了变化,就需要重新修改action的程序,并重新编译和发布。很显然这不是很有效率的模式。按软件工程的说法,叫“业务逻辑和输入校验这两者之间的耦合度太强”。XML的校验方式可以方便的在不需要修改Java源码的情况下改变校验规则。
XML校验模式需要做两件事情:
1. 当然事先同样要在struts.xml中配置好错误输出页面。
你还可以在表单中指明我们需要“客户端校验模式”:(红字。但不是在所有的theme下都可以工作,所以不推荐)
<s:form action="aa" theme=”simple”
validate="true">
<s:textfield
name="u.name" label="用户名"/>
<s:textfield
name="u.password" label="密码"/>
<s:submit/>
</s:form>
2. 针对要做校验的action,做一个xml文件,文件名为XXX-validation.xml,其中XXX是action的类文件的名字。XML文件内容为:
<?xml
version="1.0" encoding="UTF-8"?>
<!DOCTYPE validators PUBLIC
"-//OpenSymphony Group//XWork Validator 1.0.2//EN"
"http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd">
<validators>
<field
name="u.password">
<field-validator type="stringlength">
<param name="minLength">4</param>
<param name="maxLength">8</param>
<message>请使用密码长度为4---8之间!</message>
</field-validator>
<field-validator type="requiredstring">
<message>请输入密码!!</message>
</field-validator>
</field>
</validators>
上面的内容和语法是可以自明的,不再多说。只注意前面的四行是不可少的。另外,注意:
l
对每一个字段,都应该有一对<field>标记
l
field-validator
的 type还可以是:
n
required:必须输入(这个类型对日期等字段起作用)
n
date:要求是日期字段。例如:
u
<field-validator
type=”date”>
<param name=”min”>1900-1-1</param>
<param name=”max”>2999-12-31</param>
<message>您的日期不正确</message>
</field-validator>
n
double:浮点数;下面例子指明最小值(不包括本身)
u
<field-validator
type=”double”>
<param name=”minExclusive”>0.1</param>
<message>价格必须大于0.1</message>
</field-validator>
u
l
<message>中可以使用表达式引用param参数:
<param name=”maxLength”>7</param>
<message>请输入一个长度不大于${maxLength}的字符串</message>
l
Param中也可以使用表达式,例如:
<field-validator type=”fieldexpression”>
<param name=”expression”>password==repassword</param>
<message>两次输入的密码必须一致</message>
</field-validator>
常见问题:
1. 重要:如果你想在表单字段中显示从数据库中取出的原始值,简单的很:
<s:textfield
name=”username”/>
即可!它会将action传来的username属性的值自动的放到这个表单字段中!
上面的写法等价于:
<s:textfield
name=”username” value=”%{username}” />
2. Struts2表单的对象注入:
在表单级即支持对象的概念,例如,在输入客户信息的时候,我们的表单可以这样来写:
<s:form action="addCustomer" method="post">
<s:textfield name="customer.customernumber" /> <br />
<s:textfield name="customer.customername" /> <br />
<s:textfield name="customer.contactlastname" /> <br />
<s:textfield name="customer.contactfirstname" /> <br />
<s:textfield name="customer.phone" /> <br />
<s:textfield name="customer.addressline1" /> <br />
<s:textfield name="customer.addressline2" /> <br />
<s:textfield name="customer.city" /> <br />
<s:textfield name="customer.state" /> <br />
<s:textfield name="customer.postalcode" /> <br />
<s:textfield name="customer.country" /> <br />
<s:textfield name="customer.salesrepemployeenumber" /> <br />
<s:textfield name="customer.creditlimit" /> <br />
<s:submit></s:submit> </s:form>
|
看懂没有,用的是属性的属性来设置字段。而相应的action则可以:
public class AddCustomer
extends ActionSupport {
private Customer customer;
public Customer
getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
public String
execute()throws Exception{
System.out.println("name: "+customer.getCustomername());
System.out.println("No.: "+customer.getCustomernumber());
return "addok";
}
}
|
只要有getter和setter,即可直接取出customer对象!这就叫做“对象注入”。
3. 从一个action跳转到另一个action:
<result
name=”xxx” type=”redirect”>action name</result> 这种方法是直接跳转,不带上一个action中的属性数据。
<result
name=”xxx” type=”chain”>action name</result>这种方法可以将上一个action中的属性数据带上
注意:action name需要直接写名字,不能带路径,也不能带.action这样的后缀。
附录2:
在Struts2.0中,Action已经与Servlet
API完全分离,这使得Struts2.0的Action具有了更加灵活和低耦合的特性,与Struts1.0相比较而言是个巨大的进步。虽然Struts2.0的Action已经与Servlet
API完全分离,但我们在实现业务逻辑处理时经常需要访问Servlet中的对象,如Session、Application等。Struts2.0 提供了一个名字为ActionContext的类,在Action中可以通过该类获得Servlet API。
ActionContext是一个Action的上下文对象,Action运行期间所用到的数据都保存在ActionContext中(如Session,客户端提交的参数等信息)。
在Action中可以通过下面的代码来创建和使用ActionContext类,关于该类的方法介绍如下所示:
ActionContext ac=ActionContext.getContext();
以下是ActionContext类的常用方法
1.Object get(Object key)
:通过参数key来查找当前ActionContext中的值
2.Map getApplication() :返回一个Application级的Map对象
3.Static ActionContext
getContext() :获得当前线程的ActionContext对象
4.Map getParameters() :返回一个包含所有HttpServletRequest参数信息的Map对象
5.Map getSession() :返回一个Map类型的HttpSession对象
6.Void put(Object
key,Object value) :向当前ActionContext对象中存入名值对信息
7.Void
setApplication(Map application) :设置Application上下文
8.Void setSession(Map
session) :设置一个Map类型的Session值
|
Struts 2默认的表达式语言是OGNL,原因是它相对其它表达式语言具有下面几大优势:
- 支持对象方法调用,如xxx.doSomeSpecial();
- 支持类静态的方法调用和值访问,表达式的格式为@[类全名(包括包路径)]@[方法名 | 值名],例如:@java.lang.String@format('foo
%s', 'bar')或@tutorial.MyConstant@APP_NAME;
- 支持赋值操作和表达式串联,如price=100,
discount=0.8, calculatePrice(),这个表达式会返回80;
- 访问OGNL上下文(OGNL context)和ActionContext;
- 操作集合对象。
OGNL的用法:OGNL是通常要结合Struts 2的标志一起使用,如<s:property
value="xx" />等。大家经常遇到的问题是#、%和$这三个符号的使用。
“#”主要有三种用途:
- 访问OGNL上下文和Action上下文,#相当于ActionContext.getContext();下表有几个ActionContext中有用的属性:
名称
|
作用
|
例子
|
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顺序访问其属性(attribute)
|
#attr.userName相当于按顺序在以上三个范围(scope)内读取userName属性,直到找到为止
|
- 用于过滤和投影(projecting)集合,如books.{?#this.price<100};
- 构造Map,如#{'foo1':'bar1', 'foo2':'bar2'}。
“%”符号的用途是在标志的属性为字符串类型时,计算OGNL表达式的值。
posted on 2010-04-06 21:23
鹏凌 阅读(2560)
评论(0) 编辑 收藏 所属分类:
java