首页 > 代码库 > Struts2 技术全总结 (正在更新)
Struts2 技术全总结 (正在更新)
背景:Struts1是一个高度成熟的框架,运行效率高,但其致命缺陷在于与JSP/Servlet的耦合非常紧密,因而导致了一些严重问题。其次,Struts1与Servlet API的严重耦合,使应用难以测试;Struts1代码严重依赖Struts1 API,属于侵入式框架。由于其种种局限性,才有了Struts2的诞生。
Struts2与1差别巨大,Struts2以WebWork为核心,采用拦截器的机制处理用户请求,这样的设计使业务逻辑控制器与Servlet API完全脱离开。
Part 1 Struts2快速入门
1. 安装与配置
下载struts-2.3.15.1-all.zip,这里采用该版本,打开后有文件夹:apps 实例代码 ,docs 帮助文档,lib jar包,src 源代码文件;
2.开始写项目
1.添加jar包;(可直接复制实例项目,复制jar)
2.在web.xml中加入struts2的 filter ,内容如下:
<?xml version="1.0" encoding="UTF-8"?><web-app id="WebApp_9",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/j2eehttp://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <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> <welcome-file-list> <welcome-file>index.html<welcome-file> </welcome-file-list></web-app>
从配置文件中,可以看出, Struts2 其实就是一个filter
3.在src文件夹下添加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> <package name="default" namespace="/" extends="struts-default"> <action name="index"> <result>/index.jsp</result> </action> </package> </struts>
4. 写jsp页面,或html页面;写java bean, action, dbutils(此实例为简明,省略了这些内容);
最简单的示例,在web文件夹下写 index.jsp
<%@page language="java" import="java.util.*" pageEncoding="UTF-8"><%String path=request.getContextPath();String basePath=request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";%><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html> <head> <base href="<%=basePath%>"> <title>My JSP ‘index.jsp‘ starting page</title> </head> <body> First One </body></html>
测试:部署tomcat或直接使用preview server,http://localhost:port/项目名/index.adtion
注意, .action
Part 2 Struts2工作流程和核心概念
Struts2使用了WebWork的设计核心(XWork),在内部使用拦截器处理用户请求,从而允许用户业务逻辑控制器和ServletAPI分离。Struts2内部是一个MVC架构,Struts2 的核心控制器是FilterDispatcher,客户端发送请求,而经过核心控制器FilterDispatcher处理,根据页面发送的请求,从而确定请求的是哪个action,而action是MVC中的Model,最后确定返回哪个页面(html或jsp)
具体工作流程:
(1)浏览器发送请求;
(2)核心拦截器FilterDispatcher根据请求决定调用合适的action;
(3)拦截器自动对应运用通用功能;
(4)回调用action上的execute()方法;
(5)action的execute()方法处理信息结果输出到浏览器。
2)核心概念:当浏览器发送请求到Servlet容器时,会经历一系列Filter过滤器,这些过滤器包括ActionContextCleanUp,接着FilterDispatcher被调用,通过参考ActionMapper决定一个请求和一个Action关联。FilterDispatcher参考框架的配置文件管理,ActionProxy生成一个ActionInvocation,它负责执行命令,并负责查找适当的结果以和struts.xml中的action result相比较,调用一个在JSP或FreeMarker中绘制的模板。而Action是需要与struts.xml配置文件一起结合使用的,下面将详细讲述配置文件使用:
[1] struts.xml文件配置
先来个实例:
login.jsp
<%@page language="java" import="java.util.*" pageEncoding="utf-8"%><%String path=request.getContextPath();String basePath=request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";%><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html> <head> <base href="<%=basePath%>"> <title></title> </head> <body> <form action="login" method="post"> 用户名:<input type="text" name="uname"></br> 密码:<input type="password" name="pwd"></br> <input type="submit" value="登录"> </form> </body></heml>
LoginAction类
public class LoginAction{ private String uname; private String pwd; public String getUname(){ return uname; } public void setUname(String uname){ this.uname=uname; } public String getPwd(){ return pwd; } public void setPwd(String pwd){ this.pwd=pwd; } public String excute(){ if("JokerJason".equals(uname)&&"12345".equals(pwd)){ return "success"; } return "fail"; }}
注意:在上述LoginAction中,属性uname和pwd必须和页面保持一致,这是因为在Struts2中提供属性参数及set/get方法通过ioc的方式将页面参数注入到Action类里。
写完Action类,对其进行配置,在src目录下创建struts.xml文件,如下
<struts> <package name="default" namespace="/" extends="struts-default"> <action name="login" class="LoginAction"> <result name="success">success.jsp</result> <result name="fail">fail.jsp</result> </action> </package></struts>
(1)package: 包在java中是用来分类的,在Struts2中,也是用来分类的,在Struts2的配置文件中,一个文件可以有多个包;
package有很多属性,name是包名,namespace是命名空间,如果访问一个Action,则一定要是namespace+actionname;
如本例中,要访问LoginAction,则必须是:http://localhost:8080/项目名/login;最后的login就是namespace+action;
extends指定继承哪个Struts的组件,通常都是继承struts-default才能使用Struts中组件。当然如果想使用JSON,就需要继承JSON-default;
(2)action:action是核心控制器FilterDispatcher将要访问的具体action,name指定的是action的名称,class指定的是具体类名,通常是包名+类名;
(3)result:指的是返回的结果页面,name指定返回结果,在实际路径页面中一定要加"/"
[2]Action对象详解
Struts2中Action无须实现任何接口和任何类型,但是为了方便实现Action,大多数情况下采用下面两种方式对Action进行处理:
(1)继承com.opensymphony.xwork2.ActionSupport;
(2)实现com.opensymphony.xwork.Action;
默认情况下,都需要重载(Override)或者覆写(Overload)其中的String excute()throws exception方法。
下面对上面实例进行修改,两个环节进行:
1.将LoginAction继承ActionSupport
import com.opensymphony.xwork2.ActionSupport;public class LoginAction extends ActionSupport{ private String uname; private String pwd; public String getUname(){ return uname; } public void setUname(String uname){ this.uname=uname; } public String getPwd(){ return pwd; } public void setPwd(String pwd){ this.pwd=pwd; } public String execute(){ if("jokerjsaon"equals(uname)&&"1234"equals(pwd)){ return "fail";//与struts.xml中配置的result相同 } }}
2.在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> <package name="default" namespace="/" extends="struts-default"> <action name="login" class="LoginAction"> <result name="success">success.jsp</result> <result name="fail">fail.jsp</result> </action> </package></struts>
这样修改并未看到太大变化,反而会因为继承一个ActionSupport类增加了累赘,但,先看一下Action接口的实现方式:
package com.opensymphony.xwork2; public interface Action{ public static final String SUCCESS="success"; public static final String NONE="none"; public static final String ERROR="error"; public static final String INPUT="input"; public static final String LOGIN="login"; public String execute()throws Exception; }
表面上看实现Action接口没有什么好处,事实上实现改接口,有利于更好的实现Action类。Action接口中定义了五个字符串常量,因为ActionSupport是Action的实现类,所以继承了ActionSupport也就可以使用这五个常量了:如下
import com.opensymphony.xwork2.ActionSupport;public class LoginAction extends ActionSupport{ private String uname; private String pwd; public String getUname(){ return uname; } public void setUname(String uname){ this.uname=uname; } public String getPwd(){ return pwd; } public void setPwd(String pwd){ this.pwd=pwd; } public String execute(){ if("jokerjsaon"equals(uname)&&"1234"equals(pwd)){ return ERROR;//注意此处 } }}
Part 3 Struts2配置文件
1. Struts的配置文件类型
Struts2的配置文件分为内部使用和供开发人员使用两大类,内部配置文件由Struts2框架自动加载,对其自身进行配置,其他的配置文件由开发人员使用,用于对Web应用进行配置。Struts2所需的配置文件如下表:
文件 | 位置 | 用途 |
web.xml | /WEB-INF/ | Web部署描述文件 |
struts.xml | /WEB-INF/classes | 包含result映射,action映射,拦截器配置等 |
struts.properties | /WEB-INF/classses | 与struts.xml作用一样,不同的配置文件罢了 |
struts-default.xml | /WEB-INF/lib/struts-core.jar | Struts2提供的默认配置,由框架提供 |
struts-plugin.xml | /WEB-INF/lib/struts-xxx-plugin.jar | Struts2框架插件所提供的配置文件 |
下面对核心内容进行解释:
[1] struts.xml配置文件:是该框架的核心配置文件,用于配置和管理开发人员编写的Action,在这个配置文件中,开发人员可以配置作用于Action的拦截器,Action和result的映射等。
[2]struts-default.xml: 基础配置文件,为框架提供默认设置,该文件包含在struts2-core-2.x.jar中,由框架自动加载。其会被自动包含在struts.xml中,以提供标准的配置设置而不需要复制其内容:
<package name="default" extends="struts-default"/>
(其内容包括Servlet转发,Servlet重定向,FreeMarker模板输出,XSTL渲染,内置拦截器和不同拦截器组成的拦截器栈等等)
2. Struts2的包配置
Struts2中的包类似于java中的包,提供了将action,result,拦截器和拦截器栈组织为一个逻辑单元的一种方式。Struts2中的包可以扩展另外的包,从而继承原有包的所有定义,并可以添加自己包的特有配置,以及修改原有包的部分配置。在struts.xml中庸package定义包,下面为属性:
+ name:(必须) 被其他包引用的key(键);
+ extends: (不必须) 指定需要扩展的包(如果需要扩展多个包,用","逗号隔开,父包必须在子包前面) ;
+ namespace:(不必须) 指定命名空间;
+ abstract :(不必须) 声明包为抽象(这样包内不能再有action的定义,比如前面struts-default.xml就是抽象包,可以使用抽象包进行一些默认配置,其他包继承以减少工作量);
实例:
<package name="default" extends="struts-default"> <action name="login" class="LoginAction"> <result name="success">/success.jsp</result> <result name="error">/error.jsp</result> </action></package><package name="test" extends="default" namespace="/my"></package>
当发起/my/login.action的路径请求时,会先在test包内查找是否有合适的action,而在此时是在test包中存在LoginAction处理请求,因为test包继承了default包,某种意义上包的extends相当于java的extends。
3. 名称空间配置
package的namespace属性可以将包中的action配置为不同的名称空间,这样在不同名称空间中可以使用同名action。Struts2框架使用action名字和它所在的名称空间来标识一个action。
当Struts2接收到一个请求时,它会将请求URL分为namespace和action名字两个部分,然后Struts2从struts.xml中查询namespace/action这个命名对,如果没有找到,就会在默认的名称空间搜索相应的action名。默认名称空间用空字符串" "表示,如果在定义包时没有使用namespace,就指定了默认名称空间。
此外,Struts2还支持以"/"命名的根名称空间,如果直接请求Web应用上下文路径下的action,那么框架会在根名称空间查找相应的action,如果没有找到就在默认名称空间查找:
<!--default包在默认名称空间中--><package name="default" extends="struts-default"> <action name="foo" class="LoginAction"> <rfesult name="success">foo.jsp</result> </action> <action name="bar" class="LoginAction"></action><!--mypackage1包在根名称空间里--><package name="mypackage1" namespace="/"> <action name="moo" class="LoginActionTwo"> <result name="success">/moo.jsp</result> </action></package><!--mypackage2包在/accp名称空间中--><package name="mypackage2" namespace="/accp"> <action name="foo" class="LoginActionThree"> <result name="success">/foo2.jsp</result> </action></package>
当发起请求/moo.action时,框架会在根空间(‘/‘)查找moo.action,如果没有找到再在默认空间查找,此例中Mypackage中存在moo.action,因此它会被执行,结果转向moo.jsp。
4. Action相关配置
struts2的核心功能是Action,对于开发人员来说,使用struts2框架主要的工作就是编写action类。Action类一般都要实现Action接口,并实现接口中的execute()方法:签名如下:
public String execute()
Struts2并不要求自己编写的Action类一定实现Action接口,也可以编写普通java类作为Action,只要此类提供一个返回类型为String的无参public方法:
public String xxx()
实际开发中,Action类很少使用Action接口,通常都是继承ActionSupport类。
开发好Action后,在struts.xml配置,以告诉Struts2框架,针对某个URL的请求应该交给哪个Action处理。
struts.xml中action元素属性如下:
+ name 必须 Action名称,用于匹配请求;
+ class 非必须 Action实现类的完整类名
+ method 非必须 执行Action调用的方法;
+ converter 非必需 应用于Action的类型转换器的完整类名;
! 注意 : 在为Action取名时,默认情况下不许出现斜杠("/"),如果必须使用,例如<action name="book/manage" class="LoginAction">,就需要在struts.xml中加入: <constant name="struts.enable.SlashesInActionNames" values="true">
此外,不在取名中使用"_"和"."避免一些莫名其妙的问题!
前面讲述了在Struts2中可以使用一个普通的java类作为Action类,而且在此类中的方法没有特殊要求,对于此点Struts2如何处理?其实Action中有一个method属性可以自定义方法,而不需要使用默认的excute()方法。
例如在一个新闻发布系统中,对新闻有四种操作,添加,修改,删除,查询。在具体实现中,为了节约Action数量,通常在一个Action中编写四个方法实现CURD操作:
public class CurdAction(){ public String addNews(){return SUCCESS;} public String deleteNews(){return SUCCESS;} public String updateNews(){return SUCCESS;} public String selectNews(){return SUCCESS;}}
现在的问题是,如何才能让框架在不同请求到达时,去调用CurdAction里面的相应方法呢?在执行Action时,默认的调用方法是execute()方法。Action中的method属性可以指定不同的方法,如下配置:
<package name="default" extends="struts-default"> <!--请求/list时,调用CurdAction类上的selectNews方法--> <action name="list" class="CurdAction" method="selectNews"> <result>/list.jsp</result> </action> <action name="edit" class="CurdAction" method="editNews"> <result>/edit.jsp</result> </action> <action name="delete" class="CurdAction" method="deleteNews"> <result>/delete.jsp</result> </action> <action name="other" class="CurdAction"> <result>/edit.jsp</result> </action></package>
Struts2在根据Action元素的method属性查找方法时有两种途径:
+ 查找与method属性值完全一致的方法;
+ 查找doMethod()形式的方法;
在上例中,当请求/list时,Struts2首先查找selectNews()方法,如果找不到,则继续查找名为doCreate()的方法;
[另外一种无须配置就可以直接调用Action中的非execute()方法的方式,就是直接使用struts2动态方法DMI,语法如下
actionName!methodName.action
例如:
<action name="list" class="action.CurdAction">...</action>
当发起/list!selectNews.action时,将调用CurdAction类中的selectNews方法或doSelectNews().
注意:DIM存在安全隐患,可以在struts.xml中配置禁止使用:
<constants name="struts.enable.DynamicMethodInvocation" value="false"/>
]
5.通配符简化配置
通常来说,应用越大,action配置数量越多,通过使用通配符,可以将一些相似的mapping绑在一起,用一个通用的mapping表示。
优势:大大减少配置文件内容;
劣势:可读性差;
使用原则:约定高于配置;在项目中,很多命名规则是约定的,使用通配符必须有一个约定,如下
<action name="User_*" class="action.UserAction method="{1}""> <result>/{1}success.jsp</result></action>
*表示匹配所有,在struts2中,{1}表示第一个*号。
(正在更新)
Struts2 技术全总结 (正在更新)