Struts2可以完成成员变量:
1>数据类型自动转换
2>默认存储到request域对象中
3>默认为请求转发形式
使用Struts 2 开发程序的基本步骤:
1>加载Struts2 类库
2>配置web.xml文件
3>开发视图层页面
4>开发控制层Action
5>配置struts.xml文件
6>部署、运行项目
执行流程:
index.jsp --> web.xml-->StrutsPrepareAndExecuteFilter类 -->struts.xml
--> <action name="" class=""> --><action class类进行处理> --> <result>标签
Struts2 常用类库:
文件名 说 明
struts2-core-xxx.jar Struts 2框架的核心类库
xwork-core-xxx.jar XWork类库,Struts 2的构建基础
ognl-xxx.jar Struts 2使用的一种表达式语言类库
freemarker-xxx.jar Struts 2的标签模板使用类库
javassist-xxx.GA.jar 对字节码进行处理
commons-fileupload-xxx.jar 文件上传时需要使用
commons-io-xxx.jar Java IO扩展
commons-lang-xxx.jar 包含了一些数据类型的工具类
struts2五大核心jar包:
1>commons-logging.jar ————– 用于通用日志处理
2>freemarker.jar ————– 表现层框架,定义了struts2的可视组件主题
3>ognl.jar ————– ognl表达式语言,struts2支持该EL
4>struts2-core.jar ————– struts2 2.0.11.2的核心库
5>xwork.jar ————– webwork的核心库
拦截器类:StrutsPrepareAndExecuteFilter (重点)
配置web.xml:
<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>
//将全部请求定位到指定的Struts 2过滤器中(必须这样做)
</filter-mapping>
编写helloWorld.jsp
<div>
<h1>
<!--显示Struts Action中message的属性内容-->
<s:property value="message"/> //输出显示语句
</h1>
</div>
<div>
<form action="helloWorld.action" method="post">
请输入您的姓名:
<input name="name" type="text" />
<input type="submit" value="提交" />
</form>
</div>
编写HelloWorldAction
public class HelloWorldAction implements Action { //要实现Action接口
// 用户输入的姓名
private String name = "";
// 向用户显示的信息
private String message = "";
public String execute() { //此方法为固定格式
// 根据用户输入的姓名,进行"Hello,XXXX!"的封装
this.setMessage("Hello,"+this.getName()+"!");
// 处理完毕,返回导航结果的逻辑名
return "success";
}
… //省略setter、getter方法
}
配置Struts 2配置文件(struts.xml)
<package name="default" extends="struts-default">
<action name="helloWorld" class=“controller.HelloWorldAction">
//name属性与form表单的action属性值对应
<result name="success">/ok.jsp</result> //默认转发到ok.jsp
//与Action返回字符串对应
</action>
</package>
如何使用Struts 2实现用户登录验证?
开发控制层Action-LoginAction
public class LoginAction implements Action {
private String username = "";
private String password = "";
public String execute() {
if(“sa".equals(username) && “123".equals(password)) {
return "success";
} else {
return "error";
}
}
}
配置Struts 2配置文件(struts.xml)
<package name="default" namespace="/" extends="struts-default">
<action name="login" class=“controller.LoginAction">
<!-- 结果为“success”时,跳转至success.jsp页面 -->
<result name="success">success.jsp</result>
<!-- 结果为"error"时,跳转至fail.jsp页面 -->
<result name="error">fail.jsp</result>
</action>
</package>
Struts 2使用session读取参数的方式(三种):
1>与Servlet API解耦的访问方式
对Servlet API进行封装
提供了三个Map对象访问request、session、application作用域
通过ActionContext类获取这三个Map对象
Object | get("request")
Map | getSession()
Map |getApplication()
ActionContext.getContext().getSession().put("bName", bookName);
2>与Servlet API耦合的访问方式
通过ServletActionContext类获取Servlet API对象
ServletContext | getServletContext()
HttpServletResponse | getResponse()
HttpServletRequest | getRequest()
通过request.getSession()获取session对象
通过xxx.setAttribute()和xxx.getAttribute() 功能,在不同的页面或Action中传递数据
ServletActionContext.getRequest().getSession().setAttribute("bName", bookName);
3>通过实现sessionAware接口,重写setSession()方法
public class HaAction implements SessionAware {
private Map<String, Object> session;
private String bookName;
private int age; // 数据类型自动转换.
// 此处,会将属性值放在request域中.
//get和set方法
public String execute() {
bookName = "hello," + bookName;
System.out.println(age + 2);
System.out.println(bookName);
//ActionContext.getContext().getSession().put("bName", bookName);
//ServletActionContext.getRequest().getSession().setAttribute("bName",bookName);
session.put("xName", bookName);
return "yes";
}
@Override
public void setSession(Map<String, Object> session) {
this.session = session;
}
}
Struts2自动实例化javabean对象,需要在xxxAction类中提供get()和set()方法来存值和取值,并且Struts2自动完成存值和取值的:
public class UserAction {
private Users user; // struts帮你new对象
// 注入!! struts拿到值,帮你塞到对象中相应的字段
public Users getUser() {
return user;
}
public void setUser(Users user) {
this.user = user;
}
public String execute() {
if (user.getUsername().equals("sa") && "123".equals(user.getPassword())) {
return "success";
}
return "no";
}
}
Struts 2的提供了表单验证机制:
继承ActionSupport类来完成Action开发,用validate()方法进行验证,且validate()方法先于execute()方法的进行。
ActionSupport类不仅对Action接口进行简单实现,同时增加了验证、本地化等支持
public class UserAction extends ActionSupport { //继承ActionSupport
public void validate() { //表单验证方法,此方法格式固定
System.out.println("1");
if (username.trim().length() == 0) {
addFieldError("x", "帐号不能为空!"); //验证出错,指定错误提示信息
// 此错误要使用struts.xml中的<s:fielderror/> 标签来显示
}
if (password.trim().length() == 0) {
addFieldError("y", "密码不能为空!");
}
}
struts.xml:
<result name="input">/index.jsp</result> 返回错误页面
Struts2的标签库分为两个部分:
1>UI标签 : 页面显示的
2>通用标签 : 逻辑判断的
<%@ taglib prefix="s" uri="/struts-tags"%>
//需要在页面中引入Struts 2的标签库
1>UI标签 :
1>>常用表单标签
标 签 说 明
<s:form>…</s:form> 表单标签
<s:textfield>…</s: textfield > 文本输入框
<s:password>…</s: password > 密码输入框
<s:textarea>…</s: textarea > 文本域输入框
<s:radio>…</s: radio > 单选按钮
<s:checkbox>…</s: checkbox > 多选框
<s:submit /> 提交标签
<s:reset /> 重置标签
<s:hidden /> 隐藏域标签
<s:text> 文本标签
<s:text name="username"/>
2>>修改JSP页面
<s:fielderror/> //显示服务器返回的错误信息
<s:form action="login">
<s:textfield label="帐号" name="username"/> // label属性用于显示文本
<s:password label="密码" name="password"/>
<s:submit value="登录"/>
</s:form>
2>通用标签 :
1>>条件标签
<s:if test="表达式"> //表达式条件为true时,执行相应的代码
需要执行的代码
</s:if>
<s:elseif test="表达式">需要执行的代码</s:elseif>
<s:else>需要执行的代码</s:else>
2>>迭代: 用于遍历集合(只能用在集合上)
<s:iterator value="集合对象" status="status" id="name">
读取集合对象并输出显示
</s:iterator>
value属性
需要进行遍历的集合对象
status属性
表示当前迭代对象的一个实例
id属性
当前迭代元素的id,可直接访问元素,该参数可选
<s:iterator value="hobby">
<s:property /> //用于遍历集合中的元素
</s:iterator>
<s:iterator begin="1" end="9" step="1" var="i">
${i}
</s:iterator>
网站登录程序运行流程图 :
login.jsp ---> 核心控制器 ---> Action ---> Result---> main.jsp
1>核心控制器
需要在web.xml中进行配置
对框架进行初始化,以及处理所有的请求
StrutsPrepareAndExecuteFilter (org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter)
2>Action
配置Action
<action name="login" class="controller.LoginAction">
3>result
实现对结果的调用
result元素的值指定对应的实际资源位置
name属性表示result逻辑名
<result name="success">main.jsp</result>
1. struts.xml
核心配置文件,主要负责管理Action
通常放在WEB-INF/classes目录下(提高安全性),在该目录下的struts.xml文件可以被自动加载
1>constant元素
<constant name="" value="" />
配置常量,可以改变Struts 2框架的一些行为
name属性表示常量名称,value属性表示常量值
当你修改了这个文件,必须要重新启动服务器,才可以,每次重启耗时耗力.
进入开发模式
<constant name="struts.devMode" value="true" />
每次修改配置文件之后,服务器就能保存更改后的配置.这样服务器一直在监听这个配置文件. 所以在项目开发完毕,运行阶段,是一定要将此属性移除.避免不必要的性能开销!!!
设置字符集.utf-8. 等等常量配置.
修改整个项目的编码方式, 过滤器 技能最好用!
<struts>
<constant name="struts.i18n.encoding" value="UTF-8"/>
</struts>
2>package元素
<package name=""extends="">
包的作用:简化维护工作,提高重用性
包可以“继承”已定义的包,并可以添加自己包的配置
name属性为必需的且唯一,用于指定包的名称
extends属性指定要扩展的包
namespace属性定义该包中action的命名空间 ,为可选属性
假设.一个项目中. 有登录功能. 整个项目又分为前台,和后台. 前台一个人开发,后台另一个人开发. 登录功能都使用了login这个action名称. (用名称空间区分)
<package name="qian" namespace="/qian" extends="struts-default">
<action name="login" class="controller.BeforeAction">
<result>/ok.jsp</result>
</action>
</package>
<package name="hou" namespace="/hou" extends="struts-default">
<action name="login" class="controller.AfterAction">
<result>/ok2.jsp</result>
</action>
</package>
3.Action
<action name="x" class="x">
1>
Include包含:(用于区分多个struts.xml)
<include file="struts-user.xml"/>
<include file="struts-product.xml"/>
2>Action的三种创建方式
1>>Action就是一个普通的类,
必须要写public String execute(){}
2>>实现Action接口,重写execute()方法
3>>继承ActionSupport类,也要重写execute()
a)加入数据校验
b)国际化...
Action接口和ActionSupport类中有5个常量字符串,帮助我们做action返回
1.SUCCESS : 处理正常,返回成功后的结果
2.NONE : 处理正常,不返回任务提示
3.INPUT : 需要用户正确输入才能顺利执行
4.ERROR : 处理结果失败
5.LOGIN : 需要正确登录后才能顺利执行
public String execute(){
System.out.println("咋滴");
return LOGIN;
}
3>Action的作用:
1.封装数据的实际工作 (接收请求)
2.为数据转移提供场所 (转发数据)
3.框架根据返回字符串呈现响应的页面 (跳转页面)
4>Action的method属性:
实现Action中不同方法的调用
优点:
避免动态方法调用的安全隐患
减少大量的Action配置 ,节省了类的数量.
<action name="login" class="controller.UserAction" method="login">
</action>
<action name="register" class="controller.UserAction" method="register">
</action> //method的值为方法名
5>Action的动态方法调用:
作用:减少Action数量和类的数量.
使用:actionName!methodName.action
<a href="hello!a">AAA</a>
<a href="hello!b">BBB</a>
<a href="hello!b.action">BBB</a> // .action可以省略
禁用:将常量属性struts.enable.DynamicMethodInvocation设置为false
<constant name="struts.enable.DynamicMethodInvocation" value="false"/>
在学习JDBC中,为什么使用ps的占位符?
select * from user where name = ? and pwd = ?
取代了
select * from user where name = “+name+” and pwd = “+pwd+”
防止SQL的攻击(SQL注入!)
动态方法虽好,但不安全. 甚至极度危险.我们不仅不用,而且不让别人用. 应该把动态方法禁用!!!!
6>Action通配符 : 减少Action的数量,是另一种形式的动态方法调用
<a href="aUsersunguoan">test1</a>
<a href="bUsermike">test2</a>
<action name="*User*" class="controller.UserAction" method="{1}">
<result name="{2}">/{1}.jsp</result> //{1}代表第一个*,{2}代表第二个*
</action>
7>默认的Action : 没有Action匹配请求时,执行默认的Action
通过<default-action-ref … />元素配置默认Action
<!-- 引用默认的action -->
<default-action-ref name="xxx"/>
<!-- 定义默认的action -->
<action name="xxx">
<result>404.jsp</result>
</action>
<action name="buy*" class="controller.ProductAction" method="{1}">
//省略class属性,将使用ActionSupport类
<result name="{1}">/{1}.jsp</result>
</action>
练习:
使用同一Action处理用户登录和注册请求
练习:
增加默认Action
当出现异常错误时,执行默认Action,并跳转到错误提示页面
4.Result常用结果类型
<result name="x">/x</result>
1>结果类型
1>>dispatcher类型
默认结果类型,后台使用RequestDispatcher转发请求
转发 : request保值,一次请求:url不变
request.getRequestDispatcher(“xxx.jsp”).forward(request,response);
2>>redirect类型
后台使用的sendRedirect()将请求重定向至指定的URL
重定向 : request失效, 两次请求:url改变
response.sendRedirect(“xxx.jsp”);
3>>redirectAction类型
主要用于重定向到Action
struts中默认为转发形式.
转发
<result type="dispatcher">/ok.jsp</result>
重定向
<result type="redirect">/ok.jsp</result>
重定向到action
<result type="redirectAction">chong</result>
<action name="*User" class=“controller.UserAction" method="{1}">
<result name="success" type="dispatcher">{1}_success.jsp</result>
<result name="input">{1}.jsp</result>
<result name="error">error.jsp</result>
</action>
2>动态结果
配置(开发阶段)时不知道执行后的结果是哪一个,运行时才知道哪个结果作为视图显示给用户
WEB-INF下的文件.直接访问是不可以的
真实的项目中.都会把页面保护到WEB-INF下
<action name="login" class="controller.UserAction">
<result>/WEB-INF/${lv}.jsp</result> //这样才能访问到WEB-INF下的文件
</action>
public String execute() { //动态结果
if (userid.equals("sa")) {
setLv("ceo");
} else if (userid.equals("admin")) {
setLv("cto");
} else {
setLv("guest");
}
return "success";
}
3>全局结果 : 返回的字符串找不到匹配,就会去全局的result中看看有没有.
作用:
实现同一个包中多个action共享一个结果
//全局结果位于package元素内
<package name="" extends="">
<global-results>
<result name="a">a.jsp</result>
<result name="b">b.jsp</result>
<result name="woyebuzhidao">404.jsp</result>
</global-results>
</package>
按照模块划分开发任务..
张三就负责用户相关的功能
李四就负责商品页面展示的功能
练习:
用户登录成功,显示该用户的成绩信息
Oracle中创建自增序列:
Create sequence xx; 在jdbc的java文件中输出值
String sql = "insert into users (id,username,password)values(xx.nextval,?,?)";
一、拦截器
为什么需要拦截器 ?
早期MVC框架将一些通用操作写死在核心控制器中,致使框架灵活性不足、可扩展性降低
Struts 2将核心功能放到多个拦截器中实现,拦截器可自由选择和组合,增强了灵活性,有利于系统的解耦
什么是拦截器?
Struts 2大多数核心功能是通过拦截器实现的,每个拦截器完成某项功能
拦截器方法在Action执行之前或者之后执行
拦截器栈:
从结构上看,拦截器栈相当于多个拦截器的组合
在功能上看,拦截器栈也是拦截器
拦截器与过滤器原理很相似
为Action提供附加功能时,无需修改Action代码,使用拦截器来提供
拦截器做三阶段执行周期:
1、做一些Action执行前的预处理
2、将控制交给后续拦截器或返回结果字符串
3、做一些Action执行后的处理
Struts 2自带拦截器:
params拦截器
负责将请求参数设置为Action属性
servletConfig拦截器
将源于Servlet API的各种对象注入到Action
fileUpload拦截器
对文件上传提供支持
exception拦截器
捕获异常,并且将异常映射到用户自定义的错误页面
validation拦截器
调用验证框架进行数据验证
workflow拦截器
调用Action类的validate(),执行数据验证
应用:
1.阻止表单的重复提交
token 令牌 拦截器解决问题
当注册用户的时候.点按钮就注册.
struts自带的拦截器的一个应用.
注意:
报空指针异常:
1>将页面表单元素全部改为strust标签元素
2>引用默认的拦截器 defaultStack
<interceptor-ref name="defaultStack"/>
配置拦截器:在struts.xml中的<packge>中配置
<action name="register" class="controller.UserAction">
<!-- 表单得不到数据,报空指针异常,引入这个默认的拦截器,否则可省略 -->
<interceptor-ref name="defaultStack"/>
<!-- 引用拦截器 -->
<interceptor-ref name="token" />
<!--1. 此拦截器为token拦截器,struts已经实现 -->
<result>/ok.jsp</result>
<result name="invalid.token">/nosubmit.jsp</result>
<!--2. 拦截后返回的页面,必须要配置 -->
</action>
在form表单中指定拦截器 ,token为令牌
<s:form> <s:token />
<!-- 3.必须有,实现token拦截器 -->
<s:textfield label="帐号" key="user.username" /> ?
</s:form>
token原理:
所谓的token,中文译名“令牌”。顾名思义,就是使用一种验证,验证对了,就让你继续访问action,验证错了,直接把你拦截住,交给指定的页面。验证的关键步骤就是token拦截器的原理:当浏览器访问一个带有<s:token></s:token>标签的页面时,服务器生成一个随机数,把这个随机数放入session中,也放入表单中隐藏的token属性中。显然,此时这两个随机数是相等的。而后,当你向服务器提交表单时,表单中隐藏的token属性也会传给服务器。此时如果访问的的action使用了token的拦截器(即上面配置的),那么服务器会从session中得到刚才的随机数,将其和表单的token属性比较,看两者是否相同。如果相等,表示此次访问不是重复提交,无需拦截,清空session中的随机数,接着正常访问action。如果不相等,则直接拦截,不会继续访问action,直接跳到<result name="invalid.token">指定的页面,并且session中的随机数不变。
自定义拦截器:
1>实现Interceptor接口
void init():初始化拦截器所需资源
void destroy():释放在init()中分配的资源
String intercept(ActionInvocation ai) throws Exception实现拦截器功能
利用ActionInvocation参数获取Action状态返回结果(result)字符串
2>继承AbstractInterceptor类
提供了init()和destroy()方法的空实现
只需要实现intercept方法即可(常用)
案例:计算方法的执行时间
自定义拦截器:
拦截器的实现:
public class MyTime extends AbstractInterceptor { //继承AbstractInterceptor类
@Override
public String intercept(ActionInvocation ai) throws Exception {
//重写intercept()
long l1 = System.nanoTime(); //预处理工作
//long l1=System.currentTimeMillis();
System.out.println("开始..."+l1);
ai.invoke(); //放行,执行后续拦截器或Action
long l2 = System.nanoTime(); //后续处理工作
System.out.println("结束..."+l2);
System.out.println("方法共耗时:"+(l2-l1)+"纳秒!");
return null;
}
}
配置拦截器:
<package name="test2" extends="struts-default">
<interceptors><!-- 定义拦截器 -->
<interceptor name="mytime" class="interceptor.MyTime"/> //定义单个拦截器
</interceptors>
<action name="test" class="controller.TestAction">
<interceptor-ref name="mytime"/> //引用拦截器
<result>ok.jsp</result>
</action>
</package>
3.模拟查看购物车(重点)
见项目Struts2_3_test3
4.对用户添加权限控制,非登录用户不能访问信息管理页面
权限验证拦截器
public class AuthorizationInterceptor extends AbstractInterceptor {
public String intercept(ActionInvocation invocation) throws Exception{
//获取用户会话信息
Map session = invocation.getInvocationContext().getSession();
User user = (User)session.get("login");
if (user == null) {
//终止执行,返回登录页面
return Action.LOGIN;
} else {
//继续执行剩余的拦截器和Action
return invocation.invoke();
}
}
}
在配置文件中定义拦截器并引用它
<package name="renthouse" extends="struts-default">
<interceptors>
<!--定义权限验证拦截器-->
<interceptor name="myAuthorization"
class="interceptors.AuthorizationInterceptor">
</interceptor>
<!--定义拦截器栈-->
<interceptor-stack name="myStack">
<interceptor-ref name="defaultStack"/>
<interceptor-ref name="myAuthorization"/>
</interceptor-stack>
</interceptors>
<!-- 定义默认拦截器 -->
<default-interceptor-ref name="myStack"/>
//因为包含在默认拦截器内,所以Action中无需再引用权限拦截器
…
</package>
二、文件上传
1.单个文件上传
Commons-FileUpload组件
Commons是Apache开放源代码组织的一个Java子项目,其中的FileUpload是用来处理HTTP文件上传的子项目
Commons-FileUpload组件特点
使用简单:可以方便地嵌入到JSP文件中,编写少量代码即可完成文件的上传功能
能够全程控制上传内容
能够对上传文件的大小、类型进行控制
环境要求
commons-fileupload-xxx.jar
commons-io-xxx.jar
2.多个文件上传
表单设置
多个File控件
name属性相同
Action的修改
将三个属性的类型修改成数组类型
uploadContentType=控件名+ContentType
uploadFileName=控件名+FileName
//获取提交的多个文件
private File[] upload;
//封装上传文件的类型
private String[] uploadContentType;
//封装上传文件名称
private String[] uploadFileName;
表单设置:
<form action="upload" method="post" enctype="multipart/form-data">
<input type="file" name="fname"/>
<input type="file" name="fname"/>
<input type="file" name="fname"/>
<button>保存</button></form>
<s:file name="upload" label="选择文件"/>
public class Up {
// 三个属性,命名是有一定规则的
// 1.文件本身
private File[] fname;
// 2.文件类型
private String[] fnameContentType;
// 3.文件名
private String[] fnameFileName;
public String execute() {
System.out.println("开始上传!");
try {
for(int i = 0; i<fname.length;i++){
String path = ServletActionContext.getServletContext().getRealPath("/picture/"+fnameFileName[i]);
FileInputStream input = new FileInputStream(fname[i]);
FileOutputStream output = new FileOutputStream(path);
IOUtils.copy(input, output); //调用IOUtils工具类的copy方法
output.close();
input.close();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return "success";
}
//get()和set()方法
}
在struts.xml中配置:
<action name="upload" class=“controller.UploadAction">
<!--通过param参数设置保存目录的路径-->
<param name="savePath">/upload</param> //以参数方式指定保存路径
注:上传视频用FTP上传到服务器
三、下载文件
1.下载文件(可以下载电影)
stream结果类型:
stream结果类型将文件数据(通过InputStream获取)直接写入响应流相关参数的配置
名称 作用
contentType 设置发送到浏览器的MIME类型
contentLength 设置文件的大小
contentDisposition 设置响应的HTTP头信息中的Content-Disposition参数的值
inputName 指定Action中提供的inputStream类型的属性名称
bufferSize 设置读取和下载文件时的缓冲区大小
contentType类型:
文件类型 类型设置
Word application/msword
Execl application/vnd.ms-excel
PPT application/vnd.ms-powerpoint
图片 image/gif , image/bmp,image/jpeg
文本文件 text/plain
html网页 text/html
可执行文件 application/octet-stream
实现步骤
编写下载文件Action
获取InputStream输入流
配置Action
指定下载文件的类型、下载形式等
在struts.xml中配置:
<result type="stream">
<param name="contentType">application/octet-stream</param>
//通用设置,可执行文件
<param name="inputName">fileInputStream</param>
<param name="contentDisposition">attachment;filename="${fileName}"
</param>
//Attachement表示以附件形式下载Filename表示下载时显示的文件名称
<param name="bufferSize">4096</param>
</result>
Action类中编写:
// 两个属性
// 1.文件流
private InputStream fileInputStream;
// 2.下载的文件名称
private String fileName;
public String execute() {
// fielName = E:/spiderMan.exe
File f = new File(fileName);
this.fileName = f.getName();
try {
fileInputStream = new FileInputStream(f);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
return "success";
}
//get和set方法
index.jsp页面:
<a href="xia?fileName=E:/课件/JavaFrames笔记/Struts2_3/downloadVideo/1.exe">点击下载:1.exe
</a> //传参文件名
一、数据类型转换
a)原始类型和封装类型
原始类型 : 四类八种
封装类型 : 除了原始类型.
在web应用程序中.url中传递的参数,永远是字符串类型.
其实将类型转换的是struts平台中内置的类型转换器.
b)多值类型
当数组在页面指明了下标,那么必须在action中创建时指明长度.
List很像数组,但要注意:
不需要初始化任何一个list
如果没有泛型的说明,list默认为List<String>
如果指明泛型,struts2会自动创建与泛型想匹配的对象并注入
Struts 2内置类型转换器:
内置类型转换器 说 明
String 将int、long、double、boolean、String类型的数组或 者java.util.Date类型转换为字符串
boolean/Boolean 在字符串和布尔值之间进行转换
char/Character 在字符串和字符之间进行转换
date 在字符串和日期类型之间进行转换。具体输入输出格 式与当前的Locale相关
数组和集合 在字符串数组和数组对象、集合对象间进行转换
c)自定义类型转换器
页面传来的是string,接收会自动转为相应的数据类型.
页面传来一个时间格式,而我自定义时间格式.
自定义类型转换器要:
继承StrutsTypeConverter抽象类
重写以下两个方法:
将一个或多个字符串值转换为指定的类型
public Object convertFromString(Map context, String[] value, Class toType)
将指定对象转化为字符串
public String convertToString(Map context, Object object)
处理类型转换错误:
向用户输出类型转换错误的前提条件:
1>Action要继承ActionSupport类
2>配置input结果视图
3>页面使用Struts 2表单标签或<s:fielderror>标签
Struts 2表单标签内嵌了输出错误信息功能
普通HTML标签需使用<s:fielderror>标签输出转换错误
步骤:
1.在src目录创建xwork-conversion.properties
转换类全名=类型转换器类全名
java.util.Date=time.MyTime
2.Action类必须继承ActionSupport
3.在src下建立资源文件. message.properties
xwork.default.invalid.fieldvalue=error!
修改所有类型的转换错误信息
4.定制指定字段的类型转换错误信息,与Action同包下建立资源文件,ActionName.properties
invalid.fieldvalue.time=日期转换错误!
5.在Struts.xml中配置资源显示
<constant name="struts.custom.i18n.resources" value="message"/>
SimpleDateFormat[] sdf = {
new SimpleDateFormat("yyyy-MM-dd"),
new SimpleDateFormat("yyyy=MM=dd"),
new SimpleDateFormat("yyyy年MM月dd日")
};
sdf[i].parse(str); //转换为Date对象
String s =new SimpleDateFormat().format((Date)o); //转换为字符串
二、OGNL表达式
Object-Graph Navigation Language : 对象图导航语言
开源项目,取代页面中的脚本,简化数据的访问.
同EL表达式类似,但功能更为强大.
java的方式: user.getSchool().getGrade().getBanji();
ognl的方式: user.school.grade.banji
值栈(ValueStack)?
由Struts2框架创建的存储区域,具有栈的特点
Action的实例会被存放到值栈中
Struts2将值栈作为OGNL上下文根对象
OGNL访问值栈:
按照从上到下的顺序
靠近栈顶的同名属性会被读取
ognl访问:
a)访问javabean
user没有new,address也没有new. 都是由struts帮我们创建。
自动创建对象的前提:
1.必须要符合javabean规范的类.必须提供无参构造.
2.属性必须get和set.否则赋不上值
b)访问集合对象
<s:textfield label="所在城市" name="list2[0].address.city"/>
<s:property value="list2.empty"/><!-- false -->判断集合是否是空的,空返回true
<s:property value="list2.size"/> <!-- 2 --> 获得集合的长度
遍历Map集合:
<s:iterator value="map">
<s:property value="key"/> <!-- A B 键 --> 获得键
<s:property value="value.name"/> 获得值的指定属性
<s:property value="value.address.guo"/>
</s:iterator>
ActionContext对象中有值栈对象与非值栈对象:
值栈中内容可以直接访问,访问非值栈对象需添加#前缀
非值栈包括:
1>request
2>session
3>application
4>parameters
5>attr 全域查找
访问ActionContext的上下文
request.getParameter(“user”) == #parameters.userxx
request.getAttribute(“user”) == #request.userxx==#request['userxxx']
session.getAttribute(“user”) == #session.userxx
application.getAttribute(“user”) == #application.userxx
<!-- 标签访问四大域,使用ognl的一种写法 -->
<s:property value="#session.userxx"/>
<s:property value="#session['userxx']"/>
struts2扩展标签:
1>时间格式标签
<s:date name="now" format="yyyy_MM_dd hh:mm:ss"/>
<s:date format="format" nice="true|false" name="name" id="id">
……
</s:date>
format属性:指定格式进行日期格式化
nice属性:该属性只有true和false两个值,用于指定是否输出指定日期与当前时间的时差,默认是false
name属性:表示当前需要格式化的日期
id属性:表示引用该元素的id值
2>超链接
定义超链接
<s:url id="dizhi" action="test">
<s:param name="name">张三</s:param>
<s:param name="age">22</s:param>
</s:url>
<!-- url直接写,会被识别为字符串 -->
<!-- %{dizhi} -->
<s:a href="%{dizhi}">去ok</s:a>
action属性:表示指定生成URL的地址,
<s:param />表示需要传递的参数信息
name属性:表示传递的参数名称
value属性:表示传递参数所具有的值
使用超链接
<!-- url直接写,会被识别为字符串 -->
<!-- %{dizhi} -->
<s:a href="%{dizhi}">去ok</s:a>
使用OGNL标签的时候,标签中的值是具有数据类型的:
<s:url>的value是字符串类型
<s:property>的value是object类型
所以对于字符串类型的属性,必须使用%{ } , 否则会被看成是字符串.
private Date time;
<s:property value="time" />
<s:property>标签用于输出指定对象的属性值
3>调试标签
查看当前struts运行环境中的所有对象值的情况.
<s:debug/>
ActionContext的组成:
值栈-ValueStack
非值栈-Stack Context
4>
<s:set name="age" value="10" scope="request"/>
<s:set>标签将一个值赋给指定范围的变量
一、验证框架
1>验证方法
1>>validate()方法
数据校验,需要的步骤:
1.继承ActionSupport
2.重写 public void validate(){}
3.<result name=”input”>指定校验失败后返回的页面
注:execute() ,validate(), validateXxx()方法为系统自动调用
2>>validateXxx()方法
在struts.xml中配置:
<action name="login" class="controller.UserAction" method="login">
调用Action的任务方法(execute())时,validate()会默认执行,可用于验证一些通用规则。如果想为某个方法指定一些独有的验证规则,采用validateXxx()方法
//有一些特殊的验证规则,封闭到一个方法里。
public void validateLogin(){ //Xxx=指定处理请求的方法
if(user.getUsername().length()<3||user.getUsername().length()>=10)
addFieldError("2","账号必须在3~10位之间!");
//参数一属性名,参数二错误信息
}
public void validate(){ }
public String login() { //validateLogin()名称对应login()方法 }
三个方法的执行顺序:
validateLogin() ---> validate() ---> login()
当 validateLogin() 与 validate() 校验不过时,不会执行login()。
2>验证框架 : 一个独立的文件
使用validate()或者validateXxx()方法时:
当验证规则复杂时,实现过程繁琐,导致Action类中代码臃肿,不符合java设计思路
因此,要使用Struts 2提供的验证框架
验证框架中的校验器:
校验器类型 校验器名称 说 明
必填校验器 required字段不能为空
必填字符串校验器 requiredstring 字段值不能为空长度要大于0
整数校验器 int 字段的整数值的范围
字符串长度校验器 stringlength字段值的长度的范围
正则表达式校验器 regex字段是否匹配一个正则表达式
字段表达式校验器 fieldexpression字段必须满足一个逻辑表达式
日期校验器 date 日期输入是否在指定的范围内
双精度校验器 double 字段值必须是双精度类型
使用验证框架的步骤:
1)编写Action,需要继承ActionSupport类
2)配置Action
<result name=”input”>指定校验失败后返回的页面
3)编写表单
4)编写验证文件和校验规则
验证文件类型为xml格式,存放在与Action同一包中
命名规则有两种方式
1>ClassName-validation.xml //ClassName: Action类名
2>UserAction-user_regist-validation.xml(只校验regist方法,必须为此格式)
UserAction-validation.xml内容:
<validators> //验证文件的根节点
<field name="user.username">
<field-validator type="requiredstring">
<param name="trim">true</param>
<message>用户名不能为空!哈哈</message>
</field-validator>
<field-validator type="stringlength">
<param name="minLength">5</param>
<param name="maxLength">10</param>
<message>用户名长度在${minLength}和${maxLength}之间~@~!</message>
</field-validator>
</field>
<field name="user.password2">
<field-validator type="fieldexpression">
<param name="expression">password1==password2</param>
<message>两次密码必须相同~!</message>
</field-validator>
</field>
<field name="user.telephone">
<field-validator type="regex">
<param name="expression">^\d{3,4}-\d{7,8}$</param>
<message>座机格式错误</message>
</field-validator>
</field>
</validators>
注意:验证文件的dtd的导入.如果报错,可以改为绝对路径
二、国际化
国际化(Internationalization:I18N)?
使程序在不做任何修改的情况下,可以在不同国家或地区和不同语言环境下,按照当地的语言和格式习惯显示字符
本地化(Localization:L10N)
一个国际化的程序,当它运行在本地机器时,能够根据本地机器的语言和地区设置显示相应字符
Java程序的国际化思路(struts的国际化,也是基于java的国际化.只不过是struts进行了二次封装):
将程序中的提示信息、错误信息等放在资源文件中,为不同国家/语言编写对应资源文件
资源文件由很多key-value对组成,key保持不变,value随国家/语言不同而不同
这些资源文件使用共同的基名,通过在基名后面添加语言代码、国家和地区代码来进行区分
且必须要翻译.用一个文件来做为翻译的资源.
常用资源文件:
资源文件名 说 明
ApplicationResources_en.properties 所有英文语言的资源
ApplicationResources_zh.properties 所有的中文语言的资源
ApplicationResources_zh_CN.properties 针对中国大陆的、中文语言的资源
ApplicationResources_zh_HK.properties 针对中国香港的、中文语言的资源
ApplicationResources.properties 默认资源文件,如果请求的资源文 件不存在,将调用它的资源进行显 示
使用国际化的步骤:
1>指定资源文件的基名及存储路径 (在struts.xml中配置)
<struts>
<constant name="struts.custom.i18n.resources" value="message"/>
<constant name="struts.i18n.encoding" value="UTF-8"/>(不常用)
</struts>
2>创建资源文件
存放位置:在src目录下,或者直接在WEB-INF/classes目录名称
china.properties
english.properties
3>实现JSP页面信息的国际化显示
将JSP页面中的所有中文在english.properties文件中替换掉
4>实现验证错误信息的国际化显示
编写资源文件
编写验证文件
编写Action
资源文件的范围:(不重要)
1>全局资源文件
所有包的所有Action类都可以访问
导致资源文件变得非常庞大臃肿,不便于维护
2>包范围资源文件
对应包下创建package_language_country.properties
处于该包及子包下的action才可以访问该文件
3>Action范围资源文件
命名格式ActionClassName_language_country.properties
只有单个Action可以访问
查找顺序:Action---package---全局
<validators>
<field name="user.username">
<field-validator type="requiredstring">
<param name="trim">true</param>
<message key="namenull"/> //通过key对应错误信息
</field-validator>
</validators>
三、分页查询(重要)
CREATE TABLE stu(
id INT(10) PRIMARY KEY AUTO_INCREMENT, //mySql设置主键自增
NAME VARCHAR(20)
);
SELECT * FROM stu;
INSERT INTO stu VALUES(DEFAULT,'张三');
sql语句:
oracle : rownum
mysql : limit
sqlserver : between...and...
String sql = "SELECT * from stu order by id limit ?,?";
//第一个参数指定第一个返回记录行的下标,第二个参数指定返回记录行的最大数目。初始记录行的下标是 0(而不是 1)
对象的注入
具体的见Struts2_5_test2。
1>数据类型自动转换
2>默认存储到request域对象中
3>默认为请求转发形式
使用Struts 2 开发程序的基本步骤:
1>加载Struts2 类库
2>配置web.xml文件
3>开发视图层页面
4>开发控制层Action
5>配置struts.xml文件
6>部署、运行项目
执行流程:
index.jsp --> web.xml-->StrutsPrepareAndExecuteFilter类 -->struts.xml
--> <action name="" class=""> --><action class类进行处理> --> <result>标签
Struts2 常用类库:
文件名 说 明
struts2-core-xxx.jar Struts 2框架的核心类库
xwork-core-xxx.jar XWork类库,Struts 2的构建基础
ognl-xxx.jar Struts 2使用的一种表达式语言类库
freemarker-xxx.jar Struts 2的标签模板使用类库
javassist-xxx.GA.jar 对字节码进行处理
commons-fileupload-xxx.jar 文件上传时需要使用
commons-io-xxx.jar Java IO扩展
commons-lang-xxx.jar 包含了一些数据类型的工具类
struts2五大核心jar包:
1>commons-logging.jar ————– 用于通用日志处理
2>freemarker.jar ————– 表现层框架,定义了struts2的可视组件主题
3>ognl.jar ————– ognl表达式语言,struts2支持该EL
4>struts2-core.jar ————– struts2 2.0.11.2的核心库
5>xwork.jar ————– webwork的核心库
拦截器类:StrutsPrepareAndExecuteFilter (重点)
配置web.xml:
<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>
//将全部请求定位到指定的Struts 2过滤器中(必须这样做)
</filter-mapping>
编写helloWorld.jsp
<div>
<h1>
<!--显示Struts Action中message的属性内容-->
<s:property value="message"/> //输出显示语句
</h1>
</div>
<div>
<form action="helloWorld.action" method="post">
请输入您的姓名:
<input name="name" type="text" />
<input type="submit" value="提交" />
</form>
</div>
编写HelloWorldAction
public class HelloWorldAction implements Action { //要实现Action接口
// 用户输入的姓名
private String name = "";
// 向用户显示的信息
private String message = "";
public String execute() { //此方法为固定格式
// 根据用户输入的姓名,进行"Hello,XXXX!"的封装
this.setMessage("Hello,"+this.getName()+"!");
// 处理完毕,返回导航结果的逻辑名
return "success";
}
… //省略setter、getter方法
}
配置Struts 2配置文件(struts.xml)
<package name="default" extends="struts-default">
<action name="helloWorld" class=“controller.HelloWorldAction">
//name属性与form表单的action属性值对应
<result name="success">/ok.jsp</result> //默认转发到ok.jsp
//与Action返回字符串对应
</action>
</package>
如何使用Struts 2实现用户登录验证?
开发控制层Action-LoginAction
public class LoginAction implements Action {
private String username = "";
private String password = "";
public String execute() {
if(“sa".equals(username) && “123".equals(password)) {
return "success";
} else {
return "error";
}
}
}
配置Struts 2配置文件(struts.xml)
<package name="default" namespace="/" extends="struts-default">
<action name="login" class=“controller.LoginAction">
<!-- 结果为“success”时,跳转至success.jsp页面 -->
<result name="success">success.jsp</result>
<!-- 结果为"error"时,跳转至fail.jsp页面 -->
<result name="error">fail.jsp</result>
</action>
</package>
Struts 2使用session读取参数的方式(三种):
1>与Servlet API解耦的访问方式
对Servlet API进行封装
提供了三个Map对象访问request、session、application作用域
通过ActionContext类获取这三个Map对象
Object | get("request")
Map | getSession()
Map |getApplication()
ActionContext.getContext().getSession().put("bName", bookName);
2>与Servlet API耦合的访问方式
通过ServletActionContext类获取Servlet API对象
ServletContext | getServletContext()
HttpServletResponse | getResponse()
HttpServletRequest | getRequest()
通过request.getSession()获取session对象
通过xxx.setAttribute()和xxx.getAttribute() 功能,在不同的页面或Action中传递数据
ServletActionContext.getRequest().getSession().setAttribute("bName", bookName);
3>通过实现sessionAware接口,重写setSession()方法
public class HaAction implements SessionAware {
private Map<String, Object> session;
private String bookName;
private int age; // 数据类型自动转换.
// 此处,会将属性值放在request域中.
//get和set方法
public String execute() {
bookName = "hello," + bookName;
System.out.println(age + 2);
System.out.println(bookName);
//ActionContext.getContext().getSession().put("bName", bookName);
//ServletActionContext.getRequest().getSession().setAttribute("bName",bookName);
session.put("xName", bookName);
return "yes";
}
@Override
public void setSession(Map<String, Object> session) {
this.session = session;
}
}
Struts2自动实例化javabean对象,需要在xxxAction类中提供get()和set()方法来存值和取值,并且Struts2自动完成存值和取值的:
public class UserAction {
private Users user; // struts帮你new对象
// 注入!! struts拿到值,帮你塞到对象中相应的字段
public Users getUser() {
return user;
}
public void setUser(Users user) {
this.user = user;
}
public String execute() {
if (user.getUsername().equals("sa") && "123".equals(user.getPassword())) {
return "success";
}
return "no";
}
}
Struts 2的提供了表单验证机制:
继承ActionSupport类来完成Action开发,用validate()方法进行验证,且validate()方法先于execute()方法的进行。
ActionSupport类不仅对Action接口进行简单实现,同时增加了验证、本地化等支持
public class UserAction extends ActionSupport { //继承ActionSupport
public void validate() { //表单验证方法,此方法格式固定
System.out.println("1");
if (username.trim().length() == 0) {
addFieldError("x", "帐号不能为空!"); //验证出错,指定错误提示信息
// 此错误要使用struts.xml中的<s:fielderror/> 标签来显示
}
if (password.trim().length() == 0) {
addFieldError("y", "密码不能为空!");
}
}
struts.xml:
<result name="input">/index.jsp</result> 返回错误页面
Struts2的标签库分为两个部分:
1>UI标签 : 页面显示的
2>通用标签 : 逻辑判断的
<%@ taglib prefix="s" uri="/struts-tags"%>
//需要在页面中引入Struts 2的标签库
1>UI标签 :
1>>常用表单标签
标 签 说 明
<s:form>…</s:form> 表单标签
<s:textfield>…</s: textfield > 文本输入框
<s:password>…</s: password > 密码输入框
<s:textarea>…</s: textarea > 文本域输入框
<s:radio>…</s: radio > 单选按钮
<s:checkbox>…</s: checkbox > 多选框
<s:submit /> 提交标签
<s:reset /> 重置标签
<s:hidden /> 隐藏域标签
<s:text> 文本标签
<s:text name="username"/>
2>>修改JSP页面
<s:fielderror/> //显示服务器返回的错误信息
<s:form action="login">
<s:textfield label="帐号" name="username"/> // label属性用于显示文本
<s:password label="密码" name="password"/>
<s:submit value="登录"/>
</s:form>
2>通用标签 :
1>>条件标签
<s:if test="表达式"> //表达式条件为true时,执行相应的代码
需要执行的代码
</s:if>
<s:elseif test="表达式">需要执行的代码</s:elseif>
<s:else>需要执行的代码</s:else>
2>>迭代: 用于遍历集合(只能用在集合上)
<s:iterator value="集合对象" status="status" id="name">
读取集合对象并输出显示
</s:iterator>
value属性
需要进行遍历的集合对象
status属性
表示当前迭代对象的一个实例
id属性
当前迭代元素的id,可直接访问元素,该参数可选
<s:iterator value="hobby">
<s:property /> //用于遍历集合中的元素
</s:iterator>
<s:iterator begin="1" end="9" step="1" var="i">
${i}
</s:iterator>
网站登录程序运行流程图 :
login.jsp ---> 核心控制器 ---> Action ---> Result---> main.jsp
1>核心控制器
需要在web.xml中进行配置
对框架进行初始化,以及处理所有的请求
StrutsPrepareAndExecuteFilter (org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter)
2>Action
配置Action
<action name="login" class="controller.LoginAction">
3>result
实现对结果的调用
result元素的值指定对应的实际资源位置
name属性表示result逻辑名
<result name="success">main.jsp</result>
1. struts.xml
核心配置文件,主要负责管理Action
通常放在WEB-INF/classes目录下(提高安全性),在该目录下的struts.xml文件可以被自动加载
1>constant元素
<constant name="" value="" />
配置常量,可以改变Struts 2框架的一些行为
name属性表示常量名称,value属性表示常量值
当你修改了这个文件,必须要重新启动服务器,才可以,每次重启耗时耗力.
进入开发模式
<constant name="struts.devMode" value="true" />
每次修改配置文件之后,服务器就能保存更改后的配置.这样服务器一直在监听这个配置文件. 所以在项目开发完毕,运行阶段,是一定要将此属性移除.避免不必要的性能开销!!!
设置字符集.utf-8. 等等常量配置.
修改整个项目的编码方式, 过滤器 技能最好用!
<struts>
<constant name="struts.i18n.encoding" value="UTF-8"/>
</struts>
2>package元素
<package name=""extends="">
包的作用:简化维护工作,提高重用性
包可以“继承”已定义的包,并可以添加自己包的配置
name属性为必需的且唯一,用于指定包的名称
extends属性指定要扩展的包
namespace属性定义该包中action的命名空间 ,为可选属性
假设.一个项目中. 有登录功能. 整个项目又分为前台,和后台. 前台一个人开发,后台另一个人开发. 登录功能都使用了login这个action名称. (用名称空间区分)
<package name="qian" namespace="/qian" extends="struts-default">
<action name="login" class="controller.BeforeAction">
<result>/ok.jsp</result>
</action>
</package>
<package name="hou" namespace="/hou" extends="struts-default">
<action name="login" class="controller.AfterAction">
<result>/ok2.jsp</result>
</action>
</package>
3.Action
<action name="x" class="x">
1>
Include包含:(用于区分多个struts.xml)
<include file="struts-user.xml"/>
<include file="struts-product.xml"/>
2>Action的三种创建方式
1>>Action就是一个普通的类,
必须要写public String execute(){}
2>>实现Action接口,重写execute()方法
3>>继承ActionSupport类,也要重写execute()
a)加入数据校验
b)国际化...
Action接口和ActionSupport类中有5个常量字符串,帮助我们做action返回
1.SUCCESS : 处理正常,返回成功后的结果
2.NONE : 处理正常,不返回任务提示
3.INPUT : 需要用户正确输入才能顺利执行
4.ERROR : 处理结果失败
5.LOGIN : 需要正确登录后才能顺利执行
public String execute(){
System.out.println("咋滴");
return LOGIN;
}
3>Action的作用:
1.封装数据的实际工作 (接收请求)
2.为数据转移提供场所 (转发数据)
3.框架根据返回字符串呈现响应的页面 (跳转页面)
4>Action的method属性:
实现Action中不同方法的调用
优点:
避免动态方法调用的安全隐患
减少大量的Action配置 ,节省了类的数量.
<action name="login" class="controller.UserAction" method="login">
</action>
<action name="register" class="controller.UserAction" method="register">
</action> //method的值为方法名
5>Action的动态方法调用:
作用:减少Action数量和类的数量.
使用:actionName!methodName.action
<a href="hello!a">AAA</a>
<a href="hello!b">BBB</a>
<a href="hello!b.action">BBB</a> // .action可以省略
禁用:将常量属性struts.enable.DynamicMethodInvocation设置为false
<constant name="struts.enable.DynamicMethodInvocation" value="false"/>
在学习JDBC中,为什么使用ps的占位符?
select * from user where name = ? and pwd = ?
取代了
select * from user where name = “+name+” and pwd = “+pwd+”
防止SQL的攻击(SQL注入!)
动态方法虽好,但不安全. 甚至极度危险.我们不仅不用,而且不让别人用. 应该把动态方法禁用!!!!
6>Action通配符 : 减少Action的数量,是另一种形式的动态方法调用
<a href="aUsersunguoan">test1</a>
<a href="bUsermike">test2</a>
<action name="*User*" class="controller.UserAction" method="{1}">
<result name="{2}">/{1}.jsp</result> //{1}代表第一个*,{2}代表第二个*
</action>
7>默认的Action : 没有Action匹配请求时,执行默认的Action
通过<default-action-ref … />元素配置默认Action
<!-- 引用默认的action -->
<default-action-ref name="xxx"/>
<!-- 定义默认的action -->
<action name="xxx">
<result>404.jsp</result>
</action>
<action name="buy*" class="controller.ProductAction" method="{1}">
//省略class属性,将使用ActionSupport类
<result name="{1}">/{1}.jsp</result>
</action>
练习:
使用同一Action处理用户登录和注册请求
练习:
增加默认Action
当出现异常错误时,执行默认Action,并跳转到错误提示页面
4.Result常用结果类型
<result name="x">/x</result>
1>结果类型
1>>dispatcher类型
默认结果类型,后台使用RequestDispatcher转发请求
转发 : request保值,一次请求:url不变
request.getRequestDispatcher(“xxx.jsp”).forward(request,response);
2>>redirect类型
后台使用的sendRedirect()将请求重定向至指定的URL
重定向 : request失效, 两次请求:url改变
response.sendRedirect(“xxx.jsp”);
3>>redirectAction类型
主要用于重定向到Action
struts中默认为转发形式.
转发
<result type="dispatcher">/ok.jsp</result>
重定向
<result type="redirect">/ok.jsp</result>
重定向到action
<result type="redirectAction">chong</result>
<action name="*User" class=“controller.UserAction" method="{1}">
<result name="success" type="dispatcher">{1}_success.jsp</result>
<result name="input">{1}.jsp</result>
<result name="error">error.jsp</result>
</action>
2>动态结果
配置(开发阶段)时不知道执行后的结果是哪一个,运行时才知道哪个结果作为视图显示给用户
WEB-INF下的文件.直接访问是不可以的
真实的项目中.都会把页面保护到WEB-INF下
<action name="login" class="controller.UserAction">
<result>/WEB-INF/${lv}.jsp</result> //这样才能访问到WEB-INF下的文件
</action>
public String execute() { //动态结果
if (userid.equals("sa")) {
setLv("ceo");
} else if (userid.equals("admin")) {
setLv("cto");
} else {
setLv("guest");
}
return "success";
}
3>全局结果 : 返回的字符串找不到匹配,就会去全局的result中看看有没有.
作用:
实现同一个包中多个action共享一个结果
//全局结果位于package元素内
<package name="" extends="">
<global-results>
<result name="a">a.jsp</result>
<result name="b">b.jsp</result>
<result name="woyebuzhidao">404.jsp</result>
</global-results>
</package>
按照模块划分开发任务..
张三就负责用户相关的功能
李四就负责商品页面展示的功能
练习:
用户登录成功,显示该用户的成绩信息
Oracle中创建自增序列:
Create sequence xx; 在jdbc的java文件中输出值
String sql = "insert into users (id,username,password)values(xx.nextval,?,?)";
一、拦截器
为什么需要拦截器 ?
早期MVC框架将一些通用操作写死在核心控制器中,致使框架灵活性不足、可扩展性降低
Struts 2将核心功能放到多个拦截器中实现,拦截器可自由选择和组合,增强了灵活性,有利于系统的解耦
什么是拦截器?
Struts 2大多数核心功能是通过拦截器实现的,每个拦截器完成某项功能
拦截器方法在Action执行之前或者之后执行
拦截器栈:
从结构上看,拦截器栈相当于多个拦截器的组合
在功能上看,拦截器栈也是拦截器
拦截器与过滤器原理很相似
为Action提供附加功能时,无需修改Action代码,使用拦截器来提供
拦截器做三阶段执行周期:
1、做一些Action执行前的预处理
2、将控制交给后续拦截器或返回结果字符串
3、做一些Action执行后的处理
Struts 2自带拦截器:
params拦截器
负责将请求参数设置为Action属性
servletConfig拦截器
将源于Servlet API的各种对象注入到Action
fileUpload拦截器
对文件上传提供支持
exception拦截器
捕获异常,并且将异常映射到用户自定义的错误页面
validation拦截器
调用验证框架进行数据验证
workflow拦截器
调用Action类的validate(),执行数据验证
应用:
1.阻止表单的重复提交
token 令牌 拦截器解决问题
当注册用户的时候.点按钮就注册.
struts自带的拦截器的一个应用.
注意:
报空指针异常:
1>将页面表单元素全部改为strust标签元素
2>引用默认的拦截器 defaultStack
<interceptor-ref name="defaultStack"/>
配置拦截器:在struts.xml中的<packge>中配置
<action name="register" class="controller.UserAction">
<!-- 表单得不到数据,报空指针异常,引入这个默认的拦截器,否则可省略 -->
<interceptor-ref name="defaultStack"/>
<!-- 引用拦截器 -->
<interceptor-ref name="token" />
<!--1. 此拦截器为token拦截器,struts已经实现 -->
<result>/ok.jsp</result>
<result name="invalid.token">/nosubmit.jsp</result>
<!--2. 拦截后返回的页面,必须要配置 -->
</action>
在form表单中指定拦截器 ,token为令牌
<s:form> <s:token />
<!-- 3.必须有,实现token拦截器 -->
<s:textfield label="帐号" key="user.username" /> ?
</s:form>
token原理:
所谓的token,中文译名“令牌”。顾名思义,就是使用一种验证,验证对了,就让你继续访问action,验证错了,直接把你拦截住,交给指定的页面。验证的关键步骤就是token拦截器的原理:当浏览器访问一个带有<s:token></s:token>标签的页面时,服务器生成一个随机数,把这个随机数放入session中,也放入表单中隐藏的token属性中。显然,此时这两个随机数是相等的。而后,当你向服务器提交表单时,表单中隐藏的token属性也会传给服务器。此时如果访问的的action使用了token的拦截器(即上面配置的),那么服务器会从session中得到刚才的随机数,将其和表单的token属性比较,看两者是否相同。如果相等,表示此次访问不是重复提交,无需拦截,清空session中的随机数,接着正常访问action。如果不相等,则直接拦截,不会继续访问action,直接跳到<result name="invalid.token">指定的页面,并且session中的随机数不变。
自定义拦截器:
1>实现Interceptor接口
void init():初始化拦截器所需资源
void destroy():释放在init()中分配的资源
String intercept(ActionInvocation ai) throws Exception实现拦截器功能
利用ActionInvocation参数获取Action状态返回结果(result)字符串
2>继承AbstractInterceptor类
提供了init()和destroy()方法的空实现
只需要实现intercept方法即可(常用)
案例:计算方法的执行时间
自定义拦截器:
拦截器的实现:
public class MyTime extends AbstractInterceptor { //继承AbstractInterceptor类
@Override
public String intercept(ActionInvocation ai) throws Exception {
//重写intercept()
long l1 = System.nanoTime(); //预处理工作
//long l1=System.currentTimeMillis();
System.out.println("开始..."+l1);
ai.invoke(); //放行,执行后续拦截器或Action
long l2 = System.nanoTime(); //后续处理工作
System.out.println("结束..."+l2);
System.out.println("方法共耗时:"+(l2-l1)+"纳秒!");
return null;
}
}
配置拦截器:
<package name="test2" extends="struts-default">
<interceptors><!-- 定义拦截器 -->
<interceptor name="mytime" class="interceptor.MyTime"/> //定义单个拦截器
</interceptors>
<action name="test" class="controller.TestAction">
<interceptor-ref name="mytime"/> //引用拦截器
<result>ok.jsp</result>
</action>
</package>
3.模拟查看购物车(重点)
见项目Struts2_3_test3
4.对用户添加权限控制,非登录用户不能访问信息管理页面
权限验证拦截器
public class AuthorizationInterceptor extends AbstractInterceptor {
public String intercept(ActionInvocation invocation) throws Exception{
//获取用户会话信息
Map session = invocation.getInvocationContext().getSession();
User user = (User)session.get("login");
if (user == null) {
//终止执行,返回登录页面
return Action.LOGIN;
} else {
//继续执行剩余的拦截器和Action
return invocation.invoke();
}
}
}
在配置文件中定义拦截器并引用它
<package name="renthouse" extends="struts-default">
<interceptors>
<!--定义权限验证拦截器-->
<interceptor name="myAuthorization"
class="interceptors.AuthorizationInterceptor">
</interceptor>
<!--定义拦截器栈-->
<interceptor-stack name="myStack">
<interceptor-ref name="defaultStack"/>
<interceptor-ref name="myAuthorization"/>
</interceptor-stack>
</interceptors>
<!-- 定义默认拦截器 -->
<default-interceptor-ref name="myStack"/>
//因为包含在默认拦截器内,所以Action中无需再引用权限拦截器
…
</package>
二、文件上传
1.单个文件上传
Commons-FileUpload组件
Commons是Apache开放源代码组织的一个Java子项目,其中的FileUpload是用来处理HTTP文件上传的子项目
Commons-FileUpload组件特点
使用简单:可以方便地嵌入到JSP文件中,编写少量代码即可完成文件的上传功能
能够全程控制上传内容
能够对上传文件的大小、类型进行控制
环境要求
commons-fileupload-xxx.jar
commons-io-xxx.jar
2.多个文件上传
表单设置
多个File控件
name属性相同
Action的修改
将三个属性的类型修改成数组类型
uploadContentType=控件名+ContentType
uploadFileName=控件名+FileName
//获取提交的多个文件
private File[] upload;
//封装上传文件的类型
private String[] uploadContentType;
//封装上传文件名称
private String[] uploadFileName;
表单设置:
<form action="upload" method="post" enctype="multipart/form-data">
<input type="file" name="fname"/>
<input type="file" name="fname"/>
<input type="file" name="fname"/>
<button>保存</button></form>
<s:file name="upload" label="选择文件"/>
public class Up {
// 三个属性,命名是有一定规则的
// 1.文件本身
private File[] fname;
// 2.文件类型
private String[] fnameContentType;
// 3.文件名
private String[] fnameFileName;
public String execute() {
System.out.println("开始上传!");
try {
for(int i = 0; i<fname.length;i++){
String path = ServletActionContext.getServletContext().getRealPath("/picture/"+fnameFileName[i]);
FileInputStream input = new FileInputStream(fname[i]);
FileOutputStream output = new FileOutputStream(path);
IOUtils.copy(input, output); //调用IOUtils工具类的copy方法
output.close();
input.close();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return "success";
}
//get()和set()方法
}
在struts.xml中配置:
<action name="upload" class=“controller.UploadAction">
<!--通过param参数设置保存目录的路径-->
<param name="savePath">/upload</param> //以参数方式指定保存路径
注:上传视频用FTP上传到服务器
三、下载文件
1.下载文件(可以下载电影)
stream结果类型:
stream结果类型将文件数据(通过InputStream获取)直接写入响应流相关参数的配置
名称 作用
contentType 设置发送到浏览器的MIME类型
contentLength 设置文件的大小
contentDisposition 设置响应的HTTP头信息中的Content-Disposition参数的值
inputName 指定Action中提供的inputStream类型的属性名称
bufferSize 设置读取和下载文件时的缓冲区大小
contentType类型:
文件类型 类型设置
Word application/msword
Execl application/vnd.ms-excel
PPT application/vnd.ms-powerpoint
图片 image/gif , image/bmp,image/jpeg
文本文件 text/plain
html网页 text/html
可执行文件 application/octet-stream
实现步骤
编写下载文件Action
获取InputStream输入流
配置Action
指定下载文件的类型、下载形式等
在struts.xml中配置:
<result type="stream">
<param name="contentType">application/octet-stream</param>
//通用设置,可执行文件
<param name="inputName">fileInputStream</param>
<param name="contentDisposition">attachment;filename="${fileName}"
</param>
//Attachement表示以附件形式下载Filename表示下载时显示的文件名称
<param name="bufferSize">4096</param>
</result>
Action类中编写:
// 两个属性
// 1.文件流
private InputStream fileInputStream;
// 2.下载的文件名称
private String fileName;
public String execute() {
// fielName = E:/spiderMan.exe
File f = new File(fileName);
this.fileName = f.getName();
try {
fileInputStream = new FileInputStream(f);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
return "success";
}
//get和set方法
index.jsp页面:
<a href="xia?fileName=E:/课件/JavaFrames笔记/Struts2_3/downloadVideo/1.exe">点击下载:1.exe
</a> //传参文件名
一、数据类型转换
a)原始类型和封装类型
原始类型 : 四类八种
封装类型 : 除了原始类型.
在web应用程序中.url中传递的参数,永远是字符串类型.
其实将类型转换的是struts平台中内置的类型转换器.
b)多值类型
当数组在页面指明了下标,那么必须在action中创建时指明长度.
List很像数组,但要注意:
不需要初始化任何一个list
如果没有泛型的说明,list默认为List<String>
如果指明泛型,struts2会自动创建与泛型想匹配的对象并注入
Struts 2内置类型转换器:
内置类型转换器 说 明
String 将int、long、double、boolean、String类型的数组或 者java.util.Date类型转换为字符串
boolean/Boolean 在字符串和布尔值之间进行转换
char/Character 在字符串和字符之间进行转换
date 在字符串和日期类型之间进行转换。具体输入输出格 式与当前的Locale相关
数组和集合 在字符串数组和数组对象、集合对象间进行转换
c)自定义类型转换器
页面传来的是string,接收会自动转为相应的数据类型.
页面传来一个时间格式,而我自定义时间格式.
自定义类型转换器要:
继承StrutsTypeConverter抽象类
重写以下两个方法:
将一个或多个字符串值转换为指定的类型
public Object convertFromString(Map context, String[] value, Class toType)
将指定对象转化为字符串
public String convertToString(Map context, Object object)
处理类型转换错误:
向用户输出类型转换错误的前提条件:
1>Action要继承ActionSupport类
2>配置input结果视图
3>页面使用Struts 2表单标签或<s:fielderror>标签
Struts 2表单标签内嵌了输出错误信息功能
普通HTML标签需使用<s:fielderror>标签输出转换错误
步骤:
1.在src目录创建xwork-conversion.properties
转换类全名=类型转换器类全名
java.util.Date=time.MyTime
2.Action类必须继承ActionSupport
3.在src下建立资源文件. message.properties
xwork.default.invalid.fieldvalue=error!
修改所有类型的转换错误信息
4.定制指定字段的类型转换错误信息,与Action同包下建立资源文件,ActionName.properties
invalid.fieldvalue.time=日期转换错误!
5.在Struts.xml中配置资源显示
<constant name="struts.custom.i18n.resources" value="message"/>
SimpleDateFormat[] sdf = {
new SimpleDateFormat("yyyy-MM-dd"),
new SimpleDateFormat("yyyy=MM=dd"),
new SimpleDateFormat("yyyy年MM月dd日")
};
sdf[i].parse(str); //转换为Date对象
String s =new SimpleDateFormat().format((Date)o); //转换为字符串
二、OGNL表达式
Object-Graph Navigation Language : 对象图导航语言
开源项目,取代页面中的脚本,简化数据的访问.
同EL表达式类似,但功能更为强大.
java的方式: user.getSchool().getGrade().getBanji();
ognl的方式: user.school.grade.banji
值栈(ValueStack)?
由Struts2框架创建的存储区域,具有栈的特点
Action的实例会被存放到值栈中
Struts2将值栈作为OGNL上下文根对象
OGNL访问值栈:
按照从上到下的顺序
靠近栈顶的同名属性会被读取
ognl访问:
a)访问javabean
user没有new,address也没有new. 都是由struts帮我们创建。
自动创建对象的前提:
1.必须要符合javabean规范的类.必须提供无参构造.
2.属性必须get和set.否则赋不上值
b)访问集合对象
<s:textfield label="所在城市" name="list2[0].address.city"/>
<s:property value="list2.empty"/><!-- false -->判断集合是否是空的,空返回true
<s:property value="list2.size"/> <!-- 2 --> 获得集合的长度
遍历Map集合:
<s:iterator value="map">
<s:property value="key"/> <!-- A B 键 --> 获得键
<s:property value="value.name"/> 获得值的指定属性
<s:property value="value.address.guo"/>
</s:iterator>
ActionContext对象中有值栈对象与非值栈对象:
值栈中内容可以直接访问,访问非值栈对象需添加#前缀
非值栈包括:
1>request
2>session
3>application
4>parameters
5>attr 全域查找
访问ActionContext的上下文
request.getParameter(“user”) == #parameters.userxx
request.getAttribute(“user”) == #request.userxx==#request['userxxx']
session.getAttribute(“user”) == #session.userxx
application.getAttribute(“user”) == #application.userxx
<!-- 标签访问四大域,使用ognl的一种写法 -->
<s:property value="#session.userxx"/>
<s:property value="#session['userxx']"/>
struts2扩展标签:
1>时间格式标签
<s:date name="now" format="yyyy_MM_dd hh:mm:ss"/>
<s:date format="format" nice="true|false" name="name" id="id">
……
</s:date>
format属性:指定格式进行日期格式化
nice属性:该属性只有true和false两个值,用于指定是否输出指定日期与当前时间的时差,默认是false
name属性:表示当前需要格式化的日期
id属性:表示引用该元素的id值
2>超链接
定义超链接
<s:url id="dizhi" action="test">
<s:param name="name">张三</s:param>
<s:param name="age">22</s:param>
</s:url>
<!-- url直接写,会被识别为字符串 -->
<!-- %{dizhi} -->
<s:a href="%{dizhi}">去ok</s:a>
action属性:表示指定生成URL的地址,
<s:param />表示需要传递的参数信息
name属性:表示传递的参数名称
value属性:表示传递参数所具有的值
使用超链接
<!-- url直接写,会被识别为字符串 -->
<!-- %{dizhi} -->
<s:a href="%{dizhi}">去ok</s:a>
使用OGNL标签的时候,标签中的值是具有数据类型的:
<s:url>的value是字符串类型
<s:property>的value是object类型
所以对于字符串类型的属性,必须使用%{ } , 否则会被看成是字符串.
private Date time;
<s:property value="time" />
<s:property>标签用于输出指定对象的属性值
3>调试标签
查看当前struts运行环境中的所有对象值的情况.
<s:debug/>
ActionContext的组成:
值栈-ValueStack
非值栈-Stack Context
4>
<s:set name="age" value="10" scope="request"/>
<s:set>标签将一个值赋给指定范围的变量
一、验证框架
1>验证方法
1>>validate()方法
数据校验,需要的步骤:
1.继承ActionSupport
2.重写 public void validate(){}
3.<result name=”input”>指定校验失败后返回的页面
注:execute() ,validate(), validateXxx()方法为系统自动调用
2>>validateXxx()方法
在struts.xml中配置:
<action name="login" class="controller.UserAction" method="login">
调用Action的任务方法(execute())时,validate()会默认执行,可用于验证一些通用规则。如果想为某个方法指定一些独有的验证规则,采用validateXxx()方法
//有一些特殊的验证规则,封闭到一个方法里。
public void validateLogin(){ //Xxx=指定处理请求的方法
if(user.getUsername().length()<3||user.getUsername().length()>=10)
addFieldError("2","账号必须在3~10位之间!");
//参数一属性名,参数二错误信息
}
public void validate(){ }
public String login() { //validateLogin()名称对应login()方法 }
三个方法的执行顺序:
validateLogin() ---> validate() ---> login()
当 validateLogin() 与 validate() 校验不过时,不会执行login()。
2>验证框架 : 一个独立的文件
使用validate()或者validateXxx()方法时:
当验证规则复杂时,实现过程繁琐,导致Action类中代码臃肿,不符合java设计思路
因此,要使用Struts 2提供的验证框架
验证框架中的校验器:
校验器类型 校验器名称 说 明
必填校验器 required字段不能为空
必填字符串校验器 requiredstring 字段值不能为空长度要大于0
整数校验器 int 字段的整数值的范围
字符串长度校验器 stringlength字段值的长度的范围
正则表达式校验器 regex字段是否匹配一个正则表达式
字段表达式校验器 fieldexpression字段必须满足一个逻辑表达式
日期校验器 date 日期输入是否在指定的范围内
双精度校验器 double 字段值必须是双精度类型
使用验证框架的步骤:
1)编写Action,需要继承ActionSupport类
2)配置Action
<result name=”input”>指定校验失败后返回的页面
3)编写表单
4)编写验证文件和校验规则
验证文件类型为xml格式,存放在与Action同一包中
命名规则有两种方式
1>ClassName-validation.xml //ClassName: Action类名
2>UserAction-user_regist-validation.xml(只校验regist方法,必须为此格式)
UserAction-validation.xml内容:
<validators> //验证文件的根节点
<field name="user.username">
<field-validator type="requiredstring">
<param name="trim">true</param>
<message>用户名不能为空!哈哈</message>
</field-validator>
<field-validator type="stringlength">
<param name="minLength">5</param>
<param name="maxLength">10</param>
<message>用户名长度在${minLength}和${maxLength}之间~@~!</message>
</field-validator>
</field>
<field name="user.password2">
<field-validator type="fieldexpression">
<param name="expression">password1==password2</param>
<message>两次密码必须相同~!</message>
</field-validator>
</field>
<field name="user.telephone">
<field-validator type="regex">
<param name="expression">^\d{3,4}-\d{7,8}$</param>
<message>座机格式错误</message>
</field-validator>
</field>
</validators>
注意:验证文件的dtd的导入.如果报错,可以改为绝对路径
二、国际化
国际化(Internationalization:I18N)?
使程序在不做任何修改的情况下,可以在不同国家或地区和不同语言环境下,按照当地的语言和格式习惯显示字符
本地化(Localization:L10N)
一个国际化的程序,当它运行在本地机器时,能够根据本地机器的语言和地区设置显示相应字符
Java程序的国际化思路(struts的国际化,也是基于java的国际化.只不过是struts进行了二次封装):
将程序中的提示信息、错误信息等放在资源文件中,为不同国家/语言编写对应资源文件
资源文件由很多key-value对组成,key保持不变,value随国家/语言不同而不同
这些资源文件使用共同的基名,通过在基名后面添加语言代码、国家和地区代码来进行区分
且必须要翻译.用一个文件来做为翻译的资源.
常用资源文件:
资源文件名 说 明
ApplicationResources_en.properties 所有英文语言的资源
ApplicationResources_zh.properties 所有的中文语言的资源
ApplicationResources_zh_CN.properties 针对中国大陆的、中文语言的资源
ApplicationResources_zh_HK.properties 针对中国香港的、中文语言的资源
ApplicationResources.properties 默认资源文件,如果请求的资源文 件不存在,将调用它的资源进行显 示
使用国际化的步骤:
1>指定资源文件的基名及存储路径 (在struts.xml中配置)
<struts>
<constant name="struts.custom.i18n.resources" value="message"/>
<constant name="struts.i18n.encoding" value="UTF-8"/>(不常用)
</struts>
2>创建资源文件
存放位置:在src目录下,或者直接在WEB-INF/classes目录名称
china.properties
english.properties
3>实现JSP页面信息的国际化显示
将JSP页面中的所有中文在english.properties文件中替换掉
4>实现验证错误信息的国际化显示
编写资源文件
编写验证文件
编写Action
资源文件的范围:(不重要)
1>全局资源文件
所有包的所有Action类都可以访问
导致资源文件变得非常庞大臃肿,不便于维护
2>包范围资源文件
对应包下创建package_language_country.properties
处于该包及子包下的action才可以访问该文件
3>Action范围资源文件
命名格式ActionClassName_language_country.properties
只有单个Action可以访问
查找顺序:Action---package---全局
<validators>
<field name="user.username">
<field-validator type="requiredstring">
<param name="trim">true</param>
<message key="namenull"/> //通过key对应错误信息
</field-validator>
</validators>
三、分页查询(重要)
CREATE TABLE stu(
id INT(10) PRIMARY KEY AUTO_INCREMENT, //mySql设置主键自增
NAME VARCHAR(20)
);
SELECT * FROM stu;
INSERT INTO stu VALUES(DEFAULT,'张三');
sql语句:
oracle : rownum
mysql : limit
sqlserver : between...and...
String sql = "SELECT * from stu order by id limit ?,?";
//第一个参数指定第一个返回记录行的下标,第二个参数指定返回记录行的最大数目。初始记录行的下标是 0(而不是 1)
对象的注入
具体的见Struts2_5_test2。