自己实现Struts2(四)实现ActionInvocation

上一章自己实现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类定义了四个成员变量,现在我就简单介绍一下这个几个变量

  1. interceptors:很明显这就是拦截器链了,存放着需要执行的拦截器,用Iterator类型是为了在后面调用的时候方便取出来。
  2. action:要调用的action实例。
  3. config:要调用的action实例对应的配置信息,要根据配置信息去调用action的方法。
  4. 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就不会被调用到了。

那么思路就是这样的:

  1. 如果拦截器链中还有其他的Interceptor,并且未被拦截器拦截(即未返回一个结果串),那么久调用下一个拦截器的拦截方法。

  2. 如果拦截器链中没有下一个拦截器了,那么根据action配置信息即config调用action实例的处理方法。

  3. 最后将结果串返回。

代码实现:

/**
 * 调用拦截器链和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,共同学习共同进步

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值