SSH中Struts可谓是比较常用的框架了。通过对Struts官方文档的阅读了解到Struts主要包含:原理和配置文件(所有框架都会涉及的)、拦截器(Interceptors)、Actions、Results、数据验证、国际化、OGNL和数据类型转换、标签以及Struts与其他框架如DWR、Spring、Hibernate等框架的结合使用。 今天主要先整体说明一下Struts,然后写一个CRUD Demo,让自己对Struts有个整体上的认识。细节性问题在以后的博文中再详细说明。
Struts 内核采用了AOP ,所以进行了解耦和,我们对照上面这张图熟悉一个请求在Struts2 中的处理大概分为一下几个步骤:
1、 客户端初始化一个指向Servlet 容器(例如Tomcat )的请求;
2、 这个请求经过一系列的过滤器(Filter ),接着FilterDispatcher (在web.xml 中进行配置)被调用,FilterDispatcher 询问ActionMapper 来决定这个请求是否需要调用某个Action ;
3、 如果ActionMapper 决定需要调用某个Action ,那么FilterDispatcher 会把请求的处理交给ActionProxy ;
4、 ActionProxy 通过Configuration Manager 询问框架的配置文件,找到需要调用的Action 类;
5、 ActionProxy 创建一个ActionInvocation 的实例(Action 调度器,实现拦截器与Action 的cross-cut (横切));
6、 ActionInvocation 实例使用命名模式来调用,在调用Action 的过程前后,涉及到相关拦截器(Intercepter )的调用;
7、 一旦Action 执行完毕,ActionInvocation 负责根据struts.xml 中的配置找到对应的返回结果。返回结果通常是(但不总是,也可能是另外的一个Action 链)一个需要被表示的JSP 或者FreeMarker 的模版。在表示的过程中可以使用Struts2 框架中继承的标签。在这个过程中需要涉及到ActionMapper ;
这里需要对 ActionProxy 进行一点说明的是,ActionProxy 不仅可以实现Action 的功能,还可以在不发生耦合的情况下在Action 之前之后插入一些拦截器。
上面理论上了解了一下Struts2 的处理流程,下面我们来一个CRUD Demo 来实践一下,在列出代码之前,先说明一下,Struts 的开发步骤:
1、 将struts2 的包添加到lib 下
Jar 包 |
说明 |
struts2-core-2.1.8.1.jar |
Struts2 的核心包 |
xwork-core-2.1.6.jar |
Command 模式框架,WebWork 和Struts2 都基于xwork |
commons-logging-1.0.4.jar |
Java 日志工具类包 |
freemarker-2.3.15.jar |
模板引擎,一个基于模板生成文本输出的通用工具 |
ognl-2.7.3.jar |
Object-Graph Navigation Language ,表达式语言,用来获取和设置Java 对象属 |
如果想要实现上传还要再多加commons-fileupload-1.2.1.jar 和commons-io-1.3.2.jar 。
2 、实现相应的Action 及jsp 页面
3 、在web.xml 中配置MVC 控制器(Struts 核心过滤器)
4 、生成struts.xml(src 下) 的配置文件及映射Action 和jsp 页面
为了方便,我直接将数据以集合的形式存储,而没用存在数据库中去了。Ok ,废话说了很多,直接上代码:
首先配置文件 :
web.xml:
<?xml version="1.0" encoding="UTF-8"?> <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"> <display-name>Struts2CRUD</display-name> <welcome-file-list> <welcome-file>index.html</welcome-file> </welcome-file-list> <!-- struts核心过滤器 --> <filter> <filter-name>struts2</filter-name> <filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>
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> <constant name="struts.devMode" value="true" /> <constant name="struts.custom.i18n.resources" value="guest" /> <!-- Configuration for the default package. --> <package name="default" extends="struts-default" namespace="/"> <!-- setup the default-stack exception mapping interceptor so that any exceptions not caught by this application will be logged and then handled by the global exception mapping --> <interceptors> <interceptor-stack name="appDefault"> <interceptor-ref name="defaultStack"> <param name="exception.logEnabled">true</param> <param name="exception.logLevel">ERROR</param> </interceptor-ref> </interceptor-stack> </interceptors> <default-interceptor-ref name="appDefault" /> <global-results> <result name="error">/page/error.jsp</result> </global-results> <global-exception-mappings> <exception-mapping exception="java.lang.Exception" result="error" /> </global-exception-mappings> <action name="index" class="com.iflytek.action.ListEmployeeAction"> <result name="success">/page/employeeList.jsp</result> </action> <action name="addEmployee" class="com.iflytek.action.EmployeeAction" method="input"> <result name="input">/page/employeeEdit.jsp</result> </action> <action name="saveEmployee" class="com.iflytek.action.EmployeeAction" method="save"> <result name="success" type="redirectAction"> <param name="actionName">index</param> </result> <result name="input">/page/employeeEdit.jsp</result> </action> <action name="editEmployee" class="com.iflytek.action.EmployeeAction" method="input"> <result name="input">/page/employeeEdit.jsp</result> </action> <action name="deleteEmployee" class="com.iflytek.action.EmployeeDeleteAction" method="delete"> <result name="success" type="redirectAction"> <param name="actionName">index</param> </result> <result name="input">/page/employeeEdit.jsp</result> </action> </package> </struts>
View
index.html:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd"> <html> <head> <title>Redirecting</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <META HTTP-EQUIV="Refresh" CONTENT="0; URL=index.action"> </head> <body> </body> </html>
employeeList.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <%@ taglib prefix="s" uri="/struts-tags"%> <html> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <head> <link href="<s:url value='/css/main.css'/>" rel="stylesheet" type="text/css" /> <title><s:text name="label.employees" /></title> <style type="text/css"> th { text-align: center; } </style> </head> <body> <div class="titleDiv"> <s:text name="application.title" /> </div> <h1> <s:text name="label.employees" /> </h1> <s:url var="url" action="addEmployee" /> <a href="<s:property value="#url"/>">添加员工</a> <br /> <br /> <table class="borderAll"> <tr> <th><s:text name="label.firstName" /></th> <th><s:text name="label.lastName" /></th> <th><s:text name="label.age" /></th> <th><s:text name="label.department" /></th> <th><s:text name="label.salary" /></th> <th><s:text name="label.oper" /></th> </tr> <s:iterator value="employees" status="status"> <tr class="<s:if test="#status.even">even</s:if><s:else>odd</s:else>"> <td class="nowrap"><s:property value="firstName" /></td> <td class="nowrap"><s:property value="lastName" /></td> <td class="nowrap"><s:property value="age" /></td> <td class="nowrap"><s:property value="department.name" /></td> <td class="nowrap"><s:property value="salary" /></td> <td class="nowrap"><s:url action="editEmployee" var="url"> <s:param name="employee.employeeId" value="employeeId" /> </s:url> <a href="<s:property value="#url"/>">编辑</a> <s:url action="deleteEmployee" var="url"> <s:param name="employee.employeeId" value="employeeId" /> </s:url> <a href="<s:property value="#url"/>">删除</a></td> </tr> </s:iterator> </table> </body> </html>
employeeEdit.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <%@ taglib prefix="s" uri="/struts-tags"%> <s:if test="employee==null || employee.employeeId == null"> <s:set name="title" value="%{'添加员工'}" /> </s:if> <s:else> <s:set name="title" value="%{'编辑员工'}" /> </s:else> <html> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <head> <link href="<s:url value='/css/main.css'/>" rel="stylesheet" type="text/css" /> <style> td { white-space: nowrap; } </style> <title><s:property value="#title" /></title> <s:head /> </head> <body> <div class="titleDiv"> <s:text name="application.title" /> </div> <h1> <s:property value="#title" /> </h1> <s:actionerror /> <s:actionmessage /> <s:form action="saveEmployee" method="post"> <s:textfield key="employee.firstName" size="40" /> <s:textfield key="employee.lastName" size="40" /> <s:textfield key="employee.age" size="20" /> <s:select key="employee.department.departmentId" list="departments" listKey="departmentId" listValue="name" /> <s:textfield key="employee.salary" size="20" /> <s:hidden name="employee.employeeId" /> <s:submit key="submit" /> </s:form> </body> </html>
error.jsp:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <%@ taglib prefix="s" uri="/struts-tags"%> <html> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <head> <title>Error</title> </head> <body> <h4>The application has malfunctioned.</h4> <p>Please contact technical support with the following information:</p> <!-- the exception and exceptionStack bean properties were created by Struts2's Exception Intercepter --> <h4> Exception Name: <s:property value="exception" /> </h4> <h4> Exception Details: <s:property value="exceptionStack" /> </h4> <p> <a href="index.action">Return to the home page.</a> </p> </body> </html>
页面中用到的提示信息
guest.properties(src目录下)
#labels application.title=Struts2 CRUD Demo label.employees=\u5458\u5DE5\u4FE1\u606F label.delete=\u5220\u9664 label.edit=\u7F16\u8F91 label.employee.edit=\u7F16\u8F91\u5458\u5DE5 label.employee.add=\u6DFB\u52A0\u5458\u5DE5 label.firstName=\u540D\u5B57 label.lastName=\u59D3 label.department=\u6240\u5C5E\u90E8\u95E8 label.age=\u5E74\u9F84 label.salary=\u85AA\u6C34 label.oper=\u64CD\u4F5C employee.firstName=\u540D\u5B57 employee.lastName=\u59D3 employee.department.departmentId=\u6240\u5C5E\u90E8\u95E8 employee.age=\u5E74\u9F84 employee.salary=\u85AA\u6C34 submit=\u63D0\u4EA4 ##-- errors errors.prefix=<span style="color:red;font-weight:bold;"> errors.suffix=</span> errors.general=\u51FA\u73B0\u9519\u8BEF errors.required.firstname=\u540D\u5B57\u5FC5\u586B\uFF01 errors.required.lastname=\u59D3\u5FC5\u586B\uFF01 errors.required.age=\u5E74\u9F84\u5FC5\u586B\uFF01 errors.required.age.limit=\u5E74\u9F84\u5728 ${min} \u5230 ${max}\u4E4B\u95F4\uFF01 errors.required.department=\u90E8\u95E8\u5FC5\u586B\uFF01
Action
ListEmployeeAction.java
package com.iflytek.action; import java.util.List; import org.apache.log4j.Logger; import com.iflytek.model.Employee; import com.iflytek.service.IEmployeeService; import com.iflytek.serviceImpl.EmployeeServiceImpl; import com.opensymphony.xwork2.ActionSupport; /** * * @author xdwang * * @date 2012-6-6 下午8:48:08 * * @email [email protected] * * @blog xdwangiflytek.iteye.com * * 员工列表 * */ public class ListEmployeeAction extends ActionSupport { private static final long serialVersionUID = 1L; @SuppressWarnings("unused") private static Logger logger = Logger.getLogger(ListEmployeeAction.class .getName()); private static IEmployeeService empService = new EmployeeServiceImpl(); private List<Employee> employees; /** * Create collection of Employee objects. * * @return success */ public String execute() throws Exception { setEmployees(empService.getAllEmployees()); return SUCCESS; } public List<Employee> getEmployees() { return employees; } public void setEmployees(List<Employee> employees) { this.employees = employees; } }
EmployeeAction.java
package com.iflytek.action; import java.util.List; import org.apache.log4j.Logger; import com.iflytek.model.Department; import com.iflytek.model.Employee; import com.iflytek.service.IDepartmentService; import com.iflytek.service.IEmployeeService; import com.iflytek.serviceImpl.DepartmentServiceImpl; import com.iflytek.serviceImpl.EmployeeServiceImpl; import com.opensymphony.xwork2.ActionSupport; import com.opensymphony.xwork2.Preparable; /** * * @author xdwang * * @date 2012-6-6 下午5:03:17 * * @email [email protected] * * @blog xdwangiflytek.iteye.com * * 员工的Action * */ public class EmployeeAction extends ActionSupport implements Preparable { private static final long serialVersionUID = 1L; private static Logger logger = Logger.getLogger(EmployeeAction.class .getName()); private static IEmployeeService empService = new EmployeeServiceImpl(); private static IDepartmentService deptService = new DepartmentServiceImpl(); private Employee employee; private List<Department> departments; /** * Ensures that the collection of Department objects is always available to * the view. */ public void prepare() throws Exception { departments = deptService.getAllDepartments(); logger.debug("in method prepare"); } /** * Saves a new employee if the employee object's employeeId is null * otherwise updates existing employee. * * @return success */ public String save() { logger.debug("in method save"); if (employee.getEmployeeId() == null) { empService.insertEmployee(employee); } else { empService.updateEmployee(employee); } return SUCCESS; } /** * Gets a specific Employee objects so it can be displayed in the view. * * @return input */ public String input() { logger.debug("in method input"); if (employee != null && employee.getEmployeeId() != null) { logger.debug("Employee ID is " + employee.getEmployeeId()); employee = empService.getEmployee(employee.getEmployeeId()); } return INPUT; } public Employee getEmployee() { return employee; } public void setEmployee(Employee employee) { this.employee = employee; } public List<Department> getDepartments() { return departments; } }
EmployeeDeleteAction.java
package com.iflytek.action; import org.apache.log4j.Logger; import com.iflytek.model.Employee; import com.iflytek.service.IEmployeeService; import com.iflytek.serviceImpl.EmployeeServiceImpl; import com.opensymphony.xwork2.ActionSupport; /** * * @author xdwang * * @date 2012-6-6 下午8:38:14 * * @email [email protected] * * @blog xdwangiflytek.iteye.com * */ public class EmployeeDeleteAction extends ActionSupport { private static final long serialVersionUID = 1L; private static Logger logger = Logger.getLogger(EmployeeDeleteAction.class .getName()); private static IEmployeeService empService = new EmployeeServiceImpl(); private Employee employee; /** * Delete the Employee object based on the value of the employeeId field * from the data repository. * * @return success */ public String delete() { logger.debug("in method delete"); if (employee != null && employee.getEmployeeId() != null) { logger.debug("Employee ID is " + employee.getEmployeeId()); } empService.deleteEmployee(employee.getEmployeeId()); return SUCCESS; } public Employee getEmployee() { return employee; } public void setEmployee(Employee employee) { this.employee = employee; } }
同目录下的验证.xml
EmployeeAction-validation.xml
<!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="employee.firstName"> <field-validator type="requiredstring"> <message key="errors.required.firstname"/> </field-validator> </field> <field name="employee.lastName"> <field-validator type="requiredstring"> <message key="errors.required.lastname"/> </field-validator> </field> <field name="employee.age"> <field-validator type="required" short-circuit="true"> <message key="errors.required.age"/> </field-validator> <field-validator type="int"> <param name="min">18</param> <param name="max">65</param> <message key="errors.required.age.limit"/> </field-validator> </field> </validators>
service接口
IDepartmentService.java
package com.iflytek.service; import java.util.List; import com.iflytek.model.Department; /** * * @author xdwang * * @date 2012-6-6 下午9:11:15 * * @email [email protected] * * @blog xdwangiflytek.iteye.com * */ public interface IDepartmentService { public List<Department> getAllDepartments(); }
IEmployeeService.java
package com.iflytek.service; import java.util.List; import com.iflytek.model.Employee; /** * * @author xdwang * * @date 2012-6-6 下午9:14:20 * * @email [email protected] * * @blog xdwangiflytek.iteye.com * */ public interface IEmployeeService { public List<Employee> getAllEmployees(); public void updateEmployee(Employee emp); public void deleteEmployee(Integer id); public Employee getEmployee(Integer id); public void insertEmployee(Employee emp); }
service实现
DepartmentServiceImpl.java
package com.iflytek.serviceImpl; import java.util.List; import com.iflytek.dao.IDepartmentDao; import com.iflytek.daoImpl.DepartmentDaoImpl; import com.iflytek.model.Department; import com.iflytek.service.IDepartmentService; /** * * @author xdwang * * @date 2012-6-6 下午9:16:11 * * @email [email protected] * * @blog xdwangiflytek.iteye.com * */ public class DepartmentServiceImpl implements IDepartmentService { private IDepartmentDao dao; public DepartmentServiceImpl() { this.dao = new DepartmentDaoImpl(); } public List<Department> getAllDepartments() { return dao.getAllDepartments(); } }
EmployeeServiceImpl.java
package com.iflytek.serviceImpl; import java.util.List; import com.iflytek.dao.IEmployeeDao; import com.iflytek.daoImpl.EmployeeDaoImpl; import com.iflytek.model.Employee; import com.iflytek.service.IEmployeeService; /** * * @author xdwang * * @date 2012-6-6 下午9:21:42 * * @email [email protected] * * @blog xdwangiflytek.iteye.com * */ public class EmployeeServiceImpl implements IEmployeeService { private IEmployeeDao dao; public EmployeeServiceImpl() { this.dao = new EmployeeDaoImpl(); } public List<Employee> getAllEmployees() { return dao.getAllEmployees(); } public void updateEmployee(Employee emp) { dao.update(emp); } public void deleteEmployee(Integer id) { dao.deleteById(id); } public Employee getEmployee(Integer id) { return dao.getEmployeeById(id); } public void insertEmployee(Employee emp) { dao.insert(emp); } }
DAO接口
IDepartmentDao.java
package com.iflytek.dao; import java.util.List; import java.util.Map; import com.iflytek.model.Department; /** * * @author xdwang * * @date 2012-6-6 下午8:52:17 * * @email [email protected] * * @blog xdwangiflytek.iteye.com * */ public interface IDepartmentDao { /** * 获取所有部门信息 */ public List<Department> getAllDepartments(); public Map<Integer, Department> getDepartmentsMap(); }
IEmployeeDao.java
package com.iflytek.dao; import java.util.List; import com.iflytek.model.Employee; /** * * @author xdwang * * @date 2012-6-6 下午8:52:31 * * @email [email protected] * * @blog xdwangiflytek.iteye.com * */ public interface IEmployeeDao { /** * 获取所有员工信息 * @return 员工信息集合 */ public List<Employee> getAllEmployees(); /** * 根据id获取员工信息 * @param id 员工id * @return 员工实体信息 */ public Employee getEmployeeById(Integer id); /** * 更新员工信息 * @param employee 员工实体信息 */ public void update(Employee employee); /** * 插入员工信息 * @param employee 员工实体信息 */ public void insert(Employee employee); /** * 根据员工id删除员工信息 * @param id 员工id */ public void deleteById(Integer id); }
DAO实现
DepartmentDaoImpl.java
package com.iflytek.daoImpl; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import com.iflytek.dao.IDepartmentDao; import com.iflytek.model.Department; /** * * @author xdwang * * @date 2012-6-6 下午8:58:55 * * @email [email protected] * * @blog xdwangiflytek.iteye.com * */ public class DepartmentDaoImpl implements IDepartmentDao { private static List<Department> departments; private static Map<Integer, Department> departmentsMap; static { departments = new ArrayList<Department>(); departments.add(new Department(new Integer(100), "研发部")); departments.add(new Department(new Integer(200), "人事部")); departments.add(new Department(new Integer(300), "销售部")); departmentsMap = new HashMap<Integer, Department>(); Iterator<Department> iter = departments.iterator(); while (iter.hasNext()) { Department dept = (Department) iter.next(); departmentsMap.put(dept.getDepartmentId(), dept); } } public List<Department> getAllDepartments() { return departments; } public Map<Integer, Department> getDepartmentsMap() { return departmentsMap; } }
EmployeeDaoImpl.java
package com.iflytek.daoImpl; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; import com.iflytek.dao.IDepartmentDao; import com.iflytek.dao.IEmployeeDao; import com.iflytek.model.Department; import com.iflytek.model.Employee; /** * * @author xdwang * * @date 2012-6-6 下午9:01:59 * * @email [email protected] * * @blog xdwangiflytek.iteye.com * */ public class EmployeeDaoImpl implements IEmployeeDao { private static Map<Integer, Department> departmentsMap; private static ArrayList<Employee> employees; static { employees = new ArrayList<Employee>(); employees.add(new Employee(new Integer(1), "小明", "王", new Integer( 36), new Department(new Integer(100), "研发部"), "50000")); employees.add(new Employee(new Integer(2), "三丰", "张", new Integer( 25), new Department(new Integer(300), "销售部"), "35000")); IDepartmentDao deptDao = new DepartmentDaoImpl(); departmentsMap = deptDao.getDepartmentsMap(); } public List<Employee> getAllEmployees() { return employees; } public Employee getEmployeeById(Integer id) { Employee emp = null; Iterator<Employee> iter = employees.iterator(); while (iter.hasNext()) { emp = (Employee) iter.next(); if (emp.getEmployeeId().equals(id)) { break; } } return emp; } public void update(Employee employee) { Integer id = employee.getEmployeeId(); for (int i = 0; i < employees.size(); i++) { Employee tempEmp = (Employee) employees.get(i); if (tempEmp.getEmployeeId().equals(id)) { employee.setDepartment((Department) departmentsMap.get(employee .getDepartment().getDepartmentId())); employees.set(i, employee); break; } } } public void insert(Employee employee) { int lastId = 0; Iterator<Employee> iter = employees.iterator(); while (iter.hasNext()) { Employee temp = (Employee) iter.next(); if (temp.getEmployeeId().intValue() > lastId) { lastId = temp.getEmployeeId().intValue(); } } employee.setDepartment((Department) departmentsMap.get(employee .getDepartment().getDepartmentId())); employee.setEmployeeId(new Integer(lastId + 1)); employees.add(employee); } public void deleteById(Integer id) { for (int i = 0; i < employees.size(); i++) { Employee tempEmp = (Employee) employees.get(i); if (tempEmp.getEmployeeId().equals(id)) { employees.remove(i); break; } } } }
ok,搞定,最后的一个效果图如下所示:
通过开始的Struts图结合Demo,可以整体上对Struts2.2开发流程以及请求处理流程有了个认识。