文章目录
Struts2
五、ServletAPI
所谓Servlet APl,即我们常用的Request、Session、Application等Sevlet对象。Struts2访问Servlet API有解耦
和耦合
两种方式。
5.1、解耦方式:
解耦方式:Struts2将部分Servlet API中的对象封装成Map,可以通过ActionContext获取。获取到的Servlet API对象全部是Map。
示例:
//Action类中的方法
public String execute() throws Exception {
ActionContext actionContext = ActionContext.getContext();
Map request = (Map) actionContext.get("request"); //获取request
Map application = actionContext.getApplication(); //获取app1ication
Map session = actionContext.getSession(); //获取session
session.put("session_object","我是session~~"); //向session中存值
System.out.println("hello struts2~~~struts构建完成");
System.out.println("接收到的name是===="+name);
message="----message----";
System.out.println("user==="+user);
return "OK"; //返回的结果
}
<%@ page contentType="text/html;charset=UTF-8" isELIgnored="false" language="java" %>
<html>
<body>
<h2>struts2</h2>
<br/>
<%--前端取值--%>
<span>session:${sessionScope.session_object}</span>
</body>
</html>
5.2、解耦方式:
解耦方式:依赖原生servlet中的对象,直接获取servlet中的Request、Session、Application等Sevlet对象。
耦合方式需要导入servlet-api的jar包:
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
public String execute() throws Exception {
// ActionContext actionContext = ActionContext.getContext();
// Map request = (Map) actionContext.get("request");
// Map application = actionContext.getApplication();
// Map session = actionContext.getSession();
// session.put("session_object","我是session~~");
HttpServletRequest request = ServletActionContext.getRequest();
HttpSession session = request.getSession();
HttpServletResponse response = ServletActionContext.getResponse();
request.setAttribute("request_object","我是request对象");
return "OK"; //返回的结果
}
<span>耦合反式的request对象:${requestScope.request_object}</span>
六、Struts标签
6.1、UI标签:
- 与JSTL标签类似,Struts2提供了强大的标签库。
- Struts2标签分为UI标签和通用标签。
- 使用Struts2标签需要在页面上引入标签库:
<% @taglib uri="/struts-tags" prefix="s" %>
<h4>struts中的表单标签,语法如下:</h4>
<s:form action=" " method="POST">
<s:textfield name="username" label="用户名" />
<s:password name="password" label="密码" />
<s:submit value="登录"/>
</s:form>
以上代码等同于:
<form action=" " method="post">
中名: <input type="text" name="username" /><br/>
密码: <input type="password" name="password" /><br/>
<input type="submit" value="登录"/>
</form>
运行发现,我们没有给表单换行,但是其标签内部是有样式的。
可以去掉样式,加全局配置:
<!--去掉ui标签的主题-->
<constant name="struts.ui.theme" value="simple" />
6.2、通用标签:(业务逻辑判断)
【s:if标签用于条件判断】
<s:if test=""></s:if>
<s:elseif test=""></s:elseif>
<s:e1se></s:else>
s:if标签用于条件判断,相当于jstl中的
<c:if test=""></c:if>
示例:
<action name="tags" class="com.wxy.action.TagsAction" >
<result name="success">tags.jsp</result>
</action>
package com.wxy.action;
import com.opensymphony.xwork2.Action;
import lombok.Getter;
import lombok.Setter;
public class TagsAction implements Action {
@Setter
@Getter
private String username;
public String execute() throws Exception {
return SUCCESS;
}
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib uri="/struts-tags" prefix="s" %>
<html>
<head>
<title>标签练习页面</title>
</head>
<body>
<h4>struts中的表单标签,语法如下:</h4>
<s:form action="/tags.action" method="POST">
<s:textfield name="username" label="用户名"/>
<s:password name="password" label="密码"/>
<s:submit value="提交"/>
</s:form>
<s:if test="username=='admin'">管理员</s:if>
<s:elseif test="username=='user'">用户</s:elseif>
<s:else>不是管理员</s:else>
</body>
</html>
【s:iterator用于集合的遍历】
<s:jterator value="" var="" status=""></s:iterator>
s:iterator用于集合的遍历,相当于jstl中的
<c:forEach items="" var="" varStatus=""></c:forEach>
遍历泛型是基本数据类型的集合,如List,需要通过id属性访问当前的元素。
<table>
<!--遍历基本数据类型的集合需要借助ia属性-->
<s:iterator value="strList" id="str" status="status">
<!-- status属性可以访问对象的索引或奇偶-->
<s:if test="#status.even"> <!-- even代表奇数,注意#。此时从索引0开始-->
<tr style="background-color: green">
<td><s:property value="str"/></td>
</tr>
</s:if>
<s:else>
<tr><td><s:property value="str"/></td></tr>
</s:else>
</s:iterator>
</table>
遍历泛型是对象类型的集合,如List,不需要使用id,输出的时候直接写对象中的属性名即可:
<table>
<s:iterator value="userLis" status="status">
<!-- odd代表偶数-->
<tr <s:if test="#status.odd">style="background-color: green"</s:if>>
<td><s:property value="userName"/></td>
</tr>
</s:iterator>
</table>
注意:
奇偶是正常的,只是对应的索引是从零开始
基本类型的遍历除了用id
,还可以用var
。
具体示例:一个数组,一个对象:
@Setter
@Getter
public class TagsAction implements Action {
private String username;
private List<String> userList = new ArrayList<String>();
private List<User> users = new ArrayList<User>();
public String execute() throws Exception {
for(int i = 0; i<10; i++) {
userList.add("str"+i);
}
users.add(new User(1,"张三"));
users.add(new User(2,"李四"));
return "OK";
}
}
<s:iterator value="userList" var="str">
<s:text name="s"/>
</s:iterator>
<br/>
<s:iterator value="users">
<s:property value="id"/>===<s:property value="userName"/><br/>
</s:iterator>
遍历正确应该是下图,但是我的数组遍历出了问题,遍历了十个“S” ??????
七、校验:
7.1、通用验证:
- Action类继承ActionSupport,ActionSupport中有一个validate方法进行数据校验。
- addFieldError要求
必须给result配置一个input类型的结果
。所以在调用login方法时,会报找不到result input。
- 直接范例演示:
- 要继承
ActionSupport
,登录和注册方法之前都会先去执行验证方法
- 要继承
public class ValidateAction extends ActionSupport {
@Getter
@Setter
private String username;
@Override
public void validate()
{
if (username==null || username.trim().length()==0) {
//向页面中添加错误信息
addFieldError("username", "用户名不能为空");
}
}
public String login(){
return SUCCESS;
}
public String register(){
return SUCCESS;
}
}
<!--验证-->
<action name="validateLogin" class="com.wxy.action.ValidateAction" method="login">
<result name="success">validate.jsp</result>
<!--要添加input类型的result,否则会报错-->
<result name="input">validate.jsp</result>
</action>
<action name="validateReg" class="com.wxy.action.ValidateAction" method="register">
<result name="success">validate.jsp</result>
<result name="input">validate.jsp</result>
</action>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib uri="/struts-tags" prefix="s" %>
<html>
<head>
<title>验证页面</title>
</head>
<body>
<form action="/validateLogin" method="post">
<input name="username">
<input type="submit" value="提交">
</form>
<form action="/validateReg" method="post">
<input name="username">
<input type="submit" value="提交">
</form>
<s:fielderror/> <%--返回错误提示结果--%>
<s:fielderror name="username"/> <%--只返回关于name的错误结果--%>
</body>
</html>
执行结果:
7.2、方法验证:
validate()方法会验证当前Action类中所有的方法,如果只想验证其中的一个方法,自己设置方法:可以使用validateXxx()
方法,其中Xxx
是被验证的方法名的首字母大写
。
//只验证login方法,不验证其它方法
public void validateLogin() {
if (username == null || username.1ength() == 0){
//向页面中添加错误信息
addFieldError( "username","用户名不能为空");
}
}
7.3、校验框架:
验证框架是把验证信息都写在xml文件中,对某一个Action类进行验证,需要在Action类的同一个包下创建xml文件,文件命名为Action类的类名-validation.xml
多放两个头部,有人的失效是因为头部问题:
【但是我找了所有框架失效的方法也没解决为啥我的框架失效~~】
<!DOCTYPE validators PUBLIC
"-//Apache Struts//XWork Validator 1.0.2//EN"
"http://struts.apache.org/dtds/xwork-validator-1.0.2.dtd">
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE validators PUBLIC "-//Apache Struts//XWork Validator 1.0.3//EN"
"http://struts.apache.org/dtds/xwork-validator-1.0.3.dtd">
<validators>
<field name="username">
<field-validator type="requiredstring">
<message>用户名不可以为空</message>
</field-validator>
</field>
<field name="password">
<field-validator type="required">
<message>密码不能为空</message>
</field-validator>
<field-validator type="stringlength">
<param name="maxLength">10</param>
<param name="minLength">6</param>
<message>密码必须在${minLength}和${maxLength}</message>
</field-validator>
</field>
<field name="repwd">
<field-validator type="fieldexpression">
<param name="expression">password=repwd</param>
<message>两次密码不一致</message>
</field-validator>
</field>
</validators>
- validators标签:在校验框架中,所有的验证都写在validators标签中。
- field标签:每一个需要验证的属性都是一个field标签,name指定要验证那个属性
- field-validator标签:代表一种验证规则,通过type指定规则。
| 值 | 说明 |
|-required-|-必填-|
| requiredstring | 必填字符串,长度大于0,可使用trim参数去除空格 |
|-stringlength-|-限制字符串长度,指定maxLength和minLength-|
| regex| 正则表达式,通过参数regex指定正则表达式 |
|-fieldexpression-|-字段之间的逻辑关系-|
- param标签:给校验器传递的参数
- message标签:给页面的提示信息。
- 校验框架和validate()方法一样,会验证action中所有的方法,如果只验证某一个方法,需要将xml文件的名字命名为
action类名-动作名-validation.xml
。其中的动作名是指struts.xml中对应此方法的action的name。- 多种验证方式并存时,执行顺序是:
校验框架--->validateXxx()---->validate()
7.4、验证框架失效原因:
1. 命名是否符合约定:
2. validation.xml的标签是否正确,比如是否关闭所有标签(或者有错误的标签),是否多一个关闭标签等。注意:这方面错误没有任何提示, 只是验证不起作用。
3. fieldName 命名是否和jsp以及 action中的属性一致 如:
- action中有userName属性,
- jsp 中有标签:<s:textfield name=“userName”/>
- validation.xml中需有:< field name=“userName” >…< /field>
- 上述的三个userName一定要一致。
4.检查是否在同一个field里面配置了重复的参数,重复的参数也可能会使你的信息显示不出来
5.要记得验证框架所产生的错误信息被是写入到了fielderror里面去了,所以在需要显示错误信息的页面要用<s:fielderror/>
6.LoginAction-validation.xml文件必须与Action类放在相同的目录下
7.在struts.xml文件中的<action>标签
中,必须包含<result name="input">
和<result name="success"
,否则会报找不到result的错误。 validation.xml校验不通过后,会默认返回 input所在 页面
8.最主要的一个可能:如果你的Action类没有继承ActionSupport类
,就不会进行页面流程的自动跳转。
八、拦截器
拦截器可以在请求进入action之前做预处理,比如判断用户是否登录。也可以在action执行之后进行处理。
8.1、拦截器配置:
例如:利用拦截器计算action的执行时间------->在interceptor包下创建TimeInterceptor类
package com.wxy.interceptor;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.Interceptor;
public class TimeInterceptor implements Interceptor {
/**
* 销毁方法
*/
public void destroy() {
}
/**
* 初始化方法
*/
public void init() {
}
/**
* 核心方法
* @param actionInvocation
* @return
* @throws Exception
*/
public String intercept(ActionInvocation actionInvocation) throws Exception {
long start = System.currentTimeMillis();
String result = actionInvocation.invoke(); //将请求放行,进入action;result是action中方法返回的字符串string
long end = System.currentTimeMillis();
System.out.println("运行时间===="+(end-start));
return result ;
}
}
ResultTest 是之前使用过的一个类,现在用来给他加载拦截器:
public class ResultTest extends ActionSupport {
@Setter
@Getter
private String page;
public String goPage() {
System.out.println("page===="+page);
return page;
}
}
拦截器配置与设置拦截器:
<?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" extends="struts-default">
<!--拦截器配置-->
<interceptors>
<interceptor name="time" class="com.wxy.interceptor.TimeInterceptor"></interceptor>
</interceptors>
<action name="page" class="com.wxy.action.ResultTest" method="goPage">
<result name="success">${page}.jsp</result>
<interceptor-ref name="time"/> <!--引用上面设置的拦截器-->
<interceptor-ref name="defaultStack"/> <!--默认拦截器-->
</action>
</package>
</struts>
拦截器:
- 在action的内部,有一个默认拦截器,
<interceptor-ref name="defaultStack"/>
。- 注意:使用自己的拦截器时,内部拦截器需要自己配置上,否则自己的拦截器会覆盖内部拦截器。
8.2、拦截器栈:
<package name="default" extends="struts-default">
<interceptors>
<interceptor name="time" class="com.wxy.interceptor.TimeInterceptor"/>
<!--设置拦截器栈:-->
<interceptor-stack name="time-stack">
<interceptor-ref name="time"/>
<interceptor-ref name="defaultStack"/>
</interceptor-stack>
</interceptors>
<action name="page" class="com.wxy.action.ResultTest" method="goPage">
<result name="success">${page}.jsp</result>
<interceptor-ref name="time-stack"/> <!--直接引用栈名-->
</action>
</package>
</struts>
8.3、登录拦截器(servlet中登录过滤去):
【判断登录的时候,session中是否已有用户】
写一个登录页面login.jsp,其他几个页面(可以使用之前我们写过的那几个页面),一个登录拦截器,配置拦截器
LoginInterceptor .java登录拦截器:
public class LoginInterceptor implements Interceptor {
public void destroy() {
}
public void init() {
}
public String intercept(ActionInvocation actionInvocation) throws Exception {
ActionContext actionContext = ActionContext.getContext();
Map session = actionContext.getSession();
if(session.get("user_session")==null){ //没有登录
return "login";
}
return actionInvocation.invoke();
}
}
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>
<!--去掉ui标签的主题-->
<constant name="struts.ui.theme" value="simple" />
<!--所有的action都放在package中,必须继承struts-default-->
<!--struts-default中有默认的拦截器配置,能处理参数等信息-->
<package name="default" extends="struts-default">
<!--有很多时候一个<result>可供很多<action>使用,这时可以使用<global-results>标签来定义全局的<result>。
执行顺序:当一个Action返回的String没有相应的<result>与之对应,Struts2就会查找全局的<result>。-->
<!-- <interceptors>-->
<!-- <interceptor name="time" class="com.wxy.interceptor.TimeInterceptor"></interceptor>-->
<!-- </interceptors>-->
<interceptors>
<interceptor name="time" class="com.wxy.interceptor.TimeInterceptor"/>
<interceptor name="login" class="com.wxy.interceptor.LoginInterceptor"/>
<interceptor-stack name="time-stack">
<interceptor-ref name="time"/>
<interceptor-ref name="login"/>
<interceptor-ref name="defaultStack"/>
</interceptor-stack>
</interceptors>
<!--没有登录都回到login.jsp-->
<global-results>
<result name="login">/login.jsp</result>
</global-results>
<!--=====-->
<action name="hello" class="com.wxy.action.HelloAction">
<result name="OK">hello.jsp</result>
</action>
<!--=====-->
<action name="login" class="com.wxy.action.TestAction" method="login">
<result name="success">/index.jsp</result>
</action>
<action name="register" class="com.wxy.action.TestAction" method="register">
<result name="success">/index.jsp</result>
</action>
<!--动态结果-->
<action name="page" class="com.wxy.action.ResultTest" method="goPage">
<result name="success">${page}.jsp</result>
<interceptor-ref name="time-stack"/> <!--引用上面设置的拦截器-->
</action>
<!--标签-->
<action name="tags" class="com.wxy.action.TagsAction" >
<result name="OK">tags.jsp</result>
</action>
<!--验证-->
<action name="validateLogin" class="com.wxy.action.ValidateAction" method="login">
<result name="success">validate.jsp</result>
<result name="input">validate.jsp</result>
</action>
<action name="validateReg" class="com.wxy.action.ValidateAction" method="register">
<result name="success">validate.jsp</result>
<result name="input">validate.jsp</result>
</action>
</package>
</struts>
九、文件上传下载
9.1、文件上传:
直接上代码使用,跟以前的流方法一样
@Setter
@Getter
public class FileAction extends ActionSupport {
private File upload; //与表单name属性的值一致
private String uploadFileName; //与文件属性名+FileName
public String upload() {
System.out.println("文件名===="+uploadFileName);
try {
String ext = uploadFileName.substring(uploadFileName.lastIndexOf("."));
byte[] buffer = new byte[1024];
FileInputStream fis = new FileInputStream(upload); //读取文件
FileOutputStream fos = new FileOutputStream("E:/upload/" + System.currentTimeMillis()+ext); //文件以当前时间戳+后缀命名
int length = fis.read(buffer);
while (length != -1) {
fos.write(buffer);
length = fis.read(buffer);
}
fos.close();
fis.close();
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
return SUCCESS;
}
}
可能会上传两个同名文件,可以将文件名字:以当前时间戳+文件原后缀命名
//得到文件后缀
String ext = uploadFileName.substring(uploadFileName.lastIndexOf("."));
//文件以当前时间戳+后缀命名
FileOutputStream fos = new FileOutputStream("E:/upload/" + System.currentTimeMillis()+ext);
<action name="upload" class="com.wxy.action.FileAction" method="upload">
<result name="success">/upload.jsp</result>
</action>
9.2、文件下载:
……