Struts2是一个优秀的MVC框架,也是我比较喜欢用的框架。它个各种配置基本都可以集中在一个xml文档中完成。现在让我们看看如何简单几步实现常用功能。
一、搭建Struts2的开发环境
1)首先是利用Maven导入依赖,其实真正使用的只有一个struts2-core包
<dependencies> <dependency> <groupId>org.apache.struts</groupId> <artifactId>struts2-core</artifactId> <version>2.3.24</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> </dependencies>
2)新建各种package(其实建包这类的事情都是个人习惯,你也可以等用到再建)
3)配置web.xml
顺带提一句,如果你没有通过Maven来引入依赖而是通过Struts官网下载的完整开发包,可以直接找到事例配置文件。直接导入进你的项目就可以了,省时又省力,安全又方便。
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" > <web-app> <display-name>Struts2 Demo</display-name> <filter> <filter-name>struts2</filter-name> <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>
4)增加资源文件struts.xml(这个文件的默认路径就是在classes下面)
直接在官方开发包里找一个放进项目中就可以了。
以上4步就可以完成struts2的开发环境搭建,真的很简单。
二、功能实现与代码示例
1)首先是建立测试使用的jsp页面。
首页:
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib prefix="s" uri="/struts-tags"%> <% String path = request.getContextPath(); String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/"; %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <base href="<%=basePath%>"> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>Basic Struts 2 Application - Welcome</title> </head> <body> <h1>Welcome To Struts 2!</h1> <p> <a href="register.jsp">Please register</a> for our network station. </p> <p> Here is <a href="login.jsp">login link</a>. </p> </body> </html>
跳转成功和跳转失败页面(每次开发我都习惯配置这样一对页面):
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib prefix="s" uri="/struts-tags"%> <% String path = request.getContextPath(); String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/"; %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <base href="<%=basePath%>"> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>Insert title here</title> </head> <body> <h1>success!</h1> <p>Log in to the <a href="manager/management">Management</a></p> <s:property value="#session.user" /> <s:debug></s:debug> </body> </html>
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib prefix="s" uri="/struts-tags"%> <% String path = request.getContextPath(); String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/"; %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <base href="<%=basePath%>"> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>Insert title here</title> </head> <body> <h1>error!</h1> <s:property value="#request.message" /> <s:debug></s:debug> </body> </html>
注册页面:
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib prefix="s" uri="/struts-tags"%> <% String path = request.getContextPath(); String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/"; %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <base href="<%=basePath%>"> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>register</title> </head> <body> <h1>register</h1> <s:form action="register"> <s:textfield name="userVo.userName" label="User Name" /> <s:password name="userVo.userPassword" label="User Password" /> <s:password name="userVo.userPasswordConfirm" label="Confirm" /> <s:submit /> </s:form> </body> </html>
登录页面:
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib prefix="s" uri="/struts-tags"%> <% String path = request.getContextPath(); String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/"; %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <base href="<%=basePath%>"> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>login</title> </head> <body> <h1>login</h1> <s:form action="login"> <s:textfield name="user.userName" label="User Name" /> <s:password name="user.userPassword" label="User Password" /> <s:submit /> </s:form> </body> </html>
后台管理页面:
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib prefix="s" uri="/struts-tags"%> <% String path = request.getContextPath(); String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/"; %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <base href="<%=basePath%>"> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>management</title> </head> <body> <h1>Management</h1> <a href="manager/management-add">add</a> <br /> <a href="manager/management-remove">remove</a> <br /> <a href="manager/management-modify">modify</a> <br /> <s:property value="#request.operate" /> </body> </html>
2)配置struts.xml
由于我已经事先在IDE中完成了整个项目,所以这里就直接提供完整配置。实际开发中往往都是一条一条增加的,特别是拦截器和监听器的配置其实是在整个action都完成以后才添加的功能,具体的测试步骤就不一一赘述了。
<?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> <!-- struts2 支持"!"的动态方法调用,但并不建议 --> <constant name="struts.enable.DynamicMethodInvocation" value="false" /> <constant name="struts.devMode" value="true" /> <!-- 配置一个监听器 --> <bean type="com.opensymphony.xwork2.ActionEventListener" class="action.listener.ServiceListener" /> <package name="default" namespace="/" extends="struts-default"> <!-- 自定义拦截器需要同时继承默认拦截器栈 --> <interceptors> <interceptor name="timer" class="action.interceptor.Timer" /> <interceptor-stack name="myStack"> <interceptor-ref name="defaultStack" /> <interceptor-ref name="timer" /> </interceptor-stack> </interceptors> <!-- 在package下配置的拦截器可以对所有action生效 --> <default-interceptor-ref name="myStack" /> <!-- 若用户请求不存在,跳转掉首页。减少报错的几率 --> <default-action-ref name="index" /> <!-- 配置统一的跳转页面,一般针对异常处理 --> <global-results> <result name="error">/error.jsp</result> </global-results> <action name="index"> <result>/index.jsp</result> </action> <action name="register" class="action.RegisterAction" method="execute"> <result name="success">/login.jsp</result> <result name="input">/register.jsp</result> </action> <action name="login" class="action.LoginAction" method="login"> <result name="success">/success.jsp</result> </action> <!-- 省略了方法调用和结果集返回字符串,默认就是execute()方法和success结果集 --> <action name="management" class="action.ManagementAction"> <result>/management.jsp</result> </action> </package> <!-- 配置包继承 --> <package name="manager" namespace="/manager" extends="default"> <action name="management" class="action.ManagementAction"> <result>/management.jsp</result> </action> <!-- 使用通配符的动态方法调用是struts2推荐的方法 --> <action name="management-*" class="action.ManagementAction" method="{1}"> <result name="success">/management.jsp</result> </action> </package> </struts>
3)编写功能代码
(1)model和modelVo
package model; public class User { private int id; private String userName; private String userPassword; public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getUserPassword() { return userPassword; } public void setUserPassword(String userPassword) { this.userPassword = userPassword; } public int getId() { return id; } public void setId(int id) { this.id = id; } @Override public String toString() { return "User [userName=" + userName + ", userPassword=" + userPassword + "]"; } }
package model.vo; public class UserVo { private String userName; private String userPassword; private String userPasswordConfirm; public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getUserPassword() { return userPassword; } public void setUserPassword(String userPassword) { this.userPassword = userPassword; } public String getUserPasswordConfirm() { return userPasswordConfirm; } public void setUserPasswordConfirm(String userPasswordConfirm) { this.userPasswordConfirm = userPasswordConfirm; } }
(2)service
配置了两个用户可以正常登陆,其中admin是管理员可以登陆后台
package service; import model.*; /* * 模拟后台数据库操作,orm框架都可以通过这个端口扩展 */ public class GeneralService { public boolean save(User user) { return true; } public boolean login(User user) { if (user.getUserName().equals("admin") && user.getUserPassword().equals("admin")) { return true; } if (user.getUserName().equals("Learnhow") && user.getUserPassword().equals("Learnhow")) { return true; } return false; } public boolean isAdmin(User user) { if (user.getUserName().equals("admin") && user.getUserPassword().equals("admin")) { return true; } return false; } }
(3)action、interceptor和listener
package action; import java.util.Map; import com.opensymphony.xwork2.ActionContext; import com.opensymphony.xwork2.ActionSupport; import model.User; import service.GeneralService; /* * 用户登录 */ public class LoginAction extends ActionSupport { private static final long serialVersionUID = 1L; private GeneralService generalService = new GeneralService(); private User user; public String login() throws Exception { if (generalService.login(user)) { //获得session对象,也可以通过SessionAware继承。还可以获得request和application对象。 Map<String, Object> session = (Map) ActionContext.getContext().get("session"); session.put("user", user); return SUCCESS; } Map<String, Object> request = (Map) ActionContext.getContext().get("request"); request.put("message", "The user name or password mistake, please login again"); return ERROR; } public User getUser() { return user; } public void setUser(User user) { this.user = user; } }
package action; import java.util.Map; import com.opensymphony.xwork2.ActionContext; import com.opensymphony.xwork2.ActionSupport; import model.User; import service.GeneralService; /* * 管理员登录 */ public class ManagementAction extends ActionSupport { // GeneralService对象没有新建,而是通过监听器注入的 private GeneralService generalService; private Map<String, Object> request = (Map) ActionContext.getContext().get("request"); @Override public String execute() throws Exception { Map<String, Object> session = (Map) ActionContext.getContext().get("session"); User user = (User) session.get("user"); if (user.getUserName().equals("admin") && user.getUserPassword().equals("admin")) { return SUCCESS; } return ERROR; } // 动态的add()方法调用 public String add() throws Exception { request.put("operate", "add"); return SUCCESS; } public String remove() throws Exception { request.put("operate", "remove"); return SUCCESS; } public String modify() throws Exception { request.put("operate", "modify"); return SUCCESS; } public GeneralService getGeneralService() { return generalService; } public void setGeneralService(GeneralService generalService) { this.generalService = generalService; } }
package action; import com.opensymphony.xwork2.ActionSupport; import model.User; import model.vo.UserVo; import service.GeneralService; /* * 用户注册 */ public class RegisterAction extends ActionSupport { private static final long serialVersionUID = 1L; private GeneralService generalService = new GeneralService(); private User user; //添加了setter和getter方法的变量会跟随请求被存入值栈 private UserVo userVo; @Override public String execute() throws Exception { user = new User(); user.setUserName(userVo.getUserName()); user.setUserPassword(userVo.getUserPassword()); generalService.save(user); return SUCCESS; } //struts2的验证方法,可以针对表单做动态验证。调用验证方法的返回字符串是input @Override public void validate() { if (userVo.getUserName().length() == 0) { addFieldError("userVo.userName", "user name is required."); } if (userVo.getUserPassword().length() == 0) { addFieldError("userVo.userPassword", "user password is required."); } if (!userVo.getUserPassword().equals(userVo.getUserPasswordConfirm())) { addFieldError("userVo.userPasswordConfirm", "passwords don't match"); } } public UserVo getUserVo() { return userVo; } public void setUserVo(UserVo userVo) { this.userVo = userVo; } }
package action.interceptor; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.interceptor.AbstractInterceptor; /* * 给action添加一个运行时间的拦截器 */ public class Timer extends AbstractInterceptor { @Override public String intercept(ActionInvocation invocation) throws Exception { long startTime = System.currentTimeMillis(); String result = invocation.invoke(); long endTime = System.currentTimeMillis(); System.out.println("Time:" + (endTime - startTime)); return result; } }
package action.listener; import com.opensymphony.xwork2.ActionEventListener; import com.opensymphony.xwork2.util.ValueStack; import action.*; import model.*; import service.GeneralService; /* * 实现的ActionEventListener的监听器,可以针对action和异常处理 * 根据官方文档的描述,监听器的作用是在action实例化的过程中或有异常发生的时候增加逻辑处理,这一点不同于拦截器。也就是说监听器是可以对action做出区分的。 */ public class ServiceListener implements ActionEventListener { public Object prepare(Object action, ValueStack stack) { if (action instanceof ManagementAction) { //通过struts2框架为特定action注入对象 ((ManagementAction) action).setGeneralService(new GeneralService()); } return action; } public String handleException(Throwable t, ValueStack stack) { return null; } }
Struts2基本配置大概就是这样,其实也没有什么难度,关键是好用。不过现在好像依然使用Struts2新开发的项目越来越少了,更多项目使用SpringMVC代替,但是如果是我自己写项目Struts依然是首选,因为我比较“土”!
最后依照惯例附上整个项目的部署目录和IDE版本: