Struts2
入门
struts 的的简介
Struts2是一个基于MVC设计模式的Web应用框架,它本质上相当于一个servlet,在MVC设计模式中,Struts2作为控制器(Controller)来建立模型与视图的数据交互。Struts 2是Struts的下一代产品,是在 struts 1和WebWork的技术基础上进行了合并的全新的Struts 2框架。其全新的Struts 2的体系结构与Struts 1的体系结构差别巨大。Struts 2以WebWork为核心,采用拦截器的机制来处理用户的请求,这样的设计也使得业务逻辑控制器能够与ServletAPI完全脱离开,所以Struts 2可以理解为WebWork的更新产品。虽然从Struts 1到Struts 2有着非常大的变化,但是相对于WebWork,Struts 2的变化很小。
从一个高水平角度看,Struts2 是一个MVC拉动的(或MVC2)框架,Struts2 的模型-视图-控制器模式是通过以下五个核心部分进行实现的:
- 操作(Actions)
- 拦截器(Interceptors)
- 值栈(Value Stack)/OGNL
- 结果(Result)/结果类型
- 视图技术
而Struts2 与传统的MVC框架略有不同,因为它由Action扮演模型的角色,而不是控制器,虽然这样会有一些重叠。
环境准备
首先到Struts2的官网下载框架压缩包,解压后如下所示目录结构。
官网:https://struts.apache.org/
接着在自己的编程 软件上创建一个javaWeb项目
在WEB-INF文件夹下的lib文件夹下面导入struts框架核心的几个架包,因为struts2框架拥有很多的架包,很多我们用不到的就没有必要导入,用到时再导入即可。但是核心架包一定要导入才能是项目真正的能够使用struts2框架。
核心架包
- commons-logging-1.1.x.jar (ASF出品的日志包,struts2使用这个日志包来支持JDK和Log4j的日志记录。)
- freemarker-2.3.x.jar (表现层框架,定义了struts2的可视组件主题,struts2的UI标签的模版,使用FreeMark编写)
- ognl-2.6.x.jar (对象图导航语言(Object Graphic Navigation Language),struts2框架通过其读写对象属性)
- struts2-core-2.x.x.jar (struts2的核心类库)
- xwork.jar ( webwork的核心库,struts2在其上构建)
- commons-fileupload-1.2.1.jar (文件上传组件,2.1.6版本后必须加入此文件)
找这些架包如果是建立的maven项目直接上maven库搜索链接maven会直接下载添加,如果新建的不是maven项目则需要在下载的架包中自己拷贝到项目中。
具体的操作是解压下载的struts2的架包,在如下目录结构中打开apps
打开lib文件夹,将里面的所有jar复制到自己的项目的lib包下面即可使用struts2的基础核心功能。
Struts2的入门小案例
编写一个简单的登录页面跳转,获取pojo返回参数显示在可视页面的例子。
这里新建项目的规则与普通的javaweb项目没有区别,都是在WEB-INF包下面建立网页页面,这里我选择建立的是jsp页面。
在src目录下面建立包,在包里建立相应的java类。
这里需要注意的是在包里建立的pojo类,也就是对应的actiong类,还需要在src目录下建立一个struts.xml文件用来配置pojo类的action。
第一步首先在项目的web.xml文件中配置struts2的拦截器
<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-INF文件夹下建立jsp页面,在src目录下的包下建立pojo类,在src目录下建立struts.xml配置文件。
登录成功跳转页面success.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">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>用户名:${username}</h1>
<h1>密码:${password}</h1>
</body>
</html>
登录页面login.jsp页面
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<form action="helloWorld" method="post">
<span>用户名</span>
<input type="text" name="username"><br>
<span>密码</span>
<input type="password" name="password"><br>
<button type="submit">登录</button>
</form>
</body>
</html>
java类
import com.opensymphony.xwork2.ActionSupport;
public class helloAction extends ActionSupport {
/**
* 这里的类属性,如果设置为private属性,那就必须要为属性建立set和get方法前端页面才可以顺利取到值。
* 如果设置为public属性,则不提供get和set方法前端也可以顺利取到值。
*/
//变量名要和提价数据的<input/>标签中的name的值是一样的,这样才可以获取。
public String username;
public String password;
@Override
public String execute() throws Exception {
return "success";
}
/*public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}*/
}
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中可以有多个action,其中package的name属性是自己取的一个任意名称,extends属性
继承默认的struts-default -->
<package name="web" extends="struts-default" namespace="/*">
<!-- action中的name属性要和form表单中的action属性的名字一致才能正确运行
class属性的值是这个action所连接的pojo类的路径,代表的是我这个action链接的是 helloAction这一个java类-->
<action name="helloWorld" class="com.yxs.web.helloAction">
<!-- result属性的意思是pojo类方法返回的结果的一个字符串,如果返回的字符串和result中的name属性的值相同
则执行result标签里面的内容,比如跳转到那个页面 -->
<result name="success">/success.jsp</result>
</action>
</package>
</struts>
接下里布置项目在服务器上运行测试。
这里在success.jsp页面获取到用户输入的信息时,可以使用${变量名字},同时也可以使用struts2自带的标签库中的<s:property value=“变量名”/>来实现获取。但需要注意的是在使用这个标签的时候在myeclipse中需要手动导入<%@ taglib prefix=“s” uri="/struts-tags"%>。
在jsp页面中显示变量的方法
1.使用EL表达式:${变量名}
2.使用标签<s:property value="变量名"/>
这里需要说明的是struts2中有一些系列的标签库,可以用来编写前端的页面,但是如果使用了struts2的标签编写前端页面那么就要求后端代码使用struts2框架,有限制不能很好兼容。一般编写前端页面采用的任然是HTML标签和jsp的一些标签项。
<constant name="strtus.action.extension" value="do"></constant>
在struts.xml配置文件中加入上述一行代码,位置是struts标签之内,packe标签之前,加上这一行代码,浏览器的路径去访问该action的时候在其名字后面加上.do即可访问。
同时我们也不一定将所有的struts.xml的配置放到同一个xml文件内,也可以为每一个javaAction类编写一个.xml文件,只需要将如下代码添加到struts.xml配置文件中即可。这也叫做Action的拆分。
<include file="part1.xml"></include>
<!--其中part1是单独写的一个配置javaAction的一个配置文件 -->
Action 接口
概述
Actions是Struts2框架的核心,因为它们适用于任何MVC(Model View Controller)框架。 每个URL映射到特定的action,其提供处理来自用户的请求所需的处理逻辑。
但action还有另外两个重要的功能。 首先,action在将数据从请求传递到视图(无论是JSP还是其他类型的结果)方面起着重要作用。 第二,action必须协助框架确定哪个结果应该呈现在响应请求的视图中。
Struts2中actions的唯一要求是必须有一个无参数方法返回String或Result对象,并且必须是POJO。如果没有指定no-argument方法,则默认是使用execute()方法。
Action的作用
- 封装工作单元
- 数据转移的场所
- 返回结果字符串
创建Action
- 创建一个action类实际上就死创建一个java类,不需要继承任何类或者接口,只是在这个类中必须要有一个名字叫做execute()的方法,该方法的返回值类型是字符串,可以是任意字符串,它的作用只是为了让struts.xml配置文件中对应的Actiong下的result标签接收到这一个结果,进行相应的页面跳转工作。那由此就可见result标签的name属性就一定是和execute()方法返回的字符串是一致的。
- 创建的类实现了Action接口或者继承了ActionSupport类,重写了里面的execute方法。
这里说execute()方法返回一个字符串,可以是自己命名的任意字符串,只要和struts.xml文件中对应的Action类中的result标签的name属性一致就可以死,但是我们也可以直接使用Action 接口中内部定义的五个常量。
Action接口中定义的五个常量:
public static final String ERROR //返回错误
public static final String INPUT //返回输入页面
public static final String LOGIN //返回登录页面
public static final String NONE //返回空,比如上传下载
public static final String SUCCESS //返回成功
Struts2 访问ServletAPI的方法
1.解耦方式
原理:ActionContext是Action执行的上下文,Action的上下文可以看作是一个容器,里面封装了请求(Request)、会话(Session)、Application等,这里面的Request、Session、Application是Map类型的,往里面封装的是键值对,所以这就体现了struts2不与底层servlet Api打交道,那么对很多web的相关对象进行封装,这样可以达到Action与web层解耦。
Map<String,Object> request = ActionContext.getContext().get("request");
//往request里封装数据request.put("name", value);
Map<String,Object> session = ActionContext.getContext().getSession();
//将数据封装到session中session.put("name", value);
这一方法框架并不依赖于Servlet来运行,不具有耦合性。
2.耦合方式
使用ServletActionContext获取, 这种方法获取的都是原生态的ServletAPI
原理:ActionContext是被存放在当前线程中的,获取ActionContext也是从ThreadLocal中获取的。所以在执行拦截器、action和result的过程中,由于他们都是在一个线程中按照顺序执行的,所以可以可以在任意时候在ThreadLocal中获取 ActionContext。 它继承ActionContext,所以ServletActionContext也可以得到HttpServetRequest、HttpServletResponse,它也提供了直接与Servlet相关对象访问的功能,它可以取得的对象
耦合就要依赖于servlet来运行,和获得的属性和以前是一样的,所以就要在项目中导入servlet依赖,不然项目无法运行。
HttpServletRequest request = ServletActionContext.getRequest();
HttpServletResponse response = ServletActionContext.getResponse();
HttpSession session = request.getSession();
ServletContext application = ServletActionContext.getServletContext();
3.实现特定接口(接口注入方式)
**原理:**从servletConfig拦截器的代码中我们可以看到,首先拦截器会判断action是否实现了SessionAware, ServletRequestAware, ServletResponseAware接口,如果实现的话就从context获取相应的session,request,response通过set方法赋值给action属性,这样我们就通过实现接口可以获取到相应的属性来为我们服务了。
javal类一定要实现下面几个相应的接口: implements ServletRequestAware,ServletResponseAware,ServletContextAware 重写它的set方法,这样就可以使用相应的对象了。例如: public void setServletRequest(HttpServletRequest request){ this.request = request; }
推荐使用解耦方式来访问servleAPI这样不依赖servlet。
解耦和耦合方式可以配合着来使用,当面要向一个页面传递东西,或者是需要获得session的时候用解耦的方式来获取一个map往里面存放东西,当要获取数据的时候就使用耦合的方式来获取存到session中的数据,或者是从一个action传递到另一个Action的数据,或者是从一个页面传到一个Action的数据。
数据验证
在进行登录时,为了防止用户输入非法字符或者输入空,就需要对用户提交的数据进行安全验证。
验证是多样的,前台的html,js等就可以实现数据验证,但是有的浏览器可以将前端的验证关闭,从而跳过验证,这是不安全的,这就需要后台也对数据进行一个验证,从而保障数据的安全性。
Struts2框架就提供了一个验证机制或者说一个验证方法。
在ActionSupport中就提供了一个验证方法:
validate()
下面使用演示一个登陆数据后台使用该方法验证的步骤
1.继承ActionSupport类
2.重写其中的validate()方法
//该方法为数据验证方法
@Override
public void validate() {
if(StringUtils.isEmpty(username)){
//参数1:关键字key,自定义
//参数2:验证提示信息
addFieldError("username", "用户名不能为空");
}
if(password == null ||password.length() == 0){
addFieldError("password", "密码不能为空");
}
}
3.设置struts.xml配置文件中的action中增加一个相应的result
<result name="input">/login.jsp</result>//input是内置的,意思是返回输入页面
4.在登录页面添加放置返回的验证信息的容器
<font color="red"><s:fielderror fieldName="username"/></font>
<font color="red"><s:fielderror fieldName="password"/></font>
<!--这里的<s/>标签是struts2标签库中的标签,使用时需要导入标签库,需要在页面地步加入<%@ taglib prefix="s" uri="/struts-tags"%>代码
需要注意的是fielName的值要和addFieldError("key", "验证提示信息")中的key值相同,请注意key是一个字符串,写的时候请加入双引号,不然可能不会显示提示信息-->
标签库
标签分类
Struts2 的标签库是一个比较完善且功能强大的标签库,它将所有标签都统一到一个标签库中,从而简化了标签的使用;它提供了对主题和模板的支持,极大地简化了视图页面代码的编写;它还提供了对 Ajax 的支持,极大地丰富了视图页面的展示效果。
- Struts2 的标签库主要分为两大类:普通标签和 UI 标签。
- 普通标签的主要功能:在页面生成时控制页面代码的执行流程;
- UI 标签的主要功能:以丰富且可复用的 HTML 文件显示数据。
- 普通标签又分为控制标签(Control Tags)和数据标签(Data Tags)。
- 控制标签用于完成条件逻辑和循环逻辑的控制,也可用于做集合的操作。
- 数据标签用于输出后台的数据和完成其他数据访问功能。
- UI 标签又分为表单标签(Form Tags)、**非表单标签(Non-Form Tags)**和 Ajax 标签。
- 表单标签主要用于生成 HTML 页面中的表单元素,
- 非表单标签主要用于生成非表单的可视化元素,如输出 Action 中封装的信息等。
- Ajax 标签主要用于提供对 Ajax 技术的支持。
标签的使用
使用 Struts2 的标签库非常简单,一般只需在 JSP 文件内使用 taglib 指令导入 Struts2 标签库即可,其导入代码如下所示:
<%@taglib prefix="s" uri="/struts-tags" %>
在上述代码中,taglib 指令的 uri 属性用于指定引入标签库描述符文件的位置,prefix 属性用于指定引入标签库描述符文件的前缀。在 JSP 文件中,所有的 Struts2 标签都建议使用 s 作为前缀。
控制标签
Struts2 标签库中的 s:if、s:elseif、s:else 标签与 Java 中的 if、else if 和 else 语句功能类似,主要用于程序的分支逻辑控制。其中,只有 s:if 标签可以单独使用,而 s:elseif、s:else 都必须与 s:if 标签结合才能使用。
<s:if test="表达式1">
标签体
</s:if>
<s:elseif test="表达式2">
标签体
</s:elseif>
<s:else>
标签体
</s:else>
test属性是一个boolean类型的判断属性。
s:iterator 标签主要用于对集合中的数据进行迭代,它可以根据条件遍历集合中的数据。
数据标签
s:property标签
s:property 标签的作用是输出指定的值,通常输出的是 value 属性指定的值,s:property 标签的属性及属性说明如下。
- value:可选属性,指定需要输出的属性值,如果没有指定该属性,则默认输出 ValueStack 栈顶的值(关于值栈内容会在后面教程中进行讲解)。
- id:可选属性,指定该元素的标识。
- default:可选属性,如果要输出的属性值为 null,则显示 default属性的指定值。
- escape:可选属性,指定是否忽略 HTML 代码。默认值是 true,即忽略输出值中的 HTML 代码。
s:a 标签与 HTML 中的 标签类似,主要用于构造 HTML 页面中的超链接。
表单标签
form:输出一个form表单。
textfield:输出一个单行文本框。
password:输出一个密码框。
radio:输出一个单选框。
checkbox:输出一个复选框。
checkboxlist:输出一个复选框列表。
select:输出一个下拉框。
doubleselect:输出一个双选下拉框部件,第二个下拉框依赖第一个。
combobox:输出一个部件,可以从下拉框的内容填充一个文本框。
optiontransferselect:输出一个选项移动下拉组件,主要是两个下拉框和用来在两个下拉框之间移动选项的按钮。
updownselect:输出一个下拉框控件,带有上下按钮来移动下拉框组件元素。
textarea:输出一个多行文本框。
hidden:输出一个hidden表单字段。
file:输出一个文件选择框。
label:输出一个label。
submit:输出一个submit(提交)表单按钮。
token:输出一个隐藏的字段来防止多次提交表单。
head:输出对应theme的head部分的内容,比如Css和JavaScript的引用。
datepicker:输出一个日期选择部件,使用JavaScript和DOM。
reset:输出一个reset(重置)表单按钮。
richtexteditor:输出一个富文本编辑器。
访问Action的三种方式
通过method属性来访问Action属性中的方法
一个Acton的java类中可能会写很多的方法,当项目继承ActionSupport的时候如果没有写method属性的时候,默认的是会执行execute 方法当我们需要使用其他的方法的时候,就需要调用特定的方法。这时候就需要使用method 属性,及 method=“方法名”
<action name="helloWorld" class="com.yxs.web.helloAction" method="test1">
<!--这时候这个action就会去执行test1方法 -->
好处:一眼就可以认出Action执行的是那一个方法
坏处:一个Action只能使用一个 方法,每一个方法就要一个Action
使用动态方法调用Action
好处:可以动态的调用大大的减少了Action的数量。
第一步:开启动态方法调用
<constant name="struts.enable.DynamicMethodInvocation" value="true"></constant>
第二步:开启白名单
这个是2.5版本以后才需要开启的,之前的不用开启白名单
这个是设置在标签内的
<global-allowed-methods>regex:.*</global-allowed-methods>
在action类中编写方法
public String test(){
return "input";
}
struts.xml文件
<!-- 开启动态调用 -->
<constant name="struts.enable.DynamicMethodInvocation" value="true"></constant>
<!-- 一个package中可以有多个action,其中package的name属性是自己取的一个任意名称,extends属性
继承默认的struts-default -->
<package name="web" extends="struts-default" namespace="/*">
<action name="test" class="com.yxs.web.helloAction">
<result name="input">/testtwo.jsp</result>
</action>
</package>
第一个页面
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<a href="test!test.action">点击</a>
</body>
</html>
第二个页面
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>方法执行成功</h1>
</body>
</html>
结果是跳转显示方法执行成功
动态的调用的意义在于编写一个Action不需要指定它执行的是那一个方法,只需要在页面要是使用相应方法的时候
填入 Action的名字+!+方法名.action
例如
<a href="test!test.action">点击</a>
通配符调用
通配符用星号(*)表示,用于配置0个或多个字符串,在配置Action时,可以在action元素的name属性中使用星号来匹配任意的字符串
java方法
public String add(){
return "input";
}
struts,xml配置文件
<action name="*test" class="com.yxs.web.helloAction" method="{1}">
<result name="input">/testtwo.jsp</result>
</action>
<!--* 是用来统配方法名的,{1}占位,就是第一个*位置的值 -->
页面执行方法
<a href="addtest.action">点击</a>
这里的ad就是方法名,test是action的名字
这里有一个问题是当我们方法命名的时候出现了test,例如方法名叫做addtest,那上面就会变成href="addtesttest.action"这样会报错,因为一但匹配到第一个test就不会往下面执行了。这时候就需要在两者之间加下线就可以了。
<action name="*_test" class="com.yxs.web.helloAction" method="{1}">
<result name="input">/testtwo.jsp</result>
</action>
<!--* 是用来统配方法名的,{1}占位,就是第一个*位置的值 -->
<a href="addtest_test.action">点击</a>
全局配置
我们可以在struts.xml文件中设置一个全局配置的包
<package name="web" extends="struts-default" namespace="/*">
</package>
在里面来编写一些我们需要应用到所有的包中的配置,接下来让其他包继承他即可
<package name="webb" extends="web">
</package>
Result的配置
action标签中的result标签是接受返回的字符串跳转页面,如果结果类型没声明,(result标签中没有指明type属性)默认是 dispatcher (请求转发)
比较重要的几个类型:
- dispatcher —— 请求转发到一个页面 (默认),不可以用这种方式转发到一个action
- chain —— 一个action请求转发至另一个 action
- redirect —— 响应重定向到一个页面
- redirectAction —— 一个action响应重定向至另一个 action
- stream —— 文件下载
重定向是服务器重定向,转发的时候还需要返回来给浏览器,浏览器参与转发过程,所以相对于服务器转发较为不安全。
获取表单数据
1.属性封装
这个用法就是在Action类中定义一些类属性,属性的名字要和将要接收的表单的数据的名字相同才可以接受成功。
以前使用servlet来获取表单数据的时候,获得的数据的类型都是字符串类型,现在框架已经帮我们处理好了,我们的属性是什么类型的数据定义的时候就填写什么类型的数据,获取过来的时候就是同一种类型的数据,不需要静心转换了。
需要注意的是但定义的属性值是public类型的时候可以不为属性提供get和set方法也能获取到数据,但是当数据定义为private的时候就需要为每一个属性提供get和set方法,数据才可以正常的获取。
2.模型驱动封装
- 让Action类要实现一个指定接口ModelDriven
- 实例化模型对象(就是要new出来javaBean)
- 重写getModel方法将实例化的模型返回。
public class LoginAction extends ActionSupport implements ModelDriven<User>{
private User user = new User();
@Override
public User getModel() {
return user;
}
对于模型驱动它与属性驱动对比,在实际开发中使用比较多,模型驱动缺点,它只能对 一个模型数据进行封装。
使用该方式的时候表单的数据的名字要严格区分大小的和实体类的属性的名字相同,属性封装和模型驱动封装一起用的时候,系统优先使用模型驱动封装,属性封装这时候是取不到值的。
3.表达式封装
使用表达式封装和使用属性封装有些类似,就是在Action中定义一个属性,类型就是实体类,然后给这个实体类属性提供set和get方法。
使用该方法的时候前端页面获取数据的时候就要采用 格式
对象名.属性名
OGNL表达式
OGNL的全称是Object Graph Navigation Language(对象图导航语言),它是一种强大的表达式语言,让你通过简单一致的表达式语法来读取和设置Java对象的属性值,调用对象的方法,遍历整个对象的结构图,实现字段类型转换等功能。
拦截器
简介
- Struts2拦截器是在访问某个Action或Action的某个方法,字段之前或之后实施拦截,并且Struts2拦截器是可插拔的,拦截器是AOP的一种实现.
- 拦截器栈(Interceptor Stack)。Struts2拦截器栈就是将拦截器按一定的顺序联结成一条链。在访问被拦截的方法或字段时,Struts2拦截器链中的拦截器就会按其之前定义的顺序被调用。
- 拦截器相当于一个地铁的进出入口检票机,请求进去的时候需要通过拦截器,出来的时候也需要通过拦截器。
- 当用户发起业务请求,服务器先通过web.xml配置,找到Struts2核心控制器,进入拦截器,通过后再由核心控制器找到对应的action,在到Action的控制类,接着返回结果到Result,然后经过过滤器出去
自定义拦截器
自定义一个拦截器需要三步:
1 自定义一个实现Interceptor接口(或者继承自AbstractInterceptor)的类。
public class MyInterceptor extends AbstractInterceptor{
@Override
public String intercept(ActionInvocation arg0) throws Exception {
ActionContext ac = arg0.getInvocationContext();
// 获取session中的user类,若没有则user==null
Object user = ac.getSession().get("loginuser");
if( user != null){
return arg0.invoke();
}
return "LOGIN";
}
}
2 在strutx.xml中注册上一步中定义的拦截器。
<interceptors>
<interceptor name="checkLogin" class="com.xyl.util.MyInterceptor"> </interceptor>
</interceptors>
3 在需要使用的Action中引用上述定义的拦截器,为了方便也可将拦截器定义为默认的拦截器,这样在不加特殊声明的情况下所有的Action都被这个拦截器拦截。
<action name="view" class="com.xyl.web.ViewAction">
<result name="success">/list.jsp</result>
<result name="LOGIN">/login.jsp</result>
<interceptor-ref name="defaultStack"></interceptor-ref>默认拦截器,必须要不然会失效
<interceptor-ref name="checkLogin"></interceptor-ref>引用自定义的拦截器
</action>
拦截器栈
拦截器栈(Interceptor Stack)。Struts2拦截器栈就是将拦截器按一定的顺序连接成一条链。在访问被拦截的方法或字段时,Struts2拦截器链中的拦截器就会按其之前定义的顺序被调用。
<interceptors>
<interceptor-stack name="AllInterceptor">
<interceptor-ref name="FirstInterceptor"></interceptor-ref>
<interceptor-ref name="SecondInterceptor"></interceptor-ref>
<interceptor-ref name="defaultStack"></interceptor-ref>
</interceptor-stack>
</interceptors>
这就是一个拦截器栈,拦截器依次装入栈中,当action要使用拦截器的时候就直接引用拦截器栈就可以了。
但是需要注意的是一旦引用栈,则掌中的所有拦截器都会作用。
在全局变量中配置拦截器需要配置,使一些不需要拦截的action 被通过。
<action name="LoginAction" class="Action.LoginAction">
<interceptor-ref name="AllInterceptor"></interceptor-ref>引用拦截器栈
<result name="success">success.jsp</result>
</action>
记住 拦截器和拦截器栈的初始化都是在标签中的。
配置那些方法可以通行
<param name="excludeMethods">login</param>
设置那些方法拦截
<param name="includeMethods">add*,delete*</param>
全貌
<interceptors>
<interceptor-stack name="AllInterceptor">
<interceptor-ref name="FirstInterceptor">
<param name="excludeMethods">login</param>
<param name="includeMethods">add*,delete*</param>
</interceptor-ref>
<interceptor-ref name="SecondInterceptor">
<param name="excludeMethods">login</param>
<param name="includeMethods">add*,delete*</param>
</interceptor-ref>
<interceptor-ref name="defaultStack"></interceptor-ref>
</interceptor-stack>
</interceptors>
文件上传
必须有commons-fileupload-1.3.1.jar包. 在开发Struts2框架必备的13个jar包中,包含了此jar包.
这是一个文件上传的插件有了这个插件文件上传就会变得简单。
文件的上传大小是有限制的,最大的不超过2M,如果需要上传大文件就需要在struts.xml文件中进行配置
利用struts2想要设置或者限制上传文件的大小,可以在struts.xml配置文件里面进行如下配置:
<constant name="struts.multipart.maxSize" value="10000000" />
上面这句话的意思是设置文件上传大小,最大不超过9.8M。计算方式如下:
设置上次文件的大小最大为10000000字节也就是(10000000/1024/1024)=9.5MB
单个文件上传
文件上传要注意有三个属性,一个是文件,名字必须和form表单的名字一致才可以,第二个是文件类型,文件名+ContentType(后缀)组成,这是固定的,文件名称也是文件名+FileName(后缀),这也是固定的。
接着必须为这三个属性添加get和set方法。
这里介绍有两种上传方式,一种是通过IO流进行文件上传,一种是简单的上传方式,使用的工具上传
public class UploadAction extends ActionSupport{
//文件
private File upload; //属性名字必须和input的name属性的名字一致
//文件类型
private String uploadContentTyep;//input标签的name属性名+ContentType(后缀)
//文件名称
private String uploadFileName;//input标签的name属性名+FileName(后缀)
public File getUpload() {
return upload;
}
public void setUpload(File upload) {
this.upload = upload;
}
public String getUploadContentTyep() {
return uploadContentTyep;
}
public void setUploadContentTyep(String uploadContentTyep) {
this.uploadContentTyep = uploadContentTyep;
}
public String getUploadFileName() {
return uploadFileName;
}
public void setUploadFileName(String uploadFileName) {
this.uploadFileName = uploadFileName;
}
/**
* 通过IO流上传单个文件
* @return
* @throws IOException
*/
public String uploadByIo() throws IOException{
//路径
String path ="E:/upload";
FileInputStream fis = null;
FileOutputStream fos =null;
try {
//通过输入流读取文件
fis = new FileInputStream(upload);
//获取文件后缀
String extension = new FilenameUtils().getExtension(uploadFileName);
//重命名文件名称
String newFileName = UUID.randomUUID().toString().replace("-","")+"."+extension;
//通过输出流写文件
fos = new FileOutputStream(path+"/"+newFileName);
try {
//通过工具类保存文件
IOUtils.copy(fis, fos);
} catch (IOException e) {
e.printStackTrace();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}finally{
if(fis != null){
fis.close();
}
if(fos != null){
fos.close();
}
}
return SUCCESS;
}
/**
* 普通方法实现单文件上传
*/
public String uploadSimple(){
//路径
String path ="E:/upload";
//获取文件后缀
String extension = new FilenameUtils().getExtension(uploadFileName);
//重命名文件名称
String newFileName = UUID.randomUUID().toString().replace("-","")+"."+extension;
//构建真实路径
String realPath = path+File.separator+newFileName;//File.separator根据系统判断类型加入分隔符
//通过工具类上传
try {
FileUtils.copyFile(upload,new File(realPath));
} catch (IOException e) {
e.printStackTrace();
}
return SUCCESS;
}
}
Struts.xml
<package name="upload" extends="struts-default" namespace="/*">
<action name="uploadByIo" class="com.yxs.web.UploadAction" method="uploadByIo">
<result name="success">/success.jsp</result>
</action>
<action name="uploadSimple" class="com.yxs.web.UploadAction" method="uploadSimple">
<result name="success">/success.jsp</result>
</action>
</package>
前端jsp
<h1>文件上传</h1>
<form action="uploads" method="post" enctype="multipart/form-data">
<div>
<label>选择文件:</label><hr>
<input type="file" name="upload">
</div>
<div>
<input type="submit" value="提交">
</div>
</form>
多文件上传
多文件上传就需要将三个属性字符串类型改为字符数组的类型,生成对应的set和get方法
多文件上传和单文件上传的区别就是在于多文件上传一次性上传多个文件,采用数组储存,采用循环方式来读取文件上传文件。除了这两点,单文件上传和多文件上传的方法是一样的。
public class UploadsAction extends ActionSupport{
//文件
private File[] upload; //属性名字必须和input的name属性的名字一致
//文件类型
private String[] uploadContentTyep;//input标签的name属性名+ContentType(后缀)
//文件名称
private String[] uploadFileName;//input标签的name属性名+FileName(后缀)
public File[] getUpload() {
return upload;
}
public void setUpload(File[] upload) {
this.upload = upload;
}
public String[] getUploadContentTyep() {
return uploadContentTyep;
}
public void setUploadContentTyep(String[] uploadContentTyep) {
this.uploadContentTyep = uploadContentTyep;
}
public String[] getUploadFileName() {
return uploadFileName;
}
public void setUploadFileName(String[] uploadFileName) {
this.uploadFileName = uploadFileName;
}
/**
* 普通方法实现多文件上传
*/
public String uploadSimple(){
//路径
String path ="E:/upload";
for(int i=0;i<upload.length;i++){
//获取文件后缀
String extension = new FilenameUtils().getExtension(uploadFileName[i]);
//重命名文件名称
String newFileName = UUID.randomUUID().toString().replace("-","")+"."+extension;
//构建真实路径
String realPath = path+File.separator+newFileName;//File.separator根据系统判断类型加入分隔符
//通过工具类上传
try {
FileUtils.copyFile(upload[i],new File(realPath));
} catch (IOException e) {
e.printStackTrace();
}
}
return SUCCESS;
}
}
struts.xml
<package name="upload" extends="struts-default" namespace="/*">
<action name="uploads" class="com.yxs.web.UploadsAction" method="uploadSimple">
<result name="success">/success.jsp</result>
</action>
</package>
JSP
<h1>文件上传</h1>
<form action="uploads" method="post" enctype="multipart/form-data">
<div>
<label>选择文件:</label><hr>
<input type="file" name="upload">
</div>
<div>
<input type="file" name="upload">
</div>
<div>
<input type="submit" value="提交">
</div>
</form>
文件下载
java
package com.yxs.web;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import com.opensymphony.xwork2.ActionSupport;
public class DownloadAction extends ActionSupport{
//文件输入流
private InputStream inputStream;
//文件名
private String fileName;//与<a>标签中的属性名一致
public InputStream getInputStream() {
return inputStream;
}
public void setInputStream(InputStream inputStream) {
this.inputStream = inputStream;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
/**
*下载文件
* @return
*/
public String download(){
//指定文件下载的地址(这里没有服务器就用本地磁盘代替)
String path ="E:/upload/"+fileName;
try {
//指定编码格式
fileName = URLEncoder.encode(fileName, "utf-8");
//指定编码格式是因为有些浏览器下载文件的编码是不同的,有可能下载下来以后文件名就改变了
try {
//通过输入流读取文件并下载
inputStream = new BufferedInputStream(new FileInputStream(path));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return SUCCESS;
}
}
struts.xml
<package name="default" extends="struts-default">
<action name="download" class="com.yxs.web.DownloadAction" method="download">
<result name="success" type="stream">
<!-- 设置下载文件的类型 contentType 指定下载文件的文件类型 —— application/octet-stream 表示无限制-->
<param name="contentType">application/octet-stream</param>
<!--inputName 流对象名 —— 比如这里写inputStream,它就会自动去找Action中的getInputStream方法。 -->
<param name="inputName">inputStream</param>
<!-- contentDisposition 使用经过转码的文件名作为下载文件名 —— 默认格式是attachment;
filename="${filename}",将调用该Action中的getFilename方法。
bufferSize 下载文件的缓冲大小 -->
<param name="contentDisposition">attachment;filename="${fileName}"</param>
<param name="bufferSize">4096</param>
</result>
</action>
</package>
jsp
<h1>文件下载</h1>
<a href="download?fileName=hello.jpg">英文名称下载</a>
<a href="download?fileName=你好.jpg">中文名称下载</a>
/指定编码格式
fileName = URLEncoder.encode(fileName, “utf-8”);
//指定编码格式是因为有些浏览器下载文件的编码是不同的,有可能下载下来以后文件名就改变了
try {
//通过输入流读取文件并下载
inputStream = new BufferedInputStream(new FileInputStream(path));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return SUCCESS;
}
}
struts.xml
~~~~xml
<package name="default" extends="struts-default">
<action name="download" class="com.yxs.web.DownloadAction" method="download">
<result name="success" type="stream">
<!-- 设置下载文件的类型 contentType 指定下载文件的文件类型 —— application/octet-stream 表示无限制-->
<param name="contentType">application/octet-stream</param>
<!--inputName 流对象名 —— 比如这里写inputStream,它就会自动去找Action中的getInputStream方法。 -->
<param name="inputName">inputStream</param>
<!-- contentDisposition 使用经过转码的文件名作为下载文件名 —— 默认格式是attachment;
filename="${filename}",将调用该Action中的getFilename方法。
bufferSize 下载文件的缓冲大小 -->
<param name="contentDisposition">attachment;filename="${fileName}"</param>
<param name="bufferSize">4096</param>
</result>
</action>
</package>
jsp
<h1>文件下载</h1>
<a href="download?fileName=hello.jpg">英文名称下载</a>
<a href="download?fileName=你好.jpg">中文名称下载</a>
下载的时候应该保证服务器里(这里是本地路径的地址)有与之名字对应的文件。