一个基于请求的轻量级的mvc框架
struts2的helloworld案例
- 新建web项目
- 导入jar包
- 编辑web.xml文件–配置struts2的前段控制器(分发器)
在struts2中所有的业务方法都是public的,返回值都为String类型,所有的业务方法都没有参数,方法名可以自定义,默认为execute
struts2配置
web.xml
前端控制器
struts2框架开始工作的入口,接管请求
struts2.xml该配置文件名称固定,并且只能放于src下
package(重点):
name自定义,但不能重复
namespace命名空间和url直接相关
如:/请求为/hello.action /user 请求为/user/hello.action
extends 继承,必须直接或者间接继承struts-default
action:
name为请求名 不加后缀
class 处理类的完全限定名称 包名+类名,如果不配置,有默认类来处理(ActionSupport)
method 绑定方法名,指定处理请求的方法
<result name=""></result>结果集配置
name 结果集名称和处理方法的返回值匹配,默认为success
Struts2提供了5个返回结果:
Action.SUCCESS 执行成功,跳转到下一个试图
Actuon.NONE 执行成功,不需要视图显示
Actuon.ERROR 执行失败,显示失败页面
Actuon.INPUT 要执行该Action需要更多的输入条件
Actuon.LOGIN 需要登陆后才能执行
type 指定响应结果类型
dispatchr 转发默认
redirect 重定向
redirectAction 重定向到Action
result的值为跳转页面,如果不加/为namespace路径,建议绝对路径
在package中可以配置全局的结果集
<global-results>
<result></result>
</global-results>
constant(常量配置):
name:
struts.action.extension 扩展名
struts.i18n.encoding 乱码
struts.devMode 开发模式
value:
对应配置值
include:
引入其它配置文件
file="struts/configuration/system.xml"
配置优化
随着业务增加action配置文件会急剧增加,导致配置文件的膨胀问题。
- 动态方法调用来解决
开启动态方法调用
<constant name="struts.enable.DynamicMethodInvocation"value="true" />
配置action,一个处理类只需配置一次,不需要配置处理方法。
调用处理方法:ActionName!methodName.action
注意:不同处理方法的返回值是否一致。会配置多个结果集。不推荐使用,不安全。
2. 通配符配置
- 注解
导入jar包**struts2-convention-plugin
**
在处理类上填写相关注解
struts2的执行流程
- 发起请求
- 服务器接受请求并交给struts2的前端控制器web.xml(和filter-mapping url-pattern匹配)
- 根据请求的url查看strus.xml中的namespace+action是否存在,不存在404
- 存在,执行action所对行类的对应方法
- 根据方法的执行结果到action的结果集进行匹配
- 响应结果
数据处理
属性驱动
- 使用struts2获取表单域名称和action处理类的属性名称一致,并且提供属性的set方法,那么在action处理类中即可获得表单数据,称为属性驱动。
驱动类
页面
2. 如果数据需要显示到页面上,那么数据可以做为处理类的属性,处理方法后该属性有值,并且有该属性的get方法。页面上可以通过el表达式获取
3.
对象驱动(重点)
在action的处理类中,属性以对象方式存在,该属性只需声明即可。需要保证该属性对象有无参构造函数(以反射创建对象),并且提供set,get方法。在表单域中的表单域名名称,以属性对象.属性对象的属性来命名
user类
action处理类
页面
模型驱动
在对象驱动中,页面的表单域名称复杂,通过模型驱动可以解决这个问题。
模型驱动需要处理类实现ModelDriven<对象>接口,并且主动将对象创建好
处理类
页面
action处理类的创建方式
-
通过实现Action接口(使用较少)
实现Action接口的方式,可以直接使用Action提供的常量。
必须重写默认处理方法(execute)
处理类 -
通过继承ActionSupport来创建Action的处理类,struts2推荐方式
实现了Action接口。
并且提供了其他功能如:数据校验,国际化。 -
无侵入性实现
灵活可以更换框架
ThreadLocal和ActionContext
ThreadLocal是一个容器,存放在该容器中的数据是线程安全的
final ThreadLocal<Integer> th = new ThreadLocal<Integer>();
th.set(23);
new Thread() {
public void run() {
th.set(12);
System.out.println(th.get());
}
}.start();
System.out.println(th.get());
}
ActionContext是struts2的上下文对象。本质是容器,一个map结构对象。线程安全的,贯穿整个Action的执行生命周期,每次接受请求都会重新建一个ActionContext对象,将servlet中的数据存入容器中,实现了struts2和servlet解耦。
ActionContext包含六个存储(request
放HttpservletRequest域中的数据,session
放HttpSession域中的事件,application
放ServletContest域中事件,parameters
放请求参数,attr
放request,session,application中数据,ValueStack
放业务相关属性)
ognl(object navigation language)
在struts2中ognl完成数据设置,类型转换两个功能
map上下文对象不常用数据存放地,userroot对象常用数据,不常用加#获取,如Ognl.getvalue("name",map,user); Ognl.getvalue("#name",map,user);
Ognl.getvalue("表达式",map,user);
struts2中通过ognl来设值和取值。ActionContest作为上下文对象,ValueStack作为ognl的root对象
在struts2中使用ognl表达式获取数据需要使用struts2的标签库,使用struts2的标签库需要注意页面一定是通过过滤器后才能解析struts2的标签
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags"%>
<html>
<head>
<title>menu</title>
</head>
<body>
登录成功,欢迎${username}<br/> <%--el表达式--%>
<%--value填写ognl表达式--%>
<s:property value="username" /><br>
<s:property value="#application.address" /><br>
<s:property value="#aparameters.username" /><br>
</body>
</html>
类型转换
在struts2中对于常用的数据类型已经自动进行了类型转换工作。但对于自定义类型struts2没法转换。需要自定义转换器来实现转换类型。
类型转换实现
新建类型转换器类继承StrutsTypeConverter类
还要建立配置文件
类
public class d extends StrutsTypeConverter {
@Override
// maps action context上下文
// strings values,表单提交的字符串
// aClass toclass
public Object convertFromString(Map map, String[] strings, Class aClass) {
// 表单提交的数据在这个方法中被转换
// 返回值是目标对象,同时完成赋值
return null;
}
@Override
/*
*将指定类型转换为String
* 使用ognl表达式获取值时会调用该方法
* */
public String convertToString(Map map, Object o) {
return null;
}
}
文件
cn.sxt.vo.point=cn.sxt.converter.PointConverter
#point 对象 PointConverter 自定义转换器类
数据校验
数据校验分为2类,一类前段数据检验,一般js实现,一类是后端的数据校验。struts2提供了两种后端方法,一种编码实现,一种框架实现
硬编码
action需要继承ActionSupport类。在该类中提供了一个validate方法,只有方法执行通过后,才会执行业务方法
业务处理类
在validate方法中,添加了FieldError或ActionErrot那么方法不通过,并且返回结果为INPUT,所以需要配置INPUT处理结果集。在页面中添加struts2的标签库,并且添加错误标签。
Jsp页面:一定要加上<s:actionerror/>或者是<s:fielderror/>
<s:actionerror/>
<form action="register.action" method="post">
用户名:<input type="text" name="name"/><br>
密码:<input type="password" name="pwd"/><br>
年龄:<input type="text" name="age"/><br>
生日:<input type="text" name="birthday"/><br>
<input type="submit" value="登录"/>
</form>
public class RegisterAction extends ActionSupport{
private String name;
private String pwd;
private int age;
private Date birthday;
@Override
public String execute() throws Exception {
System.out.println("execute");
return Action.SUCCESS;
}
public String register(){
System.out.println("register");
return Action.SUCCESS;
}
public void validateRegister(){
System.out.println("validate age");
if(age>100||age<1){
this.addActionError("年龄不合法");
}
}
public void validate() {
System.out.println("validate");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
}
Struts.xml配置文件
<package name="default" extends="struts-default" namespace="/">
<action name="register" class="cn.sxt.action.RegisterAction" method="register">
<result>/index.jsp</result>
<result name="input">/register.jsp</result>
</action>
</package>
但是如果一个类中有多个业务方法,并且每个校验可能不一致,但所有的业务方法都会通过validate。导致功能不能实现。
如果需要为每个业务添加校验方法,验证方法名规则为validate+业务方法名(首字母大写)。这样执行顺序为validateXxx -->validate -->业务方法。这样validate填写的是公共校验
校验框架
在action所在包下,创建 + -validation.xml校验文件
创建注释可以在
<!--
XWork Validators DTD.
Used the following DOCTYPE.
<!DOCTYPE validators PUBLIC
"-//Apache Struts//XWork Validator 1.0.3//EN"
"http://struts.apache.org/dtds/xwork-validator-1.0.3.dtd">
-->
里找
<validators>
<!--field表示对哪一个表单域进行验证-->
<field name="">
<!--字段验证器,默认提供,根据-->
<field-validator type="">
<message>you must enter a name</message>
</field-validator>
</field>
</validators>
拦截器
struts2的所有功能都是由拦截器来实现。可以自定义拦截器来实现没有提供的功能。
struts2的默认拦截器都是定义在struts-default.xml
中,所以在使用struts2时定义的package要直接或间接继承struts-default
使用内置拦截器时需要在action配置中引用
```csharp
<action name="getPwd class=“”
<result name="success">/success.jsp</result>
引用拦截器
<interceptor-ref name=""></interceptor-ref>
</action>```
自定义拦截器
实现Interceptor接口或继承AbstractInterceptor类
定义。当引用自定义拦截器后,struts2的默认拦截器将不起作用,需要重新引用
实现Interceptor接口定义
package cn.cq.jszg.candidate;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.Interceptor;
public class MyInterceptor implements Interceptor {
@Override
public void destroy() {
// 销毁
}
@Override
public void init() {
// 初始化
}
@Override
public String intercept(ActionInvocation invocation) throws Exception {
// 拦截器的主体实现
/**
* 当拦截器方法被调用后需要通过invocation.invoke调用下一个拦截器,如果没有拦截器那么执行action中的业务方法
*/
System.out.println("自定义拦截器被执行了");
// 返回结果为结果集
return invocation.invoke();
}
}
在package标签中配置拦截器
<package name="default" namespace="/" extends="struts-default">
<!-- 拦截器的配置-->
<interceptors>
<!-- name自定义,在项目中唯一-->
<interceptor name="MyInterceptor" class="cn.cq.jszg.candidate.MyInterceptor"></interceptor>
<interceptor-ref name="defautstrack"></interceptor-ref>
</interceptors>
</package>
在使用的action中引用拦截器
<action name="save" class="" method="save">
<!-- 使用自定义拦截器,有多个会从上往下执行-->
<interceptor-ref name="MyInterceptor"></interceptor-ref>
</action>
拦截器栈
拦截器栈就是一组拦截器,放在一个配置中,方便引用
<!-- 定义拦截器栈,拦截器栈中可以引用另一个拦截器栈,可以引用多个拦截器,真正调用只需引用一个栈即可-->
<interceptors>
<interceptor-stack name="myStack">
<interceptor-ref name="myInterceptor"/>
<interceptor-ref name="defaultStack"/>
</interceptor-stack>
</interceptors>
<action name="b" class="" method="">
<interceptor-ref name="myStack"/>
</action>
设置默认的拦截器栈
<!--设置默认拦截器栈-->
<default-interceptor-ref name="myStack"></default-interceptor-ref>
struts2定义了默认的拦截器栈,defaultStack
里面默认的18个拦截器
方法拦截器
拦截器拦截的是整个Action,action中所有业务方法都会被拦截。有时需要拦截某个方法。就用方法拦截器
实现
需要继承MethodFilterInterceptor
类
package cn.cq.jszg.candidate;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.MethodFilterInterceptor;
import ognl.MethodFailedException;
public class MethodInterceptor extends MethodFilterInterceptor {
@Override
protected String doIntercept(ActionInvocation invocation) throws Exception {
System.out.println("方法拦截器执行");
return invocation.invoke();
}
}
<interceptor name="methodInterceptor" class="cn.cq.jszg.candidate.MethodInterceptor"/>
<interceptor-ref name="methodInterceptor">
<!-- 配置哪些方法配拦截,对应action中的hello,save方法-->
<param name="includeMethods">hello,save</param>
<!-- 配置哪些方法不被来接-->
<param name="excludeMethods"></param>
</interceptor-ref>
单文件上传
添加jsp页面,表单必须时post提交,并且设置enctype="multipart/form-data"
jsp
<form action="xxx.action" method="post" enctype="multipart/form-data">
文件:<input type="file" name="file" /><input type="submit" value="上传"/>
</form>
处理类
package cn.cq.jszg.candidate;
import com.opensymphony.xwork2.Action;
import javax.swing.*;
import java.io.File;
/**
* 文件上传类
*/
public class dateup {
// file和表单域名称相同
private File file;
//上传文件的名称也是由struts2设置好
//属性名=表单域名+FileName
private String fileFileName;
//文件类型 属性名=表单域名+Contenttype
private String fileContenttype;
//上传文件业务方法
public String upload(){
return Action.SUCCESS;
}
public File getFile() {
return file;
}
public void setFile(File file) {
this.file = file;
}
public String getFileFileName() {
return fileFileName;
}
public void setFileFileName(String fileFileName) {
this.fileFileName = fileFileName;
}
public String getFileContenttype() {
return fileContenttype;
}
public void setFileContenttype(String fileContenttype) {
this.fileContenttype = fileContenttype;
}
}
配置
<package name="default" namespace="/" extends="struts-default">
<action name="dateup" class="cn.cq.jszg.candidate.dateup" method="upload">
<result>/success.jsp</result>
<!--设置上传单个文件大小-->
<interceptor-ref name="fileUpload">
<param name="maximumSize">xxx</param>
</interceptor-ref>
</action>
</package>
<!--设置文件上传临时目录-->
<constant name="struts.multipart.saveDir" value="目录"></constant>
<!--设置上传总文件大小,必须大于单个文件大小,bbb>xxx-->
<constant name="struts.multipart.maxSize" value="bbb"></constant>
批量上传
页面
<form action="xxx.action" method="post" enctype="multipart/form-data">
文件1:<input type="file" name="file" /><br/>
文件2:<input type="file" name="file" /><br/>
文件3:<input type="file" name="file" /><br/><input type="submit" value="上传"/>
</form>
事务类
package cn.cq.jszg.candidate;
import com.opensymphony.xwork2.Action;
import javax.swing.*;
import java.io.File;
/**
* 文件上传类
*/
public class dateup {
// file和表单域名称相同
private File file[];
//上传文件的名称也是由struts2设置好
//属性名=表单域名+FileName
private String fileFileName[];
//文件类型 属性名=表单域名+Contenttype
private String fileContenttype[];
//上传文件业务方法
public String upload(){
return Action.SUCCESS;
}
public File[] getFile() {
return file;
}
public void setFile(File[] file) {
this.file = file;
}
public String[] getFileFileName() {
return fileFileName;
}
public void setFileFileName(String[] fileFileName) {
this.fileFileName = fileFileName;
}
public String[] getFileContenttype() {
return fileContenttype;
}
public void setFileContenttype(String[] fileContenttype) {
this.fileContenttype = fileContenttype;
}
}
配置不变
文件下载
可以直接通过超链接下载,但不安全
通过流
类
package cn.cq.jszg.candidate;
import com.opensymphony.xwork2.Action;
import org.apache.struts2.ServletActionContext;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
public class DownloadAction {
private String filename;
public String execuxe(){
return Action.SUCCESS;
}
//结果集为stream需要返回InputStream类型
public InputStream getInputStream() throws FileNotFoundException {
String path = ServletActionContext.getServletContext().getRealPath(filename);
return new FileInputStream(new File(path,filename));
}
public String getFilename() {
return filename;
}
public void setFilename(String filename) {
this.filename = filename;
}
}
配置
<package name="default" namespace="/" extends="struts-default">
<action name="download" class="cn.cq.jszg.candidate.DownloadAction">
<!--文件下载结果集类型为stream-->
<result type="stream">
<!--attachment下载框,filename设置下载时的文件名-->
<param name="contentDisposition">attachment;filename=${filename}</param>
</result>
</action>
</package>
处理异常
public class Myexception {
private String name;
private int a = 2;
public String delet() throws Exception{
System.out.println(a/0);
return Action.SUCCESS;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
<action name="delet" class="cn.cq.jszg.candidate.Myexception" method="delet">
<result>/success.jsp</result>
<!--发生异常
exception为发生异常的类型,result和name一样
-->
<result name="exception">/excp.jsp</result>
<exception-mapping exception="java.lang.Exception" result="exception"></exception-mapping>
</action>
全局错误
<global-exception-mappings>
<exception-mapping exception="java.lang.Exception" result="exception"></exception-mapping>
</global-exception-mappings>