1. 结果视图的配置
1.1 result标签
在sturts.xml文件中,Result的配置非常简单,使用元素来配置Result逻辑视图与物理视图之间的映射,元素可以有name和type属性,但这两种属性都不是必选的。
<action name="demo1" class="com.wgy.web.action.Demo1Action" method="demo1">
<!-- result标签:
作用:用于配置结果视图(结果视图可以是一个jsp/html,也可以是一个action)
属性:
name:指定逻辑结果视图。作用就是和动作方法的返回值进行比较,当一致时,前往配置的页面或者action。不写的话:默认值是success——>去哪
type:指定前往结果视图的方式。以何种方式前往。 ——>怎么去
type取值都是来源于struts-default.xml文件中package名称是struts-default包中定义类型
常用的结果类型:
dispatcher:请求转发 (默认值)
redirect:重定向(可以是重定向到另外一个动作或者是重定向到一个jsp)
redirectAction:重定向到另外一个动作(它由于会自动在后面为我们拼接url后缀,所以只能重定向到动作)
请求转发和重定向的区别:
请求转发:一次请求 地址栏不变 请求域中数据不丢失 服务器行为 只能是在当前应用中转发
重定向: 两次请求 地址栏改变 请求域中数据丢失 浏览器行为 可以定向到当前应用的外部
响应浏览器的三种方式:
请求转发 重定向 使用流输出(如果只有一种方式,那就是此种方式)
-->
<result name="success" type="redirectAction">demo2</result>
<result name="error" type="dispatcher">/error.jsp</result>
</action>
<action name="demo2">
<result name="success" type="dispatcher">/success.jsp</result>
<result name="login">/login.jsp</result>
</action>
1.2 配置全局结果视图
1.2.1 局部结果视图和全局结果视图
配置在action标签内的result,我们成为局部结果视图,它只能由当前action使用。
而在实际开发中,有很多页面,每个action可能都会用到。比如:success.jsp,error.jsp,login.jsp等等。当我们很多action都用到了login.jsp,在每个action标签中都配置一次,显然是不合理的,这个时候我们就用到了全局结果视图。
1.2.2 配置方式
<!-- 定义一个公共包
全局结果视图和局部结果视图
定义是放在action标签外面, 在global-results标签内部的结果视图。
可以在多个action中使用
优先级:先找局部,再找全局。
-->
<package name="myDefault" extends="struts-default" abstract="true">
<global-results>
<result name="login">/login.jsp</result>
</global-results>
</package>
<package name="p1" extends="myDefault">
<action name="demo1" class="com.wgy.web.action.Demo1Action" method="demo1">
<result name="success" type="redirectAction">demo2</result>
</action>
<action name="demo2">
<result name="success" type="dispatcher">/success.jsp</result>
<result name="login">/login.jsp</result>
</action>
</package>
2. 访问Servlet的API的两种方式
2.1 使用ServletActionContext
/**
* 访问ServletAPI:
* 有两种方式:
* 第一种方式:
* 使用struts2框架提供的一个工具类,该类中包含了相应的静态方法,可以直接获取
* 工具类是:ServletActionContext
* 此种方式是我们实际开发中用的最多的方式
*
* 输出结果之后,找出其中一个和其他三个不一样:
* org.apache.struts2.dispatcher.StrutsRequestWrapper@1c6e453 它和其他三个不一样,它是struts2提供的
* org.apache.catalina.connector.ResponseFacade@b846ae
* org.apache.catalina.core.ApplicationContextFacade@287809
* org.apache.catalina.session.StandardSessionFacade@e0d480
*
* @author wgy
*/
public class Demo1Action extends ActionSupport {
private HttpServletRequest request;
private HttpServletResponse response;
private HttpSession session;
private ServletContext application;
/**
* 动作方法
*
* @return
*/
public String demo1(){
request = ServletActionContext.getRequest();
response = ServletActionContext.getResponse();
application = ServletActionContext.getServletContext();
session = request.getSession();
System.out.println(request);
System.out.println(response);
System.out.println(application);
System.out.println(session);
return SUCCESS;
}
}
2.2 通过实现接口的方式
/**
* 访问ServletAPI:
* 有两种方式:
* 第二种方式:
* 通过实现不同的接口,获取不同的对象。
* 要想使用request,需要实现ServletRequestAware
* 要想使用response,需要实现ServletResponseAware
* 要想使用servletContext,需要实现SerlvetContextAware
* 输出结果之后,找出其中一个和其他三个不一样:
* org.apache.struts2.dispatcher.StrutsRequestWrapper@1c6e453 它和其他三个不一样,它是struts2提供的
* org.apache.catalina.connector.ResponseFacade@b846ae
* org.apache.catalina.core.ApplicationContextFacade@287809
* org.apache.catalina.session.StandardSessionFacade@e0d480
*
*
* 如果说是一种方式获取ServletAPI对象:ActionContext中的get(key)
* 如果说是三种方式获取ServletAPI对象,除了我们讲的两种之外,也可以使用ActionContext获取
*
* 通过分析源码,我们得知,ActionContext看上去是一个类似Map的结构。
* map的key是String类型,Map的value是Object类型
*
* @author wgy
*/
public class Demo2Action extends ActionSupport implements
ServletRequestAware, ServletResponseAware, ServletContextAware {
private HttpServletRequest request = null;
private HttpServletResponse response = null;
private ServletContext application = null;
/**
* 动作方法
*
* @return
*/
public String demo2(){
System.out.println(request);
System.out.println(response);
System.out.println(application);
return SUCCESS;
}
@Override
public void setServletRequest(HttpServletRequest request) {
this.request = request;
}
@Override
public void setServletResponse(HttpServletResponse response) {
this.response = response;
}
@Override
public void setServletContext(ServletContext application) {
this.application = application;
}
}
3. 请求参数的封装
3.1 请求参数封装概述
封装请求参数就是把我们通过浏览器发送请求时,要转递给服务器的数据封装到指定的对象中。这个对象一般都是实体类。但是有时就是Action中的一个属性。也就是说,我们封装请求参数时,可以有实体类,也可以没有。同时,我们还需要知道,请求参数的封装和请求方式无关。无论get还是post都可以封装。
3.2 属性驱动
3.2.1 没有实体类
此种情况,我们一般也称为动作类和模型在一起,也就是说我们在action中定义一些私有成员,并且提供它们的公有get/set方法。具体代码如下
动作类:
/**
* 请求参数封装
* 第一种情况:
* 属性驱动:没有实体类
* 表单数据的接收都定义在动作类中,所以称为动作类和模型数据写在一起
* 要想封装成功,需要按照要求书写:
* 要求是:表单元素的name属性取值,必须和动作类中成员get/set方法后面的部分保持一致
*
* 细节:
* 1、struts2框架会我们解决post请求的中文乱码问题,但是get请求不解决。
* 2、struts2框架会自动为我们转换数据类型:
* 基本类型自动转换
* 字符串数组会按照逗号+空格的方式拼接成字符串
* 日期类型会按照本地格式转成日期对象
* 本地格式:yyyy-MM-dd
*
* 执行参数封装,是一个名称为params的拦截器实现的。
* 封装的规则只有一个,它要去指定位置找属性,找到之后调用set方法赋值。
*
* @author wgy
*/
public class Demo1Action extends ActionSupport {
private String username;
private Integer age;
private Date birthday;
private String hobby;
/**
* 动作方法
*
* @return
*/
public String demo1(){
System.out.println(username+"==="+age+"==="+birthday+"==="+hobby);
return SUCCESS;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public String getHobby() {
return hobby;
}
public void setHobby(String hobby) {
this.hobby = hobby;
}
}
jsp页面:
<%--请求参数封装:第一种情况:属性驱动-没有实体类 --%>
<form action="${pageContext.request.contextPath}/demo1.action" method="post">
姓名:<input type="text" name="username"/><br/>
年龄:<input type="text" name="age"/><br/>
生日:<input type="text" name="birthday"/><br/>
爱好:<input type="checkbox" name="hobby" value="吃饭"/>吃饭
<input type="checkbox" name="hobby" value="睡觉"/>睡觉
<input type="checkbox" name="hobby" value="写代码"/>写代码
<br/>
<input type="submit" value="提交"/>
</form>
struts.xml:
<action name="demo1" class="com.wgy.web.action.Demo1Action" method="demo1">
<result>/success.jsp</result>
</action>
3.2.2 有实体类
此种情况是,我们有独立的实体类,在action中定义的是实体类对象,并且提供get/set方法。代码如下:
动作类:
/**
* 请求参数封装
* 第二种情况:
* 属性驱动:有实体类
* 表单数据的接收都定义在实体类中,把实体类定义在动作类中。
* 要想封装成功,需要按照要求书写:
* 此时需要使用OGNL表达式来指定表单元素的name取值
* OGNL表达式全称:Object Graphic Navigation Language
* 对象 图 导航 语言
* 写法:
* user.username user.age
*
* 执行参数封装,是一个名称为params的拦截器实现的。
* 封装的规则只有一个,它要去指定位置找属性,找到之后调用set方法赋值。
*
* @author wgy
*/
public class Demo2Action extends ActionSupport {
private User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
/**
* 动作方法
* @return
*/
public String demo2(){
System.out.println(user);
return SUCCESS;
}
}
jsp页面:
<%--请求参数封装:第二种情况:属性驱动-有实体类 --%>
<form action="${pageContext.request.contextPath}/demo2.action" method="post">
姓名:<input type="text" name="user.username"/><br/>
年龄:<input type="text" name="user.age"/><br/>
生日:<input type="text" name="user.birthday"/><br/>
爱好:<input type="checkbox" name="user.hobby" value="吃饭"/>吃饭
<input type="checkbox" name="user.hobby" value="睡觉"/>睡觉
<input type="checkbox" name="user.hobby" value="写代码"/>写代码
<br/>
<input type="submit" value="提交"/>
</form>
struts.xml:
<action name="demo2" class="com.wgy.web.action.Demo2Action" method="demo2">
<result>/success.jsp</result>
</action>
3.3 模型驱动
在Struts2中,Action处理请求参数还有另外一种方式,叫做模型驱动(ModelDriven)。通过实现ModelDriven接口来接收请求参数。
动作类:
/**
* 请求参数封装
* 第三种情况: 我们在后面用的最多的方式
* 模型驱动
* 要想封装成功,需要按照要求书写:
* 1、动作类必须实现ModelDriven接口
* 2、动作类中需要定义模型,并且必须实例化出来
* 3、提供接口抽象方法的实现,返回值必须是模型对象
*
* 执行参数封装,是一个名称为params的拦截器实现的。
* 模型驱动的实现,除了params拦截器之外,还需要一个叫modelDriven的拦截器配合
* 封装的规则只有一个,它要去指定位置找属性,找到之后调用set方法赋值。
*
* @author wgy
*/
public class Demo3Action extends ActionSupport implements ModelDriven<User> {
private User user = new User();
@Override
public User getModel() {
return user;
}
/**
* 动作方法
*
* @return
*/
public String demo3(){
System.out.println(user);
return SUCCESS;
}
}
jsp页面:
<%--请求参数封装:第三种情况:模型驱动 --%>
<form action="${pageContext.request.contextPath}/demo3.action" method="post">
姓名:<input type="text" name="username"/><br/>
年龄:<input type="text" name="age"/><br/>
生日:<input type="text" name="birthday"/><br/>
爱好:<input type="checkbox" name="hobby" value="吃饭"/>吃饭
<input type="checkbox" name="hobby" value="睡觉"/>睡觉
<input type="checkbox" name="hobby" value="写代码"/>写代码
<br/>
<input type="submit" value="提交"/>
</form>
struts.xml:
<action name="demo3" class="com.wgy.web.action.Demo3Action" method="demo3">
<result>/success.jsp</result>
</action>
3.4 封装集合数据
在实际的开发中,有些时候我们需要批量插入用户或者批量插入其他的对象,在Action中需要接受到这多个Action中封装的对象,然后传递给业务层。那么这个时候就需要将表单的数据封装到集合中。
3.4.1 封装到List
动作类:
/**
* 请求参数封装
* 复杂类型的封装:List集合封装
* 复杂类型的封装都需要基于第二种情况实现
*
* @author wgy
*/
public class Demo4Action extends ActionSupport {
private List<User> users;
public List<User> getUsers() {
return users;
}
public void setUsers(List<User> users) {
this.users = users;
}
/**
* 动作方法
*
* @return
*/
public String demo4(){
System.out.println(users);
return SUCCESS;
}
}
jsp页面:
<%--请求参数封装:List集合类型的封装 --%>
<form action="${pageContext.request.contextPath}/demo4.action" method="post">
姓名:<input type="text" name="users[0].username"/><br/>
年龄:<input type="text" name="users[0].age"/><br/>
生日:<input type="text" name="users[0].birthday"/><br/>
爱好:<input type="checkbox" name="users[0].hobby" value="吃饭"/>吃饭
<input type="checkbox" name="users[0].hobby" value="睡觉"/>睡觉
<input type="checkbox" name="users[0].hobby" value="写代码"/>写代码
<br/>
姓名:<input type="text" name="users[1].username"/><br/>
年龄:<input type="text" name="users[1].age"/><br/>
生日:<input type="text" name="users[1].birthday"/><br/>
爱好:<input type="checkbox" name="users[1].hobby" value="吃饭"/>吃饭
<input type="checkbox" name="users[1].hobby" value="睡觉"/>睡觉
<input type="checkbox" name="users[1].hobby" value="写代码"/>写代码
<br/>
<input type="submit" value="提交"/>
</form>
struts.xml:
<action name="demo4" class="com.wgy.web.action.Demo4Action" method="demo4">
<result>/success.jsp</result>
</action>
3.4.2 封装到Map
动作类:
/**
* 请求参数封装
* 复杂类型的封装:Map集合封装
* 复杂类型的封装都需要基于第二种情况实现
*
* @author wgy
*/
public class Demo5Action extends ActionSupport {
private Map<String, User> users;
public Map<String, User> getUsers() {
return users;
}
public void setUsers(Map<String, User> users) {
this.users = users;
}
/**
* 动作方法
*
* @return
*/
public String demo5(){
System.out.println(users);
return SUCCESS;
}
}
jsp页面:
<%--请求参数封装:Map集合类型的封装 --%>
<form action="${pageContext.request.contextPath}/demo5.action" method="post">
姓名:<input type="text" name="users['key1'].username"/><br/>
年龄:<input type="text" name="users['key1'].age"/><br/>
生日:<input type="text" name="users['key1'].birthday"/><br/>
爱好:<input type="checkbox" name="users['key1'].hobby" value="吃饭"/>吃饭
<input type="checkbox" name="users['key1'].hobby" value="睡觉"/>睡觉
<input type="checkbox" name="users['key1'].hobby" value="写代码"/>写代码
<br/>
姓名:<input type="text" name="users['abc'].username"/><br/>
年龄:<input type="text" name="users['abc'].age"/><br/>
生日:<input type="text" name="users['abc'].birthday"/><br/>
爱好:<input type="checkbox" name="users['abc'].hobby" value="吃饭"/>吃饭
<input type="checkbox" name="users['abc'].hobby" value="睡觉"/>睡觉
<input type="checkbox" name="users['abc'].hobby" value="写代码"/>写代码
<br/>
<input type="submit" value="提交"/>
</form>
struts.xml:
<action name="demo5" class="com.wgy.web.action.Demo5Action" method="demo5">
<result>/success.jsp</result>
</action>
4. 案例:Struts2+Hibernate实现保存删除客户
4.1 Struts2
4.1.1 修改jsp
menu.jsp
<TR>
<TD class=menuSmall>
<A class=style2 href="${pageContext.request.contextPath}/customer/addUICustomer.action" target=main>- 新增客户</A>
</TD>
</TR>
add.jsp
<FORM id=form1 name=form1 action="${pageContext.request.contextPath }/customer/addCustomer.action" method=post>
...
</FORM>
list.jsp
<SCRIPT language=javascript>
function delOne(custId){
var sure = window.confirm("确定删除吗?");
if(sure){
window.location.href = "${pageContext.request.contextPath}/customer/deleteCustomer?custId="+custId;
}
}
</SCRIPT>
<a href="javascript:delOne('${customer.custId}')" >删除</a>
4.1.2 配置xml并编写Action
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"></constant>
<!-- 动作配置 -->
<package name="customer" extends="struts-default" namespace="/customer">
<!-- 查询所有客户 -->
<action name="findAllCustomer" class="com.wgy.web.action.CustomerAction" method="findAllCustomer">
<result name="findAllCustomer">/jsp/customer/list.jsp</result>
</action>
<!-- 获取添加客户页面 -->
<action name="addUICustomer" class="com.wgy.web.action.CustomerAction" method="addUICustomer">
<result name="addUICustomer">/jsp/customer/add.jsp</result>
</action>
<!-- 添加客户 -->
<action name="addCustomer" class="com.wgy.web.action.CustomerAction" method="addCustomer">
<!-- <result name="addCustomer" type="redirect">/jsp/success.jsp</result> -->
<result name="addCustomer" type="redirect">findAllCustomer</result>
</action>
<!-- 删除客户 -->
<action name="deleteCustomer" class="com.wgy.web.action.CustomerAction" method="deleteCustomer">
<result name="deleteCustomer" type="redirect">findAllCustomer</result>
</action>
</package>
</struts>
动作类
/**
* 客户的动作类
*
* @author wgy
*/
public class CustomerAction extends ActionSupport implements ModelDriven<Customer> {
private ICustomerService customerService = new CustomerServiceImpl();
private Customer customer = new Customer();
@Override
public Customer getModel() {
return customer;
}
/**
* 查询所有客户
* @return
*/
public String findAllCustomer(){
//1.调用service查询客户
List<Customer> customers = customerService.findAllCustomer();
//2.获取request对象
HttpServletRequest request = ServletActionContext.getRequest();
//3.把查询的结果存入请求域中
request.setAttribute("customers", customers);
//4.返回
return "findAllCustomer";
}
/**
* 获取添加客户页面
* @return
*/
public String addUICustomer(){
return "addUICustomer";
}
/**
* 添加客户
* @return
*/
public String addCustomer(){
customerService.saveCustomer(customer);
return "addCustomer";
}
/**
* 删除客户
* @return
*/
public String deleteCustomer(){
customerService.deleteCustomer(customer);
return "deleteCustomer";
}
}
4.2 Hibernate
4.2.1 编写业务层接口及实现类
/**
* 客户的业务层接口
*
* @author wgy
*/
public interface ICustomerService {
/**
* 查询所有客户
*
* @return
*/
List<Customer> findAllCustomer();
/**
* 添加客户
*
* @param customer
*/
void saveCustomer(Customer customer);
/**
* 删除客户
* @param customer
*/
void deleteCustomer(Customer customer);
}
/**
* 客户的业务层实现类
* 事务控制在业务层的
*
* @author wgy
*/
public class CustomerServiceImpl implements ICustomerService {
private ICustomerDao customerDao = new CustomerDaoImpl();
@Override
public List<Customer> findAllCustomer() {
Session s = null;
Transaction tx = null;
try{
//1.获取Session
s = HibernateUtil.getCurrentSession();
//2.开启事务
tx = s.beginTransaction();
//3.执行操作
List<Customer> customers = customerDao.findAllCustomer();
//4.提交事务
tx.commit();
//5.返回结果
return customers;
}catch(Exception e){
//回滚事务
tx.rollback();
throw new RuntimeException(e);
}
}
@Override
public void saveCustomer(Customer customer) {
Session s = null;
Transaction tx = null;
try{
//1.获取Session
s = HibernateUtil.getCurrentSession();
//2.开启事务
tx = s.beginTransaction();
//3.执行操作
customerDao.saveCustomer(customer);
//4.提交事务
tx.commit();
}catch(Exception e){
//回滚事务
tx.rollback();
throw new RuntimeException(e);
}
}
@Override
public void deleteCustomer(Customer customer) {
Session s = null;
Transaction tx = null;
try{
//1.获取Session
s = HibernateUtil.getCurrentSession();
//2.开启事务
tx = s.beginTransaction();
//3.执行操作
customerDao.deleteCustomer(customer);
//4.提交事务
tx.commit();
}catch(Exception e){
//回滚事务
tx.rollback();
throw new RuntimeException(e);
}
}
}
4.2.2 编写持久层接口及实现类
/**
* 客户的持久层接口
*
* @author wgy
*/
public interface ICustomerDao {
/**
* 查询所有客户
*
* @return
*/
List<Customer> findAllCustomer();
/**
* 添加客户
* @param customer
*/
void saveCustomer(Customer customer);
/**
* 删除客户
* @param customer
*/
void deleteCustomer(Customer customer);
/**
* 根据id查询客户
* @param custID
* @return
*/
Customer findCustomerById(Long custID);
}
/**
* 客户的持久层实现类
*
* @author wgy
*/
public class CustomerDaoImpl implements ICustomerDao {
@Override
public List<Customer> findAllCustomer() {
return HibernateUtil.getCurrentSession().createQuery("from Customer").list();
}
@Override
public void saveCustomer(Customer customer) {
HibernateUtil.getCurrentSession().save(customer);
}
@Override
public void deleteCustomer(Customer customer) {
HibernateUtil.getCurrentSession().delete(findCustomerById(customer.getCustId()));
}
@Override
public Customer findCustomerById(Long custID) {
return HibernateUtil.getCurrentSession().get(Customer.class,custID);
}
}
5. 请求参数封装失败后处理办法
5.1 配置input结果视图
视图路径应该是从哪来回哪去
<action name="demo3" class="com.wgy.web.action.Demo3Action" method="demo3">
<result>/success.jsp</result>
<result name="input">/user.jsp</result>
</action>
5.2 提示错误信息
引入struts2标签库
<%@ taglib uri="/struts-tags" prefix="s" %>
<%--请求参数封装:类型转换失败的处理方式 --%>
<form action="${pageContext.request.contextPath}/demo3.action" method="post">
姓名:<input type="text" name="username"/><s:fielderror fieldName="username"/><br/>
年龄:<input type="text" name="age"/><s:fielderror fieldName="age"/><br/>
生日:<input type="text" name="birthday"/><s:fielderror fieldName="birthday"/><br/>
爱好:<input type="checkbox" name="hobby" value="吃饭"/>吃饭
<input type="checkbox" name="hobby" value="睡觉"/>睡觉
<input type="checkbox" name="hobby" value="写代码"/>写代码
<s:fielderror fieldName="hobby"/><br/>
<input type="submit" value="提交"/>
</form>
5.3 把提交的数据回显回来
html标签和struts2标签都可以实现
<s:form action="demo3">
<s:textfield name="username" label="姓名"/>
<s:textfield name="age" label="年龄"/>
<s:textfield name="birthday" label="生日"/>
<s:submit value="提交"/>
</s:form>
5.4 关于中文提示的问题
I18N : 国际化 Internationalization
同实体类创建.properties文件
invalid.fieldvalue.birthday=请输入正确的日期格式。正确的格式是:yyyy-MM-dd