Apache Commons Chain 源码研究

从事Java研发之后,阅读源码习惯保持到现在,获得的好处自然不少,但同时也带来一些困扰,比如实现某种设计模式的时候,你的大脑里会存在多种选择,导致你无所适从。本文将要讨论的Apache Commons Chain 正是为了缓解这种情况。

1. 楔子

有过二方库开发或设计经验的读者应该对职责链设计模式的应用不会感到陌生,笔者在构建公司的几款二方库时候或多或少地都应用了该模式。不过在实现的过程中,慢慢发现一个问题——每次都得重新编写一些重复性的基础性代码,而且实现方式基本都是受当时场景的影响,总而言之就是没有一个自己的标准实现方式。

在阅读了Java多款经典框架之后,笔者除了深感其实现方式的精妙,也见识到了它们对职责链模式实现方式的差异,比如Druid,Tomcat的FilterChain方式,SpringMVC的Interceptor,Mybatis的Plugin,JFinal和Strus2的Invocation等等。这让选择困难症患者经常陷入一种魔障的状态——这个不错,那个也很好啊(选择太多等于没有选择)。

2. Apache Commons Chain简介

3. 使用

我们以一个简单的例子来说明Apache common-chains的使用。

引入Maven依赖:

<dependency>
    <groupId>commons-chaingroupId>
    <artifactId>commons-chainartifactId>
    <version>1.2version>
dependency>

相关测试代码

public class Command1 implements Command {
     
	public boolean execute(Context arg0) throws Exception {
     
		System.out.println("Command1 is done! the chain will going.");
		return Command.CONTINUE_PROCESSING;
	}
}

public class Command2 implements Command {
     
	public boolean execute(Context arg0) throws Exception {
     
		System.out.println("Command2 is done! the chain will going.");	
		return Command.CONTINUE_PROCESSING;
	}
}

public class Command3 implements Command {
     
	public boolean execute(Context arg0) throws Exception {
     
		System.out.println("Command3 is done! the chain not going.");
		//代表执行完成, 流程不再继续.
		return Command.PROCESSING_COMPLETE;
	}
}

public class CommandChain extends ChainBase {
     
	//增加命令的顺序也决定了执行命令的顺序
	public CommandChain() {
     
		addCommand(new Command1());
		addCommand(new Command2());
		addCommand(new Command3());
		// 这里的Command1不会执行, 因为Command3中声明流程终止
		addCommand(new Command1());
	}
}

// ============================= 测试
@Test
public void javaConfig_success() throws Exception {
     
	CommandChain process = new CommandChain();
	Context ctx = new ContextBase();
	process.execute(ctx);
}

/*
// 输出
	Command1 is done! the chain will going.
	Command2 is done! the chain will going.
	Command3 is done! the chain not going.
*/

4. 源码解析

只解析下基本的流程和相关类。

4.1 Command接口

该接口为commons-chain组件中的核心接口,内部制只定义了一个核心方法 execute(),对于该方法:

  1. 返回true(对应Command.PROCESSING_COMPLETE常量), 代表整体逻辑执行完毕,流程不再继续
  2. 返回false(对应 Command.CONTINUE_PROCESSING常量), 则代表**将处理逻辑交给之后的Command, 流程继续执行 ** 。

对于Command接口,框架自身是提供了一系列的默认实现类的,诸如CopyCommand(将一个键值对的KEY值切换为另外一个KEY,主要用于适配),RemoveCommand(移除某个键值对)等等。感兴趣的读者可以测试相关的功能。

4.2 Context接口
  1. 该接口相当于框架设计里的会话域,会在每次实际的流程处理开始前被构建。
  2. 其直接继承自JDK中的Map,这样的设计确保了足够的扩展性,使用者可以根据需要向其中填充约定的键值对,这样之后的Command实现类中可以取到这些键值对以进行自定义处理。

接下来我们来看看该接口在框架中的基本实现类 ContextBase
Apache Commons Chain 源码研究_第1张图片
通过继承链我们可以发现 ContextBase实现了Context接口,并直接继承自HashMap,并,这样便不需要自己去实现Context接口基类Map所定义的一系列方法了。

另外观察 ContextBase的构造函数实现,可以发现ContextBase在初始化的时候会对本身使用反射进行处理,提取自身的自定义属性,并以键值对的形式推入到自身容器中,这样可以直接通过 context.get("beanPropertyName") 来获取相应的值。

4.3 Chain接口
  1. 注意本接口中,重新声明了一次 boolean execute(Context context) throws Exception; 方法。
  2. 注意其直接继承自Command接口,所以Chain也是一种Command,这样我们就可以将某个Chain作为Command添加到另外一个Chain中。这样我们可以以更加模块化的方式搭建出一个个代表处理逻辑的积木块(Chain),然后将这些积木块进行拼接得到最后的完整处理链条。

最后让我们把目光集中到Chain接口在框架中的唯一实现类ChainBase,其实现的execute()正是整个职责链模式实现的关键所在。

// ChainBase.execute()
public boolean execute(Context context) throws Exception {
     

    // Verify our parameters
    if (context == null) {
     
        throw new IllegalArgumentException();
    }

    // Freeze the configuration of the command list
    frozen = true;

    // Execute the commands in this list until one returns true
    // or throws an exception
    boolean saveResult = false;
    Exception saveException = null;
    int i = 0;
    int n = commands.length;
	// 顺序性地迭代执行Command
    for (i = 0; i < n; i++) {
     
        try {
     
            saveResult = commands[i].execute(context);
			// 如果当前Command执行时返回true, 则跳出循环迭代, 不再执行之后的Command
            if (saveResult) {
     
                break;
            }
        } catch (Exception e) {
     
			// 如果发生异常, 则记录下当前异常;
			// 并跳出循环迭代, 不再执行之后的Command
            saveException = e;
            break;
        }
    }

    // Call postprocess methods on Filters in reverse order
	// 回退到链条的最后一个
    if (i >= n) {
      // Fell off the end of the chain
        i--;
    }
    boolean handled = false;
    boolean result = false;
	// 倒序执行Filter接口, 给使用者一个处理异常的机会
    for (int j = i; j >= 0; j--) {
     
        if (commands[j] instanceof Filter) {
     
            try {
     
                result =
                    ((Filter) commands[j]).postprocess(context,
                                                       saveException);
				// 是否处理了异常, 返回true代表Command自身处理了该异常, 框架不要再处理
                if (result) {
     
                    handled = true;
                }
            } catch (Exception e) {
     
                  // Silently ignore
            }
        }
    }
	
    // Return the exception or result state from the last execute()
	// 如果执行时发生了异常, 并且上面的Filter没有显式声明处理了该异常; 则抛出异常
    if ((saveException != null) && !handled) {
     
        throw saveException;
    } else {
     
        return (saveResult);
    }

}

5. 补充

  1. 注意因为实现年代的原因,该框架中并没有实现到Java5引入的泛型,所以Context 继承自Map,而非Map
  2. commons-chain.jar 默认情况下对外界没有依赖,读者可以放心地依赖它。

6. Links

  1. 官网cookbook

你可能感兴趣的:(Java,Java,Apache,Chain,源码)