Struts2
Struts2
Struts2由传统的Struts1、WebWork两个经典的MVC框架发展起来,无论是从Struts2设计角度来看,还是从Struts2在实际项目中的易用性来看,Struts2都是一个非常优秀的MVC框架。与传统的Struts1相比,Struts2允许使用普通的、传统的Java对象作为Action;Action的execute()方法不再与Servlet API耦合,因而更易测试;支持更多的视图技术;基于AOP思想的拦截器机制,提供了极好的可扩展性;更强大、更易用的输入校验功能;整合的Ajax支持等,这些都是Struts2的巨大吸引力。
Struts2应用的开发步骤
用户登录项目目录
1、在web.xml文件中定义核心Filter来拦截用户请求。
由于web应用是基于请求和响应架构的应用,都需要在web.xml文件中配置框架的核心Servlet或Filter,这样才可以让框架介入Web应用中。
<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>
2、定义处理请求的Action类,UserAction.java
继承ActionSupport,重写execute方法;Action类封装HTTP请求参数,类中应该包含与请求参数对应的实例变量,并提供对应的setter和getter方法;处理请求的方法中应返回字符串。
以下代码是验证用户名和密码是否输入正确
public class UserAction extends ActionSupport {
private String username;
private String userpass;
public void setUsername(String username) {
this.username = username;
}
public void setUserpass(String userpass) {
this.userpass = userpass;
}
@Override
public String execute() throws Exception {
HttpServletRequest request = ServletActionContext.getRequest();
HttpSession session = request.getSession();
session.setAttribute("name", username);
if ("ghq".equals(username)&&"ghq".equals(userpass)) {
return "Suc";
}
return "loginFail";
}
}
3、配置Action,struts.xml
配置Action就是指定哪个请求对应用哪个Action进行处理,从而让核心控制器根据该配置来创建合适的Action实例,并调用该Action的业务控制方法。
配置处理结果和物理视图资源之间的对应关系
当Action处理用户请求结束后,通常会返回一个处理结果(字符串),可以认为该名称就是逻辑视图名,这个逻辑视图名需要和指定物理视图资源关联才有价值。还需要配置处理结果之间的对应关系。
<?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="userlog" extends="struts-default">
<action name="UserAction" class="com.ghq.action.UserAction">
<result name="Suc">/wel.jsp</result>
<result name="loginFail">/loginfail.jsp</result>
</action>
</package>
</struts>
Struts2流程
图中StrutsPrepareAndExecuteFilter和xxxAction共同构成了Struts2的控制器。通常称StrutsPrepareAndExecuteFilter为核心控制器,xxxAction为业务控制器。
Action和Servlet API
为了能直接访问Servlet API,Struts2提供了ServletActionContext工具类,这个类中包含了如下几个静态方法:
//取得Web应用的PageContext对象
static PageContext getPageContext();
//取得Web应用的HttpServletRequest对象
static HttpServletRequest getRequest();
//取得Web应用的HttpServletResponse对象
static HttpServletResponse getResponse();
//取得Web应用的ServletContext对象
static ServletContext getServletContext();
配置Action详解
配置Action就是让Struts2知道哪个Action处理哪个请求,也就是完成用户请求和Action之间的关系。
1、包和命名空间
Struts2使用包来组织Action,Action定义放在包定义下完成,定义Action通过使用<package …/>下的<action …/>子元素来完成。每个包就是多个Action、多个拦截器、多个拦截器引用的集合。
<!-- 包名为user,继承struts-default -->
<package name="user" extends="struts-default">
每次定义一个package元素,都可以指定一个namespace属性,用于指定该包对应的命名空间。命名空间主要是为了处理同一个Web应用中包含同名Action的情形,同一个命名空间不能有同名的Action。
当包指定了命名空间后,该包下所有的Action处理的URL应该是命名空间+Action名。
2、Action的基本配置
<action name="UserAction" class="com.ghq.action.UserAction">
<result name="Suc">/wel.jsp</result>
<result name="loginFail">/loginfail.jsp</result>
</action>
动态方法调用
<constant name="struts.enable.DynamicMethodInvocation" value="true"></constant>
<package name="user" extends="struts-default">
<action name="UserAction" class="com.ghq.action.UserAction" >
<result name="addUser">/addUser.jsp</result>
<result name="getAllUser">/main.jsp</result>
</action>
</package>
经过上述配置以后,请求的url为:Http://localhost:8080/login/UserAction!getAllUser
使用method属性及使用通配符
将一个Action处理类定义成多个逻辑Action。在配置<aciton …/>元素时,可以为它指定method属性,则可以让Action调用指定方法。
<package name="user" extends="struts-default">
<action name="getAllUser" class="com.ghq.action.UserAction" method="getAllUser">
<result name="getAllUser">/main.jsp</result>
</action>
<action name="addUser" class="com.ghq.action.UserAction" method="addUser">
<result name="addUser">/addUser.jsp</result>
</action>
</package>
上面的配置文件中,连个action定义的绝大部分相同,定义冗余,Struts2有一种简化的形式:使用通配符的方式
<package name="user" extends="struts-default">
<action name="UserAction_*" class="com.ghq.action.UserAction" method="{1}">
<result name="addUser">/addUser.jsp</result>
<result name="getAllUser">/main.jsp</result>
</action>
</package>
3、配置处理结果
Action处理完用户的请求后,将返回一个普通字符串,整个普通字符串就是一个逻辑视图名。Struts2通过配置逻辑视图名和物理视图之间的映射关系,一旦收到Action返回的逻辑视图名,系统会把相应的物理视图呈现给用户。
<result name="addUser">/addUser.jsp</result>
全局结果
<global-results>
<result name="login">/login.jsp</result>
</global-results>
该<result …/>元素配置了一个全局结果,全局结果将对所有的Action都有效
Struts2控制文件上传
文件上传的流程
1、导入commons-fileupload.jar,common-io.jar包
2、表单中设置entype = ”multipart/form-data”
<form action=”” method="post" enctype="multipart/form-data">
<input type="file" name="userphoto">
<input type="submit" value="注册">
</form>
3、编写Action类
public class UserAction extends ActionSupport {
private String username;
private String userpass;
//用于封装上传的对象,userphoto必须和文件域的名字相同 ,不管上传的文件类型是什么,userphoto扩展名都是tmp
private File userphoto;
//封装被上传的文件的名字,userphoto必须和文件域的名字相同,FileName区分大小写
private String userphotoFileName;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getUserpass() {
return userpass;
}
public void setUserpass(String userpass) {
this.userpass = userpass;
}
public File getUserphoto() {
return userphoto;
}
public void setUserphoto(File userphoto) {
this.userphoto = userphoto;
}
public String getUserphotoFileName() {
return userphotoFileName;
}
public void setUserphotoFileName(String userphotoFileName) {
this.userphotoFileName = userphotoFileName;
}
public String regUI() throws Exception{
return "regUI";
}
public String reg() throws Exception {
//获取目标文件所在目录的绝对路径
String filePath = ServletActionContext.getServletContext().getRealPath("/images");
//目标文件的名字 UUID和扩展名组成
String fileName = UUID.randomUUID().toString() + userphotoFileName.substring(userphotoFileName.lastIndexOf("."));
//定义目标文件的对象,即要在服务器上出现的文件,要考虑到目标文件不能重复。
File targetFile = new File(filePath,fileName);
//获取上传文件的输入流
FileInputStream fin = new FileInputStream(userphoto);
//获取目标文件的输出流
FileOutputStream fout = new FileOutputStream(targetFile);
byte[] bs = new byte[1024];
int count = 0;
//上传文件
while ((count = fin.read(bs))!= -1) {
fout.write(bs,0,count);
}
//关闭流
if (fout != null) {
fout.close();
}
if (fin != null) {
fin.close();
}
return "regSuc";
}
}
注:使用common-io.jar包中的FileUtils类可以简化文件上传。
public String reg() throws Exception {
String filePath = ServletActionContext.getServletContext().getRealPath("/images");
String fileName = UUID.randomUUID().toString() + userphotoFileName.substring(userphotoFileName.lastIndexOf("."));
File targetFile = new File(filePath,fileName);
FileUtils.copyFile(userphoto,targetFile);
return "regSuc";
}
4、配置sturts.xml文件
<package name="user" extends="struts-default">
<global-results>
<!-- 文件上传失败转发到reg页面 -->
<result name="input">/reg.jsp</result>
</global-results>
<action name="UserAction_*" class="com.ghq.action.UserAction" method="{1}">
<result name="regSuc">/regSuc.jsp</result>
<result name="regUI">/reg.jsp</result>
</action>
</package>
拦截器实现文件过滤
Struts2提供了一个文件上传的拦截器,通过配置拦截器可以轻松地实现文件过滤。在<action>元素中添加如下代码
<interceptor-ref name="fileUpload">
<param name="allowedExtensions">jpg,gif,jpeg,bmp,png</param>
<param name="maximumSize">5210</param>
</interceptor-ref>
<!— 配置系统默认的拦截器 -->
<interceptor-ref name="defaultStack"></interceptor-ref>
参数含义:
allowedExtensions:指定允许上传的文件类型,多个类型之前逗号隔开
maximumSize:指定允许上传的文件大小,单位是字节
可以在jsp页面中添加Struts2标签来输出错误信息,但是直接调用含有Struts2标签的jsp文件时,会发生如下异常
<s:fielderror/>
The Struts dispatcher cannot be found. This is usually caused by using Struts tags without the associated filter. Struts tags are only usable when the request has passed through its servlet filter, which initializes the Struts dispatcher needed for this tag.
解决办法:
利用逻辑视图和物理视图之间的对应关系,使用逻辑视图来调用物理视图。
例如上面的UserAction中的regUI方法就是实现了此功能,地址栏输入http://127.0.0.1:8080/struts2_FileUpLoad/UserAction_regUI.action,调用regUI方法,返回"regUI",在struts.xml文件中找到对应的物理视图映射reg.jsp来加载页面。
错误信息正常显示的是英文提示,用户体检不佳
可以使用国际化信息替换。编写upload.properties文件
在struts.xml文件中配置以加载此文件
<constant name="struts.custom.i18n.resources" value="upload"/>
配置后显示样式为
Struts文件下载
文件下载开发流程:
1、编写Action类
public class DownLoadAction {
private String filename;
private String finalName;
public String getFilename() {
return filename;
}
public void setFilename(String filename) {
this.filename = filename;
}
public String getFinalName() {
return finalName;
}
public InputStream getTargetFile() throws Exception{
finalName = UUID.randomUUID().toString()+filename.substring(filename.lastIndexOf('.'));
//获取ServletContext的对象
ServletContext application = ServletActionContext.getServletContext();
//ServletContext提供getResourceAsStream()方法,返回指定文件对应的输入流
InputStream fin = application.getResourceAsStream("/images/"+filename);
return fin;
}
}
2、配置Action
<struts>
<constant name="struts.enable.DynamicMethodInvocation" value="false" />
<constant name="struts.devMode" value="true" />
<constant name="struts.action.extension" value="action"/>
<package name="default" namespace="/" extends="struts-default">
<action name="DownLoadAction_*" class="com.ghq.action.DownLoadAction" method="{1}">
<result type="stream">
<param name="inputName">targetFile</param>
<!-- attachment用于指定是下载文件,而不是解析文件
filename为了解决被下载的文件名是中文,下载的时候出现文件名乱码,所以指定下载的文件名 -->
<param name="contentDisposition">attachment;filename=${finalName}</param>
</result>
</action>
</package>
</struts>
3、开发jsp页面
<a href="<%=path %>/DownLoadAction.action?filename=JavaEE项目开发实践.jpg">下载</a>
Struts2拦截器
此程序为拦截所有请求,判断用户是否登录,登录后才能访问WEB-INF下的页面,否则转到登录页面
开发流程:
1、开发拦截器类 MyInterceptor.java
public class MyInterceptor extends AbstractInterceptor {
//实现拦截的方法
@Override
public String intercept(ActionInvocation invocation) throws Exception {
//在struts2框架中使用ActionContext模拟HttpServletRequest
ActionContext request = invocation.getInvocationContext();
//获取session
Map<String, Object> session = request.getSession();
String username = (String)session.get("name");
if (username != null) {
//放行请求,即将控制器交给下一个拦截器或者直接交给目标资源
return invocation.invoke();
}else{
return "login";
}
}
}
2、配置拦截器 struts.xml
<interceptors>
<!-- 配置拦截器,自定义的拦截器不能直接拦截页面,但是可以拦截Action类的请求,必须是显示为Action使用拦截器才拦截 -->
<!-- 定义拦截器-->
<interceptor name="MyInterceptor" class="com.ghq.Action.MyInterceptor"/>
<!-- 定义拦截器栈 -->
<interceptor-stack name="myStack">
<interceptor-ref name="MyInterception"/>
<interceptor-ref name="defaultStack"/>
</interceptor-stack>
<interceptors>
<!-- 拦截器的配置中不能配置逻辑视图和物理视图的映射,所以配置为全局结果 -->
<global-results>
<result name="login">/login.jsp</result>
</global-results>
使用拦截器
<!-- action元素不写class认为class属性值就是com.opensymphony.xwork2.ActionSupport -->
<action name="*">
<result>/WEB-INF/jsp/{1}.jsp</result>
<!-- 为Action应用拦截器,指定拦截器拦截其所在的Action请求 -->
<interceptor-ref name="myStack"/>
</action>
Action配置
<action name="UserAction" class="com.ghq.Action.UserAction">
<result name="loginSuc">/Suc.jsp</result>
<result name="loginfail">/fail.jsp</result>
</action>
整体的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="user" extends="struts-default">
<interceptors>
<interceptor name="MyInterceptor" class="com.ghq.Action.MyInterceptor" />
<interceptor-stack name="myStack">
<interceptor-ref name="MyInterceptor"/>
<interceptor-ref name="defaultStack"/>
</interceptor-stack>
</interceptors>
<global-results>
<result name="login">/index.jsp</result>
</global-results>
<action name="UserAction" class="com.ghq.Action.UserAction">
<result name="loginSuc">/Suc.jsp</result>
<result name="loginfail">/fail.jsp</result>
</action>
<action name="*">
<result>/WEB-INF/jsp/{1}.jsp</result>
<interceptor-ref name="myStack"/>
</action>
</package>
</struts>
3、配置web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<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、编写UserAction.java
package com.ghq.Action;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionSupport;
public class UserAction extends ActionSupport {
private String username;
private String userpass;
public void setUsername(String username) {
this.username = username;
}
public void setUserpass(String userpass) {
this.userpass = userpass;
}
@Override
public String execute() throws Exception {
HttpServletRequest request = ServletActionContext.getRequest();
HttpSession session = request.getSession();
if (username.equals("ghq") && userpass.equals("ghq")) {
session.setAttribute("name", username);
return "loginSuc";
}
return "loginfail";
}
}
5、jsp页面展示
index.jsp
<body>
<form action="<%=path %>/UserAction" method="post">
用户名:<input type="text" name="username" />
密码:<input type="password" name="userpass" />
<input type="submit" value="登录">
</form>
</body>
Suc.jsp
<body>
欢迎,${name }
</body>
fail.jsp
<body>
用户名或密码错误,请<a href="index.jsp">重新登录</a>
</body>
WEB-INF/jsp/index.jsp
<body>
这个是内部页面
</body>
运行程序
地址栏第一次输入http://127.0.0.1:8080/struts2-Interceptor/index展现的是登录页面
输入用户名和密码后
地址栏再次输入http://127.0.0.1:8080/struts2-Interceptor/index 后即可访问内部的页面