struts2的拦截器机制:
核心脉络:
1、拦截器(Interceptor)介绍
2、拦截器的使用
3、拦截器原理
4、自定义拦截器
重点:
1、拦截器的使用
2、自定义拦截器的使用
难点:
1、拦截器的原理
1、拦截器介绍:
struts2提供了很多默认的拦截器,可以在struts-default.xml中找到
其中比较常用的几个有:
fileUpload | 提供文件上传功能 |
i18n | 记录用户选择的locale,国际化相关 |
modelDriven | 将实现了ModelDriven接口的Action中的getModel()方法返回值放到值栈中 |
params | 将请求参数设置到Action的属性中 |
servletConfig | 根据action类实现的接口,给action的相关属性赋值,访问servlet的api等,如:给实现了ServletRequestAware接口的action赋值HttpServletRequest对象。 |
staticParams | 从struts.xml文件中将<action>中的<param>中的内容设置到对应的Action中。 |
timer | 输出Action执行的时间 |
token | 通过Token来避免重复访问 |
validation | 使用action-validation.xml文件中定义的内容校验提交的数据 |
2、拦截器的使用:
在struts2中拦截器可以单独使用,也可以打包使用,打包使用的就是拦截器栈。在struts-default.xml中已经设置了默认的拦截器栈,也就是说在struts.xml中你什么都不需要做,就已经具备了这些功能(因为struts.xml中定义的package继承了struts-default)。
defaultStack的定义:
需要注意的是拦截器栈中的拦截器的顺序。越靠下的拦截器,请求时候的执行时机越靠近action。
OgnlAction.java
package com.bjsxt.demo.action;
import com.opensymphony.xwork2.ActionSupport;
public class OgnlAction extends ActionSupport {
private static final long serialVersionUID = 4567480981564608981L;
private String name;
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public static long getSerialversionuid() { return serialVersionUID; }
@Override public String execute() throws Exception { System.out.println("name = " + name); System.out.println("你已经访问了OgnlAction的execute方法"); return SUCCESS; }
}
|
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>
<constant name="struts.enable.DynamicMethodInvocation" value="false" /> <constant name="struts.devMode" value="true" />
<package name="ognl" namespace="/ognl" extends="struts-default"> <action name="ognlAction" class="com.bjsxt.demo.action.OgnlAction"> <!-- 配置系统内置的timer拦截器 --> <!-- 此时系统就不在为当前action提供默认拦截器栈了,只有timer拦截器了 --> <interceptor-ref name="timer" /> <result> /success.jsp </result> </action> </package> </struts> |
index.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!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> <a href="ognl/ognlAction.action?name=zhangsan">ognl/ognlAction.action?name=zhangsan</a><br> </body> </html> |
success.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!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> 跳转成功 </body> </html> |
用法一:在action定义中使用interceptor-ref标签添加需要的拦截器,此时系统将不再提供默认的拦截器栈。
系统配置:
运行结果:
用法二:在package中使用interceptors标签自定义拦截器栈,在系统默认拦截器栈的下面添加timer拦截器,然后在action中使用interceptor-ref引用这个拦截器栈。此时就在defaultStack的基础上添加了新的功能:timer拦截器计时。此时timer计时是比较准确的,因为timer是距离action执行最近的一个拦截器。
系统配置:
运行结果:
用法三:提供自定义的默认拦截器栈:
系统配置:
运行结果:
3、struts2拦截器设计原理
struts2使用责任链模式,实现了功能的可插拔设计
责任链模式是一种设计模式。在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。发出这个请求的客户端并不知道链上的哪一个对象最终处理这个请求,这使得系统可以在不影响客户端的情况下动态地重新组织和分配责任。
ActionInvocation.java
package com.bjsxt.demo.struts2;
import java.util.Iterator; import java.util.List;
public class ActionInvocation {
private Action action; private Iterator<Interceptor> iterator;
public Action getAction() { return action; }
public void setAction(Action action) { this.action = action; }
public void setInterceptors(List<Interceptor> interceptors) { this.iterator = interceptors.iterator(); }
public String invoke() { if (iterator.hasNext()) { Interceptor interceptor = iterator.next(); return interceptor.intercept(this); } else { return action.execute(); } }
}
|
Interceptor.java
package com.bjsxt.demo.struts2;
public interface Interceptor {
String intercept(ActionInvocation invocation);
}
|
Action.java
package com.bjsxt.demo.struts2;
public interface Action {
public String execute();
} |
HelloAction.java
package com.bjsxt.demo.action;
import com.bjsxt.demo.struts2.Action;
public class HelloAction implements Action {
@Override public String execute() { System.out.println("你访问了HelloAction的execute方法"); return "success"; } } |
FirstInterceptor.java
package com.bjsxt.demo.interceptor;
import com.bjsxt.demo.struts2.ActionInvocation; import com.bjsxt.demo.struts2.Interceptor;
public class FirstInterceptor implements Interceptor {
@Override public String intercept(ActionInvocation invocation) { System.out.println("interceptor------1------BEGIN"); String result = invocation.invoke(); System.out.println("interceptor------1------END"); return result; }
}
|
SecondInterceptor.java
package com.bjsxt.demo.interceptor;
import com.bjsxt.demo.struts2.ActionInvocation; import com.bjsxt.demo.struts2.Interceptor;
public class SecondInterceptor implements Interceptor {
@Override public String intercept(ActionInvocation invocation) { System.out.println("interceptor------2------BEGIN"); String result = invocation.invoke(); System.out.println("interceptor------2------END"); return result; } } |
ThirdInterceptor.java
package com.bjsxt.demo.interceptor;
import com.bjsxt.demo.struts2.ActionInvocation; import com.bjsxt.demo.struts2.Interceptor;
public class ThirdInterceptor implements Interceptor {
@Override public String intercept(ActionInvocation invocation) { System.out.println("interceptor------3------BEGIN"); String result = invocation.invoke(); System.out.println("interceptor------3------END"); return result; }
}
|
Test.java
package com.bjsxt.demo.test;
import java.util.ArrayList; import java.util.List;
import com.bjsxt.demo.action.HelloAction; import com.bjsxt.demo.interceptor.FirstInterceptor; import com.bjsxt.demo.interceptor.SecondInterceptor; import com.bjsxt.demo.interceptor.ThirdInterceptor; import com.bjsxt.demo.struts2.ActionInvocation; import com.bjsxt.demo.struts2.Interceptor;
public class Test {
public static void main(String[] args) { HelloAction helloAction = new HelloAction(); FirstInterceptor interceptor1 = new FirstInterceptor(); SecondInterceptor interceptor2 = new SecondInterceptor(); ThirdInterceptor interceptor3 = new ThirdInterceptor();
List<Interceptor> interceptors = new ArrayList<Interceptor>(); interceptors.add(interceptor1); interceptors.add(interceptor2); interceptors.add(interceptor3);
ActionInvocation actionInvocation = new ActionInvocation(); actionInvocation.setAction(helloAction); actionInvocation.setInterceptors(interceptors);
String result = actionInvocation.invoke();
System.out.println("最终结果:" + result);
}
}
|
责任链:
讲解Struts2 值栈
1、 什么是栈
栈(Stack)是限制仅在表的一端进行插入和删除运算的线性表。
a、通常称插入、删除的这一端为栈顶 (Top),另一端称为栈底 (Bottom)。
b、当表中没有元素时称为空栈。
c、栈为后进先出(Last In First Out,LIFO)的线性表,简称为 LIFO 表。
如:java.util.Stack
package com.bjsxt.test;
import java.util.Stack;
public class StackTest {
public static void main(String[] args) { Stack<String> stack = new Stack<String>(); boolean isEmpty = stack.isEmpty(); System.out.println(isEmpty ? "栈为空" : "栈不为空");
//压栈操作 stack.push("项目1"); stack.push("项目2"); stack.push("项目3"); stack.push("项目4"); stack.push("项目5");
System.out.println("============数据入栈完毕");
while (!stack.isEmpty()) { //出栈操作 System.out.println(stack.pop()); } } } |
2、 什么是值栈(ValueStack)
要说清ValueStack,得从OGNL开始。
OGNL(Object Graph Navigation Language)对象导航图语言。
所谓对象图,即以任意一个对象为根,通过OGNL可以访问与这个对象关联的其它对象。
如:
Dog.java
package com.bjsxt.demo.bean;
public class Dog {
private String dogName;
public String getDogName() { return dogName; }
public void setDogName(String dogName) { this.dogName = dogName; }
}
|
House.java
package com.bjsxt.demo.bean;
public class House { private String houseAddress; private Dog dog; public String getHouseAddress() { return houseAddress; } public void setHouseAddress(String houseAddress) { this.houseAddress = houseAddress; } public Dog getDog() { return dog; } public void setDog(Dog dog) { this.dog = dog; } } |
Person.java
package com.bjsxt.demo.bean;
public class Person {
private String username; private House house;
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public House getHouse() { return house; }
public void setHouse(House house) { this.house = house; }
}
|
Test.java
package com.bjsxt.demo.test;
import ognl.Ognl; import ognl.OgnlException;
import com.bjsxt.demo.bean.Dog; import com.bjsxt.demo.bean.House; import com.bjsxt.demo.bean.Person;
public class Test {
public static void main(String[] args) throws OgnlException {
Dog dog = new Dog(); dog.setDogName("小黄");
House house = new House(); house.setDog(dog); house.setHouseAddress("北京市海淀区西三旗");
Person person = new Person(); person.setHouse(house); person.setUsername("张三");
String dogName = person.getHouse().getDog().getDogName(); System.out.println(dogName);
//getValue的第一个参数就是一个OGNL表达式 Object obj = Ognl.getValue("house.dog.dogName", person); System.out.println(obj);
//通过OGNL表达式给对象赋值 Ognl.setValue("house.dog.dogName", person, "二黄");
obj = Ognl.getValue("house.dog.dogName", person); System.out.println(obj);
//使用#root代表root对象。这里root对象显然是person对象 obj = Ognl.getValue("#root.house.dog.dogName", person); System.out.println(obj); }
}
|
上面Test类中,描述了如何通过Person对象,导航到House对象,并通过House对象导航到Dog对象。如果以Person为根,可以得到下面的图:
Person(Root) ——username ——house ——houseAddress ——dog ——dogName |
对象图的导航,就是通过getter方法进行导航。
在OGNL表达式中,有可能需要访问多个毫不相干的对象,我们需要给OGNL传递一个Map类型的对象,把表达式中的对象放到Map中即可,这个Map对象,称为context。
要访问context中的对象,需要使用”#”的语法规则。
package com.bjsxt.demo.test;
import java.util.HashMap; import java.util.Map;
import com.bjsxt.demo.bean.Dog; import com.bjsxt.demo.bean.House; import com.bjsxt.demo.bean.Person;
import ognl.Ognl; import ognl.OgnlException;
public class Test2 {
public static void main(String[] args) throws OgnlException {
Dog dog = new Dog(); dog.setDogName("小黄");
House house = new House(); house.setHouseAddress("海淀区"); house.setDog(dog);
Person person = new Person(); person.setUsername("zhangsan"); person.setHouse(house);
Person person01 = new Person(); person01.setUsername("张三右舍");
Person person02 = new Person(); person02.setUsername("张三左邻");
Map<String, Object> context = new HashMap<String, Object>(); context.put("person01", person01); context.put("person02", person02);
//获取context元素的值 Object obj = Ognl.getValue("#person01.username + '===' + #person02.username", context, person); System.out.println(obj);
Ognl.setValue("#person01.username", context, person, "老李");
//给context的元素赋值 obj = Ognl.getValue("#person01.username", context, person); System.out.println(obj);
}
}
|
在struts2中,OGNL上下文(context)就是ActionContext
| |--application | |--session context map---| |--value stack(root) | |--action (the current action) | |--request | |--parameters | |--attr (searches page, request, session, then application scopes) |
|
OGNL上下文的根对象就是值栈。
OGNL转义:%{ com.opensymphony.xwork2.ActionContext.locale}
3、 值栈的作用
OGNL的作用是提供一个简单的语法将Struts2标签与特定的java端属性绑定起来,用来将数据移入、移出框架。它帮助将数据从请求参数移动到动作的javabean属性,并且帮助将数据从这些属性移动到呈现的HTML页面。
OGNL如何融入框架:
params拦截器负责将请求数据放到ValueStack上的对应对象属性上。
OGNL表达式的使用:
1、 struts标签
2、 参数提交
3、 页面结果的获取
4、 struts配置文件中使用。
struts2的类型转换器
1、通过继承StrutsTypeConverter来实现自定义的类型转换器,转换器可以将一个字符串转换为一个对象,也可以将一个对象转换为字符串。为了让struts知道这是一个类型转换错误,最好在发生异常的时候抛出XWorkException或者TypeConversionException更好。
package com.bjsxt.demo.converter;
import java.util.Map;
import org.apache.struts2.util.StrutsTypeConverter;
public class MyConverter extends StrutsTypeConverter {
@Override public Object convertFromString(Map context, String[] values, Class toClass) { // TODO Auto-generated method stub return null; }
@Override public String convertToString(Map context, Object o) { // TODO Auto-generated method stub return null; }
}
|
2、给一个Action添加类型转换器
在Action所在的包下,添加actionName-conversion.properties
#syntax:<propertyName> = <converterClassName>
#point = com.acme.PointConverter
#person.phoneNumber= com.acme.PhoneNumberConverter
3、给一个bean或者model添加类型转换器
在bean或model所在的包下,添加beanName-conversion.properties
# syntax:<propertyName>=<converterClassName>
#amount=com.acme.converters.MyCustomBigDecimalConverter
userId=com.bjsxt.demo.converter.IdConverter
username=com.bjsxt.demo.converter.UsernameConverter
password=com.bjsxt.demo.converter.PasswordConverter
birthday=com.bjsxt.demo.converter.BirthdayConverter
4、给应用添加类型转换器
在classpath下添加xwork-conversion.properties
语法:
# syntax: <type> =<converterClassName>
#java.math.BigDecimal = com.acme.MyBigDecimalConverter