自己实现Struts2(四)实现ActionInvocation

2024-04-09 22:48

本文主要是介绍自己实现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;// 数据中心ActionContextprivate 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());}// 准备数据中心ActionContextinvocationContext = 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;// 数据中心ActionContextprivate 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());}// 准备数据中心ActionContextinvocationContext = 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,共同学习共同进步

这篇关于自己实现Struts2(四)实现ActionInvocation的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/889420

相关文章

Qt使用QSqlDatabase连接MySQL实现增删改查功能

《Qt使用QSqlDatabase连接MySQL实现增删改查功能》这篇文章主要为大家详细介绍了Qt如何使用QSqlDatabase连接MySQL实现增删改查功能,文中的示例代码讲解详细,感兴趣的小伙伴... 目录一、创建数据表二、连接mysql数据库三、封装成一个完整的轻量级 ORM 风格类3.1 表结构

基于Python实现一个图片拆分工具

《基于Python实现一个图片拆分工具》这篇文章主要为大家详细介绍了如何基于Python实现一个图片拆分工具,可以根据需要的行数和列数进行拆分,感兴趣的小伙伴可以跟随小编一起学习一下... 简单介绍先自己选择输入的图片,默认是输出到项目文件夹中,可以自己选择其他的文件夹,选择需要拆分的行数和列数,可以通过

Python中将嵌套列表扁平化的多种实现方法

《Python中将嵌套列表扁平化的多种实现方法》在Python编程中,我们常常会遇到需要将嵌套列表(即列表中包含列表)转换为一个一维的扁平列表的需求,本文将给大家介绍了多种实现这一目标的方法,需要的朋... 目录python中将嵌套列表扁平化的方法技术背景实现步骤1. 使用嵌套列表推导式2. 使用itert

Python使用pip工具实现包自动更新的多种方法

《Python使用pip工具实现包自动更新的多种方法》本文深入探讨了使用Python的pip工具实现包自动更新的各种方法和技术,我们将从基础概念开始,逐步介绍手动更新方法、自动化脚本编写、结合CI/C... 目录1. 背景介绍1.1 目的和范围1.2 预期读者1.3 文档结构概述1.4 术语表1.4.1 核

在Linux中改变echo输出颜色的实现方法

《在Linux中改变echo输出颜色的实现方法》在Linux系统的命令行环境下,为了使输出信息更加清晰、突出,便于用户快速识别和区分不同类型的信息,常常需要改变echo命令的输出颜色,所以本文给大家介... 目python录在linux中改变echo输出颜色的方法技术背景实现步骤使用ANSI转义码使用tpu

Python使用python-can实现合并BLF文件

《Python使用python-can实现合并BLF文件》python-can库是Python生态中专注于CAN总线通信与数据处理的强大工具,本文将使用python-can为BLF文件合并提供高效灵活... 目录一、python-can 库:CAN 数据处理的利器二、BLF 文件合并核心代码解析1. 基础合

Python使用OpenCV实现获取视频时长的小工具

《Python使用OpenCV实现获取视频时长的小工具》在处理视频数据时,获取视频的时长是一项常见且基础的需求,本文将详细介绍如何使用Python和OpenCV获取视频时长,并对每一行代码进行深入解析... 目录一、代码实现二、代码解析1. 导入 OpenCV 库2. 定义获取视频时长的函数3. 打开视频文

golang版本升级如何实现

《golang版本升级如何实现》:本文主要介绍golang版本升级如何实现问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录golanwww.chinasem.cng版本升级linux上golang版本升级删除golang旧版本安装golang最新版本总结gola

SpringBoot中SM2公钥加密、私钥解密的实现示例详解

《SpringBoot中SM2公钥加密、私钥解密的实现示例详解》本文介绍了如何在SpringBoot项目中实现SM2公钥加密和私钥解密的功能,通过使用Hutool库和BouncyCastle依赖,简化... 目录一、前言1、加密信息(示例)2、加密结果(示例)二、实现代码1、yml文件配置2、创建SM2工具

Mysql实现范围分区表(新增、删除、重组、查看)

《Mysql实现范围分区表(新增、删除、重组、查看)》MySQL分区表的四种类型(范围、哈希、列表、键值),主要介绍了范围分区的创建、查询、添加、删除及重组织操作,具有一定的参考价值,感兴趣的可以了解... 目录一、mysql分区表分类二、范围分区(Range Partitioning1、新建分区表:2、分