JAVAEE就业框架之Struts2技术实战开发教程
接下来将详细介绍JavaEE三大框架中经典的MVC框架——Struts2。Struts1的成功得益于其丰富的文档和活跃的社区。· Strtus1支持的表现层技术单一,仅支持JSP作为表现层技术,而目前很多JavaEE应用并不一定使用JSP作为表现层技术,这一点严重制约了Struts1框架的使用。由于存在以上无法避免的问题,从2008年底 Strtus1团队放弃了对Strtus1的更新和维护。Str
接下来将详细介绍JavaEE三大框架中经典的MVC框架——Struts2。Struts1的成功得益于其丰富的文档和活跃的社区。· Strtus1支持的表现层技术单一,仅支持JSP作为表现层技术,而目前很多JavaEE应用并不一定使用JSP作为表现层技术,这一点严重制约了Struts1框架的使用。由于存在以上无法避免的问题,从2008年底 Strtus1团队放弃了对Strtus1的更新和维护。Strtus2框架诞生后,取代了原有的Strtus1和WebWork框架,原来使用Strtus1和WebWork的开发人员都转入使用Strtus2框架,基于这种背景,Strtus2在短时间内迅速成为MVC领域最流行的框架。· Strtus2中的Action不需要与Servlet API耦合,测试变的更简单,代码的复用性更高。· 具有良好的模块化和可扩展性。Strtus2提供了更好的模块化支持,更适合团队协作开发的大项目。
第1章 初识Struts2 本章学习目标 · 了解Struts2的起源和优缺点 · 熟练掌握Struts2的安装和配置 · 理解Struts2的工作流程 · 熟练掌握Struts2的入门程序 在JavaWeb阶段,相信读者已经不知不觉中接触了一些框架,例如JavaScript框架jQuery、单元测试框架Junit等,只是还没有框架这个清晰的概念。 实际上,框架的使命就是Write less,do more!像Struts2这样的大型框架实际上就是一组工具包的集合,通过配置或约定的方式实现以前需要编写代码才能够完成的功能。从另一方面来看,框架也是某个特定领域中常用操作的提炼与封装,无数程序员在大量的开发实践中总结得出了很多解决特定问题的最佳方案,将这些解决方案集成到一起,在相同领域的不同项目工程中实现复用,这就是框架。 接下来将详细介绍JavaEE三大框架中经典的MVC框架——Struts2。 1.1 Struts2概述 Struts2是开源组织Apache开发的一个MVC架构的框架,目的是为了代码的可重用性和可维护性,从字面上看,Struts2好像是Struts1的升级版本,其实二者区别很大,Struts2是Struts1和WebWork结合的产物,既充分发挥了Struts1和WebWork两种技术的优势,又抛弃了Struts1的缺点,使Web开发更加容易。 1.1.1 Struts2的起源 2001年Struts1框架诞生,它是全世界第一个MVC框架,也曾一度是MVC领域最流行的框架。Struts1的成功得益于其丰富的文档和活跃的社区。 随着互联网的飞速发展,Web应用也发展迅速,Struts1的缺点不断暴露,大量开发者不得不开始寻找新的MVC框架,Strtus1的主要问题在于JSP、Servlet耦合非常紧密,导致了许多不可避免的问题,有以下几个方面。 · Strtus1支持的表现层技术单一,仅支持JSP作为表现层技术,而目前很多JavaEE应用并不一定使用JSP作为表现层技术,这一点严重制约了Struts1框架的使用。 · Strtus1框架是在Model2的基础上发展起来的,完全基于Servlet API,与Servlet API严重耦合,一点脱离Web服务器,Action的测试将变得非常困难 · Strtus1的Action类必须继承其提供的Action基类,实现处理方法时,又必须使用大量Strtus1的专有API,这种侵入式设计的存在很大隐患,一单系统需要重构,这些Action类将无法复用。 由于存在以上无法避免的问题,从2008年底 Strtus1团队放弃了对Strtus1的更新和维护。随着JavaEE项目的不断复杂化,很多软件公司和开发人员开始选择更好的MVC框架,如JSP、Tapestry、Spring MVC等,正是在这种背景下,诞生了Strtus2框架。 Strtus2框架诞生后,取代了原有的Strtus1和WebWork框架,原来使用Strtus1和WebWork的开发人员都转入使用Strtus2框架,基于这种背景,Strtus2在短时间内迅速成为MVC领域最流行的框架。 1.1.2 Struts2的优缺点 Strtus2是由WebWork发展而来,具备了WebWork与生俱来的优势,它的优点具体有以下几个方面。 · 实现了MVC模式,结构清晰,使开发者只关注业务逻辑的实现。 · 支持更多的表现层技术,有更好的适应性。Strtus2对JSP、数据校验等表现层技术都提供了很好的支持,开发者还可以根据自己的需要,自定义显示的类型。 · Strtus2中的Action不需要与Servlet API耦合,测试变的更简单,代码的复用性更高。 · 具有良好的模块化和可扩展性。Strtus2提供了更好的模块化支持,更适合团队协作开发的大项目。 · 提供了插件机制,开发者可以在不修改框架的前提下,通过开发插件来扩展Strtus2的功能,增强了框架的可扩展性。 · 提供了丰富的标签,灵活运用这些标签,可以大大提高开发效率。 当然,Strtus2也有其缺点,开发人员应当了解这些缺点,使用时扬长避短,它的缺点具体有以下几个方面。 · 对事件支持不够,在Strtus2中,实际是一个表单Form对应一个Action类,换句话说,就是在Strtus2中实际是一个表单只能对应一个事件,这种事件方式称为application event,application event和component event相比是一种粗粒度的事件。 · 数据校验过于细化,比较繁琐,有些功能还不够简化,例如如果有多个字段,每个字段出错时返回到不同的画面,这个功能在借助Strut2框架提供的校验逻辑就不容易实现。 · 存在两个高危安全漏洞,一个是使用缩写的导航参数前缀时的远程代码执行漏洞,另一个是使用缩写的重定向参数前缀时的开放式重定向漏洞。这些漏洞可使黑客取得网站服务器的"最高权限",从而使企业服务器被控制。 1.2 Struts2下载和使用 在安装Struts2之前,要确保已经成功安装了JDK、Tomcat和Eclipse,如果未完成这些安装,请安装完毕后,再进行下面的步骤。 1.2.1 Struts2开发包下载 登陆http://struts.apache.org/,进入官网,如图1.1所示。 图1.1 Struts2官网 点击Download,进入下载页面,如图1.2所示。 图1.2 Struts2下载页面 这里使用的是最新版本2.5.10.1,下载时有如下几项。 · Full Distribution:下载Struts2的完整版,通常建议下载该选项,该选项包含Struts2的示例应用、空示例应用、核心库、源代码和文档等。 · Example Applications:下载Struts2的示例应用,这些示例应用对于学习Struts2有很大的帮助,下载Struts2的完整版时包含该选项下的全部内容。 · Essential Dependencies Only:下载Struts2的核心库,下载Struts2的完整版时包含该选项下的全部内容。 · All Dependencies:下载Struts2的所有依赖包,下载Struts2的完整版时包含该选项下的全部内容。 · Documentation:下载Struts2的相关文档,包含Struts2的使用文档、参考手册和API文档等,下载Struts2的完整版时包含该选项下的全部内容。 · Source:下载Struts2的全部源代码,下载Struts2的完整版时包含该选项下的全部内容。 通常建议读者下载Full Distribution和Documentation项,下载后的两个压缩包如图1.3所示。 图1.3 Struts2压缩包 将下载得到的两个*.zip文件解压缩,解压后两个文件夹会覆盖在一起,该文件夹包的目录结构如图1.4所示。 图1.4 Struts2文件夹目录结构 如图1.4所示的目录结构中有四个文件夹,存放的具体文件如下: · apps:该文件夹下包含了基于Struts2的示例应用,这些示例应用对于学习者是非常有用的资料。 · docs:该文件夹下包含了Struts2的相关文档,包含Struts2的快速入门、Struts2的文档,以及API文档等内容。 · lib:该文件夹下包含了Struts2框架的核心类库以及第三方插件类库。 · src:该文件夹下包含了Struts2框架的全部源代码。 1.2.2 运行Struts2例子程序 下载并解压完成Struts2的开发包后,里面包含了官方提供的例子程序,具体步骤如下。 (1)安装配置好tomcat服务器,这里使用的是tomcat7.0.57,进入tomcat的bin目录,如图1.5所示。 图1.5 tomcat的bin目录 (2)双击startup.bat运行tomcat服务器,如图1.6所示。 图1.6 tomcat运行图 (3)打开struts2开发包的apps目录,如图1.7所示。 图1.7 struts2开发包apps目录 (4)将struts2-rest-showcase.war复制到tomcat的webapps目录,该war包会自动解压并部署到服务器,如图1.8所示。 图1.8 struts2-rest-showcase.war自动部署 (5)此时打开浏览器,在地址栏输入http://localhost:8080/struts2-rest-showcase运行例子程序,运行成功如图1.9所示。 图1.9 Struts2例子程序 如上运行的Struts2的官方例子程序有基本的增删改查功能,这个例子程序能帮助开发者快速了解Struts2的用途,它用于处理程序中的视图层,能够快速、简便地将数据呈现给用户,在例子程序中点击创建、查看或修改,会跳转到相应的页面进行处理,Struts2代码的具体实现会在后面详细讲解。 1.2.3 在Eclipse中配置Struts2 直接利用tomcat服务器可以运行Struts2例子程序,但实际开发中大多在集成开发环境中来做,这里使用的是Eclipse,具体步骤如下。 (1)在Eclipse中新建Dynamic Web Project,也就是新建一个动态Web工程,如图1.10所示。 图1.10 动态Web工程 (2)在开发基于Struts2框架的Web应用时,不需要用到Struts2的全部功能,没必要将Struts2压缩包下lib目录中的所有jar复制到工程中,这里只用其中的11个jar包即可完成大部分功能,将这些jar包复制到动态Web工程中的WEB-INF/lib路径下,如图1.11所示。 图1.11 动态Web工程目录 (4)打开Web应用的配置文件web.xml,在该配置文件中增加Struts2的核心控制器StrutsPrepareAndExecuteFilter的配置信息,如例1-1所示。 例1-1 web.xml 1 <?xml version="1.0" encoding="UTF-8"?> 2 <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3 xmlns="http://java.sun.com/xml/ns/javaee" 4 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 5 http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" 6 id="WebApp_ID" version="3.0"> 7 <display-name>struts2_01</display-name> 8 <welcome-file-list> 9 <welcome-file>index.html</welcome-file> 10 <welcome-file>index.htm</welcome-file> 11 <welcome-file>index.jsp</welcome-file> 12 <welcome-file>default.html</welcome-file> 13 <welcome-file>default.htm</welcome-file> 14 <welcome-file>default.jsp</welcome-file> 15 </welcome-file-list> 16 <!-- 配置Struts2核心Filter --> 17 <filter> 18 <!-- 过滤器名 --> 19 <filter-name>struts2</filter-name> 20 <!-- 核心Filter的实现类 --> 21 <filter-class> 22 org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter 23 </filter-class> 24 </filter> 25 <!-- 配置核心Filter拦截所有请求 --> 26 <filter-mapping> 27 <!-- 过滤器名 --> 28 <filter-name>struts2</filter-name> 29 <!-- 匹配所有请求 --> 30 <url-pattern>/*</url-pattern> 31 </filter-mapping> 32 </web-app> 例1-1中,在web.xml中配置了Struts2的核心控制器StrutsPrepareAndExecuteFilter,该控制器拦截所有请求,Web应用此时已经具备了对Struts2框架基本功能的支持。 1.3 实现第一个Struts2程序 现在就自己来动手编写一个Struts2程序,亲自感受一下Struts2框架的基本使用。 下面将编写第一个Struts2程序,其功能首页有一个"添加员工"的按钮,点击按钮,跳转到添加员工的页面,输入员工姓名、员工年龄、员工性别,点击"保存"按钮,跳转到员工信息的页面,显示刚添加完成的员工信息,此时点击"回首页"按钮,会跳转回首页。 这里直接使用1.2.3小节中创建好的动态Web工程。 1.3.1 编写前端页面 根据上面添加员工的功能需求,这里需要3个JSP页面,分别为index.jsp、saveUI.jsp和detail.jsp,其中index.jsp为首页,saveUI.jsp为添加员工的页面,detail.jsp为显示员工信息的页面。 在项目中的WebContent目录下,新建index.jsp,接下来编写该文件,如例1-2所示。 例1-2 index.jsp 1 <%@ page language="java" contentType="text/html; charset=UTF-8" 2 pageEncoding="UTF-8"%> 3 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 4 "http://www.w3.org/TR/html4/loose.dtd"> 5 <html> 6 <head> 7 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 8 <title>Insert title here</title> 9 </head> 10 <body> 11 <center> 12 <h1> 13 <a href="${pageContext.request.contextPath }/saveUI.action"> 14 添加员工 15 </a> 16 </h1> 17 </center> 18 </body> 19 </html> 例1-2中,在首页写入了一个"添加员工"的按钮,该按钮访问saveUI.action,该请求会在struts.xml中进行映射。 在项目中的WebContent目录下,新建saveUI.jsp,接下来编写该文件,如例1-3所示。 例1-3 saveUI.jsp 1 <%@ page language="java" contentType="text/html; charset=UTF-8" 2 pageEncoding="UTF-8"%> 3 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 4 "http://www.w3.org/TR/html4/loose.dtd"> 5 <html> 6 <head> 7 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 8 <title>Insert title here</title> 9 <style type="text/css"> 10 .centerText { 11 text-align: center; 12 } 13 .textStyle { 14 border: 1px solid black; 15 } 16 .btnStyle { 17 border: 1px solid black; 18 width: 150px; 19 } 20 .btnStyle:hover { 21 border: 1px solid black; 22 width: 150px; 23 background-color: white; 24 } 25 .tableStyle { 26 border: 1px solid black; 27 border-collapse: collapse; 28 margin: 50px auto 0px auto; 29 } 30 .tableStyle td { 31 border: 1px solid black; 32 padding: 5px; 33 } 34 </style> 35 </head> 36 <body> 37 <form action="${pageContext.request.contextPath }/saveEmp.action" 38 method="post"> 39 <table class="tableStyle"> 40 <tr> 41 <td colspan="2" class="centerText" id="tableHeader"> 42 保存员工信息 43 </td> 44 </tr> 45 <tr> 46 <td>员工姓名:</td> 47 <td> 48 <input class="textStyle" type="text" name="empName" /> 49 </td> 50 </tr> 51 <tr> 52 <td>员工年龄:</td> 53 <td> 54 <input class="textStyle" type="text" name="empAge" /> 55 </td> 56 </tr> 57 <tr> 58 <td>员工性别:</td> 59 <td> 60 <input class="textStyle" type="text" name="empSex" /> 61 </td> 62 </tr> 63 <tr> 64 <td colspan="2" class="centerText"><input class="btnStyle" 65 type="submit" value="保存" /></td> 66 </tr> 67 </table> 68 </form> 69 </body> 70 </html> 例1-3中,在saveUI.jsp页面有一个form表单,表单中可以填写员工姓名、员工年龄和员工性别,最下面有一个保存按钮,点击按钮将提交请求到saveEmp.action,该请求会在struts.xml中进行映射。 在项目中的WebContent目录下,新建detail.jsp,接下来编写该文件,如例1-4所示。 例1-4 detail.jsp 1 <%@ page language="java" contentType="text/html; charset=UTF-8" 2 pageEncoding="UTF-8"%> 3 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 4 "http://www.w3.org/TR/html4/loose.dtd"> 5 <html> 6 <head> 7 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 8 <title>Insert title here</title> 9 <style type="text/css"> 10 .centerText { 11 text-align: center; 12 } 13 .textStyle { 14 border: 1px solid black; 15 } 16 .btnStyle { 17 border: 1px solid black; 18 width: 150px; 19 } 20 .btnStyle:hover { 21 border: 1px solid black; 22 width: 150px; 23 background-color: white; 24 } 25 .tableStyle { 26 border: 1px solid black; 27 border-collapse: collapse; 28 margin: 50px auto 0px auto; 29 } 30 .tableStyle td { 31 border: 1px solid black; 32 padding: 5px; 33 } 34 </style> 35 </head> 36 <body> 37 <table class="tableStyle"> 38 <tr> 39 <td colspan="2" class="centerText" id="tableHeader"> 40 显示员工信息 41 </td> 42 </tr> 43 <tr> 44 <td>员工姓名:</td> 45 <td>${empName }</td> 46 </tr> 47 <tr> 48 <td>员工年龄:</td> 49 <td>${empAge }</td> 50 </tr> 51 <tr> 52 <td>员工性别:</td> 53 <td>${empSex }</td> 54 </tr> 55 <tr> 56 <td colspan="2" class="centerText"> 57 <a 58 href="${pageContext.request.contextPath }/index.jsp"> 59 回首页 60 </a> 61 </td> 62 </tr> 63 </table> 64 </body> 65 </html> 例1-3中,在saveUI.jsp页面有一个form表单,表单中可以填写员工姓名、员工年龄和员工性别,最下面有一个保存按钮,点击按钮将提交请求到saveEmp.action,该请求会在struts.xml中进行映射。 1.3.2 编写控制器文件 如果说web.xml是这个Web应用的命脉,那么struts.xml就是Struts2的命脉,它用来处理请求映射,前端接收到客户的请求,在这个文件中,可以映射到具体的实现类去处理请求,在项目的src目录下,新建struts.xml,接下来编写该文件,如例1-5所示。 例1-5 struts.xml 1 <?xml version="1.0" encoding="UTF-8"?> 2 <!DOCTYPE struts PUBLIC 3 "-//Apache Software Foundation//DTD Struts 4 Configuration 2.5//EN" 5 "http://struts.apache.org/dtds/struts-2.5.dtd"> 6 <struts> 7 <constant name="struts.enable.DynamicMethodInvocation" 8 value="false" /> 9 <constant name="struts.devMode" value="true" /> 10 <package name="default" namespace="/" 11 extends="struts-default"> 12 <action name="saveUI" 13 class="com.opensymphony.xwork2.ActionSupport" 14 method="execute"> 15 <result name="success"> 16 /saveUI.jsp 17 </result> 18 </action> 19 <action name="saveEmp" 20 class="com.qianfeng.struts2.action.EmployeeAction" 21 method="saveEmp"> 22 <result name="save-success"> 23 /detail.jsp 24 </result> 25 </action> 26 </package> 27 </struts> 例1-5中,在<package>标签中配置了两个<action>标签,第一个<action>将saveUI的请求映射到了com.opensymphony.xwork2.ActionSupport类的execute方法,当方法返回success字符串时,跳转到项目根目录下的saveUI.jsp页面。第二个<action>将saveEmp的请求映射到了com.qianfeng.struts2.action.EmployeeAction类的saveEmp方法,当方法返回save-success字符串时,跳转到detail.jsp页面。 1.3.3 编写Struts2处理程序 将前端页面和控制器文件编写完成后,这里开始编写Struts2后台处理的程序,具体处理前端发来的请求,这里编写代码的包名和类名,要与控制器文件中配置的第二个<action>相对应,在工程的src目录下,新建com.qianfeng.struts2.action包,在包下新建EmployeeAction.java文件,如例1-6所示。 例1-6 EmployeeAction.java 1 public class EmployeeAction { 2 public EmployeeAction() { 3 } 4 private String empName; 5 private Integer empAge; 6 private String empSex; 7 public String saveEmp() { 8 return "save-success"; 9 } 10 public String getEmpName() { 11 return empName; 12 } 13 public void setEmpName(String empName) { 14 this.empName = empName; 15 } 16 public Integer getEmpAge() { 17 return empAge; 18 } 19 public void setEmpAge(Integer empAge) { 20 this.empAge = empAge; 21 } 22 public String getEmpSex() { 23 return empSex; 24 } 25 public void setEmpSex(String empSex) { 26 this.empSex = empSex; 27 } 28 } 例1-6中,EmployeeAction类中定义了三个变量,分别对应前端页面中员工姓名、员工年龄和员工性别的name属性值,另外还提供了三个变量对应的get和set方法。saveEmp方法是处理saveEmp.action请求的方法,方法执行后,返回save-success字符串。 1.3.4 运行程序 程序编写完成后,直接右键工程,选择Run As,点击Run on Server,如图1.12所示。 图1.12 右键工程 在弹出的Run on Server界面中,点击Finish直接运行,如图1.13所示。 图1.13 Run on Server界面 在浏览器中访问http://localhost:8080/struts2_01/index.jsp地址,会跳转到主页面,主页面中只有一个按钮,如图1.14所示。 图1.14 主页面 点击添加员工,会跳转到添加员工的页面,此时可以输入要添加的员工信息,如图1.15所示。 图1.15 添加员工页面 点击保存,会跳转到员工信息页面,显示刚添加的员工信息,如图1.16所示。 图1.16 员工信息页面 点击回首页,会再次跳转到首页,这样就完成了一个应用Struts2的Web程序。 1.4 Struts2架构和工作流程 Struts2是一个典型的MVC架构,其中M(逻辑层)用Java程序来实现,V(视图层)用JSP来实现,C(控制层)用Action实现,Struts2本身不提供模型组件,但可以支持Spring、Hibernate等框架,与其他框架组合应用。 Struts2的框架结构与Struts1差别巨大,Struts2是以WebWork为核心,采用拦截器的机制对用户的请求进行处理。Struts2的拦截器机制使得用户的业务逻辑控制器与Servlet API完全分离,业务逻辑控制器更像一个POJO,它的架构如图1.12所示。 图1.17 Struts2架构图 图1.13中可以看到,Struts2处理一个用户请求大致可以分为以下几个步骤。 (1)用户发出一个HTTP请求。 (2)这个请求经过一系列的过滤器来传送,如果Struts2与其他插件或框架集成了,则请求先经过其他过滤器。 (3)StrutsPrepareAndExecuteFilter是Struts2的核心控制器,通过询问Action映射器来确定该请求是否需要调用某个Action,如果需要调用某个Action,则StrutsPrepareAndExecuteFilter就将请求转交给Action代理来处理。 (4)Action代理通过配置管理器访问框架的配置文件struts.xml,从而找到需要调用的Action类。 (5)Action代理创建一个ActionInvocation的实例,该实例使用命名模式来调用。在Action执行的前后,ActionInvocation实例根据配置文件加载与Action相关的所有拦截器。 (6)一旦Action执行完毕,ActionInvocation实例根据struts.xml文件中的配置找到相应的返回结果,返回结果通常是一个JSP页面或者FreeMarker的模板。 (7)最后HTTP响应通过web.xml文件中配置的过滤器返回。 1.5 本章小结 通过本章的学习,大家能够对Struts2框架有初步的认识,重点要掌握的是Struts2开发环境的搭建,能编写出一个基于Struts2的简单程序,理解Struts2的架构和工作流程。 1.6 习题 1.填空题 (1) Struts2是开源组织 开发的一个框架。 (2) Strtus1的主要问题在于 、 耦合非常紧密。 (3) Strtus2实现了 模式,结构清晰,使开发者只关注业务逻辑的实现。 (4) Struts2压缩包的 目录下,存放的是Struts2官方提供的示例程序。 (5) 在动态Web工程中,配置Struts2需要在web.xml中配置Struts2的核心控制器 。 2.选择题 (1) Struts2的核心控制器配置在( )文件中。 A.struts2.xmlB.struts2.properties C.web.xmlD.struts2.java (2) Struts2的所有jar包在( )目录下。 A.appsB.docs C.libD.src (3) Struts2的核心控制器是( )。 A.StrutsPrepareAndExecuteFilterB.Servlet C.ActionD.HttpServletRequest (4) 下列不属于传统SSH三大框的是( )。 A.Struts2B.Spring MVC C.HibernateD.Spring (5) 如果配置Struts2拦截所有请求,应在<url-pattern>标签中配置( )。 A./*B.* C..D.? 3.思考题 (1) 请简述Struts2与Struts1的区别。 (2) 请简述Struts2的起源。 (3) 请简述Struts2的优缺点。 (4) 请简述Struts2开发环境的配置过程。 (5) 请简述Struts2的工作流程。 4.编程题 编写程序,完成如下功能: (1)运行程序跳转到首页index.jsp,显示如下按钮: (2)点击去登陆,跳转到login.jsp,显示如下页面: (3)输入正确的用户名密码,用户名输入"root",密码输入"admin",点击登陆,会跳转到success.jsp,显示如下页面: (4)若输入错误的用户名和密码,则跳转到error.jsp,显示如下页面: