一、 OGNL
1、OGNL,Object-Graph Navigation Language,对象图导航语言,比EL表达式强大很多倍的语言;
EL:从域对象中获得数据,从EL的11个对象中获取;
OGNL:调用对象的方法,获取Struts2的值栈的数据,OGNL其实是第三方的表达式语言。
2、OGNL在java环境下的使用
a、OGNL调用对象的方法:
@Test
public void demo1() throws OgnlException {
OgnlContext context = new OgnlContext();
Object root = context.getRoot();
Object obj = Ognl.getValue("'helloworld'.length()", context, root);
System.out.println(obj);
}
输出结果:10
b、OGNL访问对象的静态方法:@类名@方法名
@Test
public void demo2() throws OgnlException {
OgnlContext context = new OgnlContext();
Object root = context.getRoot();
Object obj = Ognl.getValue("@java.lang.Math@random()", context, root);
System.out.println(obj);
}
输出结果:0.920223035260102(0-1的随机数)
c、访问Root中的数据 不需要加#
@Test
public void demo3() throws OgnlException {
OgnlContext context = new OgnlContext();
User user = new User("张三", "123456");
context.setRoot(user);
Object root = context.getRoot();
Object userName = Ognl.getValue("userName", context, root);
Object password = Ognl.getValue("password", context, root);
System.out.println("用户名:" + userName + " " + "密码:" + password);
}
输出结果:用户名:张三 密码:123456
d、访问context中的数据 需要加#
@Test
public void demo4() throws OgnlException {
OgnlContext context = new OgnlContext();
Object root = context.getRoot();
context.put("name", "张三");
Object name = Ognl.getValue("#name", context, root);
System.out.println("用户名:" + name);
}
输出结果:用户名:张三
public class User{
private String userName;
private String password;
public User() {
super();
}
public User(String userName, String password) {
super();
this.userName = userName;
this.password = password;
}
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;
}
}
二、值栈
Struts2将XWork对OGNL的扩展这一套机制封装起来,这个对象就被叫做ValueStack。
1、什么是值栈
ValueStack是Struts2的一个接口,字面意义为值栈,OgnlValueStack是ValueStack的实
现类,客户端发起一个请求Struts2架构会创建一个action实例同时创建一个OgnlValueStack值
栈实例,OgnlValueStack贯穿整个action的生命周期,Struts2中使用OGNL将请求Action的参数
封装为对象存储到值栈中,并通过OGNL表达式读取值栈中的对象属性值。
ValueStack其实类似于一个数据中转站,(Struts2的框架当中的数据就都保存到了ValueStack中)。
2、值栈的内部结构
ValueStack中有两个主要的区域:
root区域:其实就是一个ArrayList,里面一般放置对象
context区域:其实就是一个Map,里面放置的是Web开发的常用的对象数据的引用(root、request、session、application、
parameters、attr)(attr指的就是从小到大范围去搜索数据)。
注:获取root区域的数据不需要加#;
获取context区域的数据需要加#。
所说的操作值栈,通常指的就是操作ValueStack中的root区域。
3、值栈与ActionContext的关系
ServletContext:Servlet的上下文
ActionContext:Action的上下文
通过源码查看到:当请求过来的时候,执行过滤器中doFilter方法,在这个方法中创建ActionContext,在创建ActionContext的
过程中,创建ValueStack对象,将ValueStack对象传递给ActionContext对象,所以可以通过ActionContext获取值栈的对象。
ActionContext对象之所以能够访问Servlet的API(访问时域对象的数据),是因为在其内部有值栈的引用。
4、获得值栈
a:通过ActionContext对象获取值栈
b:在Struts2内部,将值栈存入到request中一份,所以可以通过request对象获得
public class ValueStackDemo extends ActionSupport {
@Override
public String execute() {
ValueStack valueStack1 = ActionContext.getContext().getValueStack();
ValueStack valueStack2 = (ValueStack) ServletActionContext.getRequest()
.getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
System.out.println(valueStack1 == valueStack2);
return NONE;
}
}
输出结果:true
5、操作值栈---向值栈中存入数据
第一种:在Action中提供属性的get方法的方式
默认情况下,将Action对象压入到值栈,Action的属性也会在值栈中(继承自ActionSupport)。
public class ValueStackDemo extends ActionSupport {
private User user;
public User getUser() {
return user;
}
@Override
public String execute() {
user = new User("张三", "123456");
return SUCCESS;
}
}
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="/struts-tags" prefix="s"%>
<!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>
<s:debug></s:debug>
<!--方式一的获取:利用Action在值栈的特性 -->
<s:property value="user.userName"/>
<s:property value="user.password"/>
</body>
</html>
第二种:调用值栈中的方法实现
push(Object obj):针对对象
set(String key,Object obj):针对集合
public class ValueStackDemo4 extends ActionSupport {
@Override
public String execute() {
ValueStack valueStack = ActionContext.getContext().getValueStack();
User user = new User("李四", "12");
valueStack.push(user);
valueStack.set("name", "王五");
List<User> list = new ArrayList<User>();
list.add(new User("aaa", "111"));
list.add(new User("bbb", "222"));
list.add(new User("ccc", "333"));
ActionContext.getContext().getValueStack().set("list", list);
ServletActionContext.getRequest().setAttribute("name", "request张三");
ServletActionContext.getRequest().getSession().setAttribute("name", "session李四");
ServletActionContext.getServletContext().setAttribute("name", "application王五");
return SUCCESS;
}
}
<!--方式二的获取:调用值栈中的方法实现 push/set 方法 -->
<s:property value="userName" />
<s:property value="password" />
<s:property value="name" />
<!--获取root中的数据 -->
<s:property value="list[0].userName" />
<s:property value="list[0].password" />
<br />
<s:property value="list[1].userName" />
<s:property value="list[1].password" />
<br />
<s:property value="list[2].userName" />
<s:property value="list[2].password" />
<br />
<s:iterator value="list">
<s:property value="userName" />
<s:property value="password" />
</s:iterator>
<hr>
<!--获取context中的数据 -->
<s:property value="#request.name" />
<br />
<s:property value="#session.name" />
<br />
<s:property value="#application.name" />
<br />
<s:property value="#attr.name" />
<br />
<!-- parameters获取的是页面传过来的值,在valueStackDemo5.action后+“?id=5”则获取的值是5,并打印在页面上 -->
<s:property value="#parameters.id" />
6、EL也可以访问值栈数据
因为Struts2的框架的底层对request.getAttribute(String name)的加强。
三、OGNL中的特殊字符
1、#号
1、获取context的数据
<h3>获取context的数据</h3>
<%
request.setAttribute("name", "张三");
%>
<s:property value="#request.name" />
2、构建Map集合
<h3>构建Map集合</h3>
<s:iterator var="i" value="{'aa','bb','cc'}">
<s:property value="i" />--<s:property value="#i" />
<br />
</s:iterator>
<hr>
<s:iterator var="entry" value="#{'aa':'11','bb':'22','cc':'33' }">
<s:property value="key" />--<s:property value="value" />
<br />
<s:property value="#entry.key" />--<s:property value="#entry.value" />
<br />
</s:iterator>
<hr>
<!-- 单选框 -->
性别:
<input type="radio" name="sex1" value="男">男
<input type="radio" name="sex1" value="女">女
<br />
<s:radio list="{'男','女'}" name="sex2" label="性别"></s:radio>
<hr>
性别:
<input type="radio" name="sex3" value="1">男
<input type="radio" name="sex3" value="2">女
<br />
<s:radio list="#{ '1':'男','2':'女' }" name="sex4" label="性别"></s:radio>
2、%号
1、强制解析OGNL
在不属于<s:property />的标签获取解析OGNL的权利
<h1>%号的用法</h1>
<%
request.setAttribute("name", "马东");
%>
姓名:
<s:textfield name="name" value="%{ #request.name}"></s:textfield>
2、强制不解析OGNL
在<s:property />标签中也可放弃解析OGNL的权利
<h1>%号的用法</h1>
<%
request.setAttribute("name", "马东");
%>
姓名:
<s:property value="%{'#request.name'}" />
3、$号
在配置文件中使用OGNL
属性文件:
国际化地方:
message_zh_CN.properties
user.login=登录
user.welcome=欢迎,${#session.user.username}
message_en_US.properties
user.login=login
user.welcome=Welcome,${#session.user.username}
XML文件:
文件下载:
<action name="download" class="xxx.ActionDemo">
<result type="stream">
<param name="Content-Type">文件类型</param>
<param name="Content-Disposition">attachment;filename=${文件名}</param>
</result>
</action>
四、拦截器
1、什么是拦截器
Interceptor:拦截器,起到拦截Action的作用。
Filter:过滤器,过滤从客户端向服务器发送的请求。
Interceptor:拦截器,拦截是客户端对Action的访问。更细粒度化的拦截。(拦截Action中的具体的方法)。
Struts2框架核心的功能都是依赖拦截器实现。
2、Struts2的执行流程
客户端向服务器发送一个Action的请求,执行核心过滤器(doFilter)方法。在这个方法中,调用executeAction()方法,
在这个方法内部调用dispatcher.serviceAction();在这个方法内部创建一个Action代理,最终执行的是Action代理中的execute(),
在代理中执行的execute方法中调用ActionInvocation的invoke方法。在这个方法内部递归执行一组拦截器(完成部分功能),
如果没有下一个拦截器,就会执行目标Action,根据Action的返回的结果进行页面跳转。
3、拦截器入门
1、编写拦截器类
编写一个类实现Interceptor接口或者继承AbstractInterceptor类。
public class InterceptorDemo1 extends AbstractInterceptor {
@Override
public String intercept(ActionInvocation invocation) throws Exception {
System.out.println("InterceptorDemo1执行了...");
String obj = invocation.invoke();
System.out.println("InterceptorDemo1执行结束了...");
return obj;
}
}
public class InterceptorDemo2 extends AbstractInterceptor {
@Override
public String intercept(ActionInvocation invocation) throws Exception {
System.out.println("InterceptorDemo2执行了...");
String obj = invocation.invoke();
System.out.println("InterceptorDemo2执行结束了...");
return obj;
}
}
2、对拦截器进行配置
定义拦截器进行配置
<package name="demo1" extends="struts-default" namespace="/">
<!--第一种方式:定义拦截器 -->
<interceptors>
<interceptor name="interceptorDemo1" class="interceptor.InterceptorDemo1"></interceptor>
<interceptor name="interceptorDemo2" class="interceptor.InterceptorDemo2"></interceptor>
</interceptors>
<action name="actionDemo1" class="action.ActionDemo1">
<result>/demo1/demo1.jsp</result>
</action>
<!--第一种方式引入拦截器 -->
<interceptor-ref name="interceptorDemo1"></interceptor-ref>
<interceptor-ref name="interceptorDemo2"></interceptor-ref>
<!-- 一旦引入自定义拦截器,默认拦截器栈的拦截器就不执行了,这里手动配置 -->
<interceptor-ref name="defaultStack"></interceptor-ref>
</package>
定义一个拦截器栈的方式
<!--第二种方式:定义一个拦截器栈 -->
<interceptors>
<interceptor name="interceptorDemo1"
class="interceptor.InterceptorDemo1"></interceptor>
<interceptor name="interceptorDemo2"
class="interceptor.InterceptorDemo2"></interceptor>
<!-- 定义拦截器栈 -->
<interceptor-stack name="myStack">
<interceptor-ref name="defaultStack"></interceptor-ref>
<interceptor-ref name="interceptorDemo1"></interceptor-ref>
<interceptor-ref name="interceptorDemo2"></interceptor-ref>
</interceptor-stack>
</interceptors>
<action name="actionDemo1" class="com.itheima.web.action.ActionDemo1">
<result>/demo1/demo1.jsp</result>
<interceptor-ref name="myStack"></interceptor-ref>
</action>
public class ActionDemo1 extends ActionSupport {
@Override
public String execute() throws Exception {
System.out.println("ActionDemo1已经执行了.............");
return super.execute();
}
}
五、Struts2标签库
1、通用标签
1、判断标签
<s:if/>、<s:else if/>、<s:else/>、<s:iterator/>
<s:set var="i" value="2" scope="request"></s:set>
<s:if test="#request.i>3">
i 大于3
</s:if>
<s:elseif test="#request.i=3">
i 等于3
</s:elseif>
<s:else>
i 小于3
</s:else>
<s:iterator var="i" value="{'aa','bb','cc'}">
<s:property value="#i"/>
</s:iterator>
<s:iterator var="entry" value="#{'aaa':'111','bbb':'222','ccc':'333' }">
<s:property value="#entry.key"/>---<s:property value="#entry.value"/>
</s:iterator>
<s:iterator var="i" begin="1" end="10" step="1">
<s:property value="#i"/>
</s:iterator>
<s:iterator var="i" begin="1" end="100" step="10" status="status">
<s:if test="#status.count %2==0">
<font color="red"><s:property value="i"/></font>
</s:if>
<s:else>
<s:property value="i"/>
</s:else>
</s:iterator>
2、其他常用的标签
<s:property/>
<s:debug/>
<s:date/>--在页面上进行日期格式化
2、UI标签库(方便数据回显)
<h1>UI标签库</h1>
<h3>传统的表单</h3>
<form action="${pageContext.request.contextPath }/uiAction.action" method="post">
<input type="hidden" name="id">
用户名:<input type="text" name="name"> <br/>
密码:<input type="password" name="password"><br/>
年龄:<input type="text" name="age"><br/>
性别:<input type="radio" name="sex" value="男">男
<input type="radio" name="sex" value="女">女<br/>
籍贯:<select name="city">
<option value="">-请选择-</option>
<option value="北京">-北京-</option>
<option value="上海">-上海-</option>
<option value="深圳">-深圳-</option>
<option value="韩国">-韩国-</option>
</select><br/>
爱好:<input type="checkbox" name="hobby" value="basketball">篮球
<input type="checkbox" name="hobby" value="football">足球
<input type="checkbox" name="hobby" value="volleyball">排球
<input type="checkbox" name="hobby" value="pingpang">乒乓球<br/>
介绍:<textarea name="info" rows="3" cols="10">我是xxx</textarea><br/>
<input type="submit" value="提交">
</form>
<h3>UI标签的表单</h3>
<!--theme的属性可填xhtml(默认),或者simple,simple表示不使用UI标签库,和传统一样 -->
<s:form action="uiAction" namespace="/" method="post" theme="xhtml">
<s:hidden name="id" value=""></s:hidden>
<s:textfield name="name" label="用户名"></s:textfield>
<!--密码默认不回显 showPassword默认为"false",改为true时密码回显 -->
<s:password name="password" label="密码" showPassword="false"></s:password>
<s:textfield name="age" label="年龄"></s:textfield>
<s:radio list="{'男','女'}" name="sex" label="性别"></s:radio>
<s:select list="{'北京','上海','深圳','韩国'}" name="city" label="籍贯" headerKey="" headerValue="-请选择-"></s:select>
<s:checkboxlist list="#{'basketball':'篮球','football':'足球','volleyball':'排球','pingpang':'乒乓球' }" name="hobby" label="爱好"></s:checkboxlist>
<s:textarea name="info" cols="10" rows="3" label="介绍" value="我是yyy"></s:textarea>
<s:submit value="提交"></s:submit>
</s:form>
public class UIAction extends ActionSupport {
private static final long serialVersionUID = 1L;
private String name;
private String password;
private Integer age;
private String sex;
private String city;
private String[] hobby;
private String info;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String[] getHobby() {
return hobby;
}
public void setHobby(String[] hobby) {
this.hobby = hobby;
}
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
public static long getSerialversionuid() {
return serialVersionUID;
}
@Override
public String execute() {
System.out.println(hobby.toString());
System.out.println(info);
return NONE;
}
}
<action name="uiAction" class="action.UIAction">
<result name="input">/demo2/demo3.jsp</result>
</action>
六、数据校验
1、手动编码的方式
public class LoginAction extends ActionSupport {
private String username;
private String password;
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String execute() throws Exception {
return NONE;
}
@Override
public void validate() {
if (username == null || username.trim().length() == 0) {
this.addFieldError("username", "用户名不能为空!");
}
if (password == null || password.trim().length() == 0) {
this.addFieldError("password", "密码不能为空!");
}
}
public void validateExecute() {
if (password.length() < 6 || password.length() > 12) {
this.addFieldError("password", "密码需要在6-12位之间");
}
}
}
<style type="text/css">
.d1 {
color: black;
width: 150px;
height: 50px;
font-size: 20pt;
padding: 15px;
position: relative;
}
</style>
<body>
<div class="d1">登录页面</div>
<h3>
<s:fielderror></s:fielderror>
</h3>
<s:form action="checkAction" namespace="/" method="post">
<s:textfield name="username" label="用户名"></s:textfield>
<s:password name="password" label="密码"></s:password>
<s:submit value="提交"></s:submit>
</s:form>
<form action="${pageContext.request.contextPath}/checkAction.action"
method="post">
用户名:<input type="text" name="username"> 密码:<input
type="password" name="password"> <input type="submit"
value="提交">
</form>
</body>
<action name="checkAction" class="action.LoginAction">
<result name="input">/demo3/demo1.jsp</result>
</action>
2、XML配置的方式
public class LoginAction2 extends ActionSupport {
private static final long serialVersionUID = 1L;
private String username;
private String password;
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;
}
@Override
public String execute() throws Exception {
System.out.println(username);
System.out.println(password);
return NONE;
}
}
XML配置(校验一个字段)
<?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">
<!--该xml起名规则:Action类名-validation.xml -->
<validators>
<!-- name:要校验的字段名 -->
<field name="username">
<field-validator type="requiredstring">
<message>用户名不能为空!(XML)</message>
</field-validator>
</field>
<field name="password">
<field-validator type="requiredstring">
<message>密码不能为空!(XML)</message>
</field-validator>
</field>
</validators>
XML配置(校验方法)
<?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">
<!--该xml起名规则:Action类名-action的name-validation.xml -->
<!--针对一个方法校验 -->
<validators>
<field name="password">
<field-validator type="stringlength">
<param name="minLength">6</param>
<param name="maxLength">12</param>
<message>密码需要在6~12位之间!</message>
</field-validator>
</field>
</validators>
<action name="checkAction2" class="action.LoginAction2">
<result name="input">/demo3/demo2.jsp</result>
</action>
<style type="text/css">
.d1 {
color: black;
width: 150px;
height: 50px;
font-size: 20pt;
padding: 15px;
position: relative;
}
</style>
<body>
<div class="d1">登录页面</div>
<h3>
<s:fielderror></s:fielderror>
</h3>
<s:form action="checkAction" namespace="/" method="post">
<s:textfield name="username" label="用户名"></s:textfield>
<s:password name="password" label="密码"></s:password>
<s:submit value="提交"></s:submit>
</s:form>
<form action="${pageContext.request.contextPath}/checkAction2.action"
method="post">
用户名:<input type="text" name="username"> 密码:<input
type="password" name="password"> <input type="submit"
value="提交">
</form>
</body>
七、Struts2中的国际化(了解)
总结:Struts2基础知识已经总结完,Struts2框架是一款用来快速构建灵活、稳定的wed应用程序的框架,相比于servlet,大大减少了程序的代码,而且Struts2框架还提供了资源文件和标签,用来解决硬编码和提示错误信息。