上一章自己实现Struts2(三)实现ActionContext我已经实现好了Struts2的数据中心ActionContext,现在就要来实现一下action和拦截器的调用者ActionInvocation
再把Struts2框架流程图贴上来
大家可以发现拦截器链和action都是通过ActionInvocation来调用的,要实现这个功能,就必须准备好拦截器链和要被调用的action实例以及该action对应的配置信息(要通过配置信息知道调用什么方法),下面就来实现一下。
#ActionInvocation类成员变量介绍
现在我就来创建一个ActionInvocation
类,当然了,在Struts2中ActionInvocation是个接口,不过接口是为了方便扩展,我又没打算扩展,就直接定义类了。
package edu.jyu.invocation;
import java.util.Iterator;
import edu.jyu.config.ActionConfig;
import edu.jyu.context.ActionContext;
import edu.jyu.interceptor.Interceptor;
/**
* 拦截器链和action的调用类
*
* @author Jason
*/
public class ActionInvocation {
// 拦截器链
private Iterator<Interceptor> interceptors;
// 即将要被调用的action实例
private Object action;
// action配置信息
private ActionConfig config;
// 数据中心ActionContext
private ActionContext invocationContext;
}
其中Interceptor可以用Struts2自带的Interceptor
接口,不过在这里我自己写了一个,没什么差别的
package edu.jyu.interceptor;
import edu.jyu.invocation.ActionInvocation;
/**
* 拦截器接口
* @author Jason
*/
public interface Interceptor {
/**
* 执行拦截器初始化工作
*/
public void init();
/**
* 拦截功能,在请求前或请求后执行一些处理
* @param invocation
* @return
*/
public String intercept(ActionInvocation invocation);
/**
* 让拦截器做一些释放资源工作
*/
public void destory();
}
可以看到上面的ActionInvocation类定义了四个成员变量,现在我就简单介绍一下这个几个变量
- interceptors:很明显这就是拦截器链了,存放着需要执行的拦截器,用
Iterator
类型是为了在后面调用的时候方便取出来。 - action:要调用的action实例。
- config:要调用的action实例对应的配置信息,要根据配置信息去调用action的方法。
- invocationContext:本次请求的数据中心,为action实例准备好。
下面就要来做一下准备工作了,把这几个数据都准备好。
#准备工作
数据准备工作放在构造器中,代码如下
/**
* 准备各种需要的数据
*
* @param interceptorClassNames
* 存放着拦截器链每个拦截器的全限定类名
* @param config
* 需要调用的action的配置信息
* @param request
* @param response
*/
public ActionInvocation(List<String> interceptorClassNames, ActionConfig config, HttpServletRequest request,
HttpServletResponse response) {
// 准备Interceptor链
if (interceptorClassNames != null && interceptorClassNames.size() > 0) {
List<Interceptor> list = new ArrayList<Interceptor>();
for (String className : interceptorClassNames) {
try {
Interceptor interceptor = (Interceptor) Class.forName(className).newInstance();
interceptor.init();
list.add(interceptor);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("创建Interceptor失败:" + className);
}
}
interceptors = list.iterator();
}
// 准备action实例
this.config = config;
try {
action = Class.forName(config.getClassName()).newInstance();
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("创建Action失败:" + config.getClassName());
}
// 准备数据中心ActionContext
invocationContext = new ActionContext(request, response, action);
}
现在就来介绍一下准备工作。
一、准备拦截器链:构造方法会接受一个集合,里面存放着全部需要调用的拦截器的全限定类名,根据拦截器的全限定类名反射生成对应的实例,执行拦截器的初始化方法init
然后添加到另外一个集合中,最后调用interceptors = list.iterator();
赋值给interceptors,至此拦截器链就准备完毕了。
二、准备action实例:构造方法会接受一个ActionConfig
对象,这个对象存放的就是需要调用的action实例的配置信息,根据配置信息反射生成action实例。
三、准备ActionContext:这个没什么好讲的,直接new一个ActionContext对象将request、response和 action实例传进去,其中request和response对象是通过ActionInvocation的构造方法传进来的。
数据准备完成后就要开始调用了
#调用拦截器链和action
根据Struts2的框架流程图可以很清楚地看到,在调用action之前会先调用一系列的拦截器,在调用完action之后会逆序调用拦截器(其实就是执行Interceptor中intercept方法中的invocation.invoke()方法之后的代码),就是一个递归嘛,如果你会回溯法,对这个理解起来毫无压力。
不过需要注意的是,有可能拦截器直接返回一个字符串作为结果,这样action就不会被调用到了。
那么思路就是这样的:
-
如果拦截器链中还有其他的Interceptor,并且未被拦截器拦截(即未返回一个结果串),那么久调用下一个拦截器的拦截方法。
-
如果拦截器链中没有下一个拦截器了,那么根据action配置信息即
config
调用action实例的处理方法。 -
最后将结果串返回。
代码实现:
/**
* 调用拦截器链和action处理方法
* @param invocation
* @return
*/
public String invoke(ActionInvocation invocation) {
// 结果串
String result = null;
// 判断拦截器链是否还有下一个拦截器或者未被拦截器拦截(result不等于null的话说明已经有拦截器直接返回结果串了)
if (interceptors!=null && interceptors.hasNext() && result==null) {
// 有下一个拦截器,调用下一个拦截器的拦截方法
Interceptor next = interceptors.next();
result = next.intercept(invocation);
} else {
// 没有下一个拦截器,调用action实例的处理方法
// 获取处理的方法名
String methodName = config.getMethod();
try {
Method method = action.getClass().getMethod(methodName);
result = (String) method.invoke(action);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("action方法调用失败:" + methodName);
}
}
return result;
}
拦截器链和action的整个调用流程就是这样子的了。
#获取ActionContext
在Struts2中ActionInvocation类有个方法ActionContext getInvocationContext()
会返回一个ActionContext对象,其实这个对象就是我们上面定义的invocationContext
,为action实例的调用而准备的,我们也可以提供获取它的方法。
public ActionContext getInvocationContext() {
return invocationContext;
}
到了这里,整个ActionInvocation就完成了
ActionInvocation类完整代码:
package edu.jyu.invocation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import edu.jyu.config.ActionConfig;
import edu.jyu.context.ActionContext;
import edu.jyu.interceptor.Interceptor;
/**
* 拦截器链和action的调用类
*
* @author Jason
*/
public class ActionInvocation {
// 拦截器链
private Iterator<Interceptor> interceptors;
// 即将要被调用的action实例
private Object action;
// action配置信息
private ActionConfig config;
// 数据中心ActionContext
private ActionContext invocationContext;
/**
* 准备各种需要的数据
*
* @param interceptorClassNames
* 存放着拦截器链每个拦截器的全限定类名
* @param config
* 需要调用的action的配置信息
* @param request
* @param response
*/
public ActionInvocation(List<String> interceptorClassNames, ActionConfig config, HttpServletRequest request,
HttpServletResponse response) {
// 准备Interceptor链
if (interceptorClassNames != null && interceptorClassNames.size() > 0) {
List<Interceptor> list = new ArrayList<Interceptor>();
for (String className : interceptorClassNames) {
try {
Interceptor interceptor = (Interceptor) Class.forName(className).newInstance();
interceptor.init();
list.add(interceptor);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("创建Interceptor失败:" + className);
}
}
interceptors = list.iterator();
}
// 准备action实例
this.config = config;
try {
action = Class.forName(config.getClassName()).newInstance();
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("创建Action失败:" + config.getClassName());
}
// 准备数据中心ActionContext
invocationContext = new ActionContext(request, response, action);
}
/**
* 调用拦截器链和action处理方法
* @param invocation
* @return
*/
public String invoke(ActionInvocation invocation) {
// 结果串
String result = null;
// 判断拦截器链是否还有下一个拦截器或者未被拦截器拦截(result不等于null的话说明已经有拦截器直接返回结果串了)
if (interceptors!=null && interceptors.hasNext() && result==null) {
// 有下一个拦截器,调用下一个拦截器的拦截方法
Interceptor next = interceptors.next();
result = next.intercept(invocation);
} else {
// 没有下一个拦截器,调用action实例的处理方法
// 获取处理的方法名
String methodName = config.getMethod();
try {
Method method = action.getClass().getMethod(methodName);
result = (String) method.invoke(action);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("action方法调用失败:" + methodName);
}
}
return result;
}
public ActionContext getInvocationContext() {
return invocationContext;
}
}
好了,ActionInvocation大功告成,项目已经上传到Github上
https://github.com/HuangFromJYU/JStruts2
如果大家有什么问题或者发现什么错误可以发邮件到jasonwong_hjj@qq.com,共同学习共同进步