tomcat pipline设计模式

最近在看tomcat源码,看到其中的Pipline设计模式,觉得很有趣,这里记录一下。

做过java web开发的应该都使用过Servlet和Filter。   当想访问一个Servlet时会经过定义好的一系列Filter,然后再访问到Servlet 。 这里就用到了Pipline管道设计模式。

Pipline的执行流程如下图


其中的Valve就是一个个阀门,Basic就是最后被执行的对象。  可以理解成Valve是Filter,Basic是Servlet 。 执行流程是控制权从第一个Valve一直流向最后一个Valve,最后流向Basic, 然后从Basic开始再将控制权反向流到最前面的Valve。

在Valve中可以让流继续向下执行,也可以终止后面的执行。


下面通过代码来介绍这种模式。

首先定义一个Pipline接口。来代表这样一个管道,可以添加任意多的Valve,也可以设置一个Basic。(这里的Basic叫Target可能更合适一点)

package tomcat.p4;

import java.util.Map;

/**
 * 管道
 * @author zhoufeng
 *
 */
public interface Pipline{
	
	/**
	 * 这里的basic代表最后要执行的内容(在所有add的Valve执行后执行)
	 * @param basic
	 */
	void setBasic(Valve basic);
	
	/**
	 * 添加一个阀门
	 * @param valve
	 */
	void addValve(Valve valve);
	
	/**
	 * 执行
	 * 将会以此执行add的所有Valve然后执行basic
	 * @param params  携带的参数
	 */
	void invoke(Map<String, Object> params);
	
}


invoke方法就是开始执行这个管道的内容,执行流程就是上面讲的。 方法定义了一个Map类型的参数。 可以类比Servlet和Filter中的Request和Response参数。


接下来定义一个Valve接口。

该接口只有一个方法invoke,注意invoke中的参数,一个是Map类型,用于传递Pipline执行时传设置的参数,另外一个是PiplineContext,该参数描述了Pipline的上下文信息,在invoke方法中如果需要继续执行后面的Valve,可以使用PiplineContext的invokeNext方法。  两个类的定义如下:

package tomcat.p4;

import java.util.Map;

/**
 * 阀门
 * @author zhoufeng
 *
 */
public interface Valve {

	/**
	 * 执行阀门里面的内容
	 * @param params  管道执行时携带过来的参数
	 * @param context 管道上下文
	 */
	void invoke(Map<String, Object> params , PiplineContext context);
	
}

package tomcat.p4;

import java.util.Map;

public interface PiplineContext {

	void invokeNext(Map<String, Object> params);
	
}


接下来实现一个简单的Piplie类。

package tomcat.p4.core;

import java.util.Map;

import tomcat.p4.Pipline;
import tomcat.p4.PiplineContext;
import tomcat.p4.Valve;

/**
 * 一个基本的pipline实现
 * @author zhoufeng
 */
public class SimplePipline implements Pipline{
	
	/**
	 * 阀门列表(相当于一系列的Filter)
	 */
	private Valve[] valves = new Valve[0];
	
	/**
	 * 最终要执行的内容
	 */
	private Valve basic ;

	
	@Override
	public void setBasic(Valve basic) {
		this.basic = basic ;
	}

	/**
	 * 添加一个阀
	 */
	@Override
	public void addValve(Valve valve) {
		Valve[] newValves = new Valve[valves.length + 1];
		System.arraycopy(valves, 0, newValves, 0, valves.length); 
		newValves[newValves.length - 1] = valve ;
		valves = newValves ;
	}

	/**
	 * 执行管道中的内容(注意这里使用内部类实例SimplePiplineContext实现)
	 */
	@Override
	public void invoke(Map<String, Object> params) {
		new SimplePiplineContext().invokeNext(params); 
	}
	
	/**
	 * 这里使用一个私有的内部类实现PiplineContext,用于线性的依次执行pipline中的valves,然后执行basic
	 * @author zhoufeng
	 *
	 */
	private class SimplePiplineContext implements PiplineContext{
		
		/**
		 * 记录当前正在执行到哪个valve
		 */
		private int step = 0 ;

		@Override
		public void invokeNext(Map<String, Object> params) {
			int curStep = step ;	
			step++;
			if(curStep < valves.length){
				valves[curStep].invoke(params , this); 
			}else if(curStep == valves.length){
				if(SimplePipline.this.basic != null){
					SimplePipline.this.basic.invoke(params, this); 
				}
			}else{
				throw new RuntimeException("no valve fond");
			}
		}
		
	}

	
}

里面的经典之处就是使用一个内部类SimplePiplineContext实现PiplineContext接口,从而可以通过该内部类操作宿主类SimplePipline实例的方法。


注意看SImplePipline的invoke方法,是实例化了一个内部类SimplePiplineContext对象,然后通过该对象一步一步调用自身的valves数组和basic对象。






接下来写几个类来测试一下。

首先实现一个HelloValve来当作Basic,将参数params都打印出来 (可以理解为一个Servlet)

package tomcat.p4.test.valve;

import java.util.Map;
import java.util.Map.Entry;

import tomcat.p4.PiplineContext;
import tomcat.p4.Valve;

public class HelloValve implements Valve{

	@Override
	public void invoke(Map<String, Object> params, PiplineContext context) {
		System.out.println("--------------Hello Servlet :--------------");
		if(params != null && params.size() > 0){
			for (Entry<String, Object> entry : params.entrySet()) {  
				System.out.printf("key:%s\tvalue:%s\n",entry.getKey() , entry.getValue());
			}
		} 
	}
	
}

然后实现一个BeforeValve,在basic前面打印一行内容。(可以理解为一个Filter)

package tomcat.p4.test.valve;

import java.util.Map;

import tomcat.p4.PiplineContext;
import tomcat.p4.Valve;

public class BeforeValve implements Valve{

	@Override
	public void invoke(Map<String, Object> params, PiplineContext context) {
		System.out.println("----------------BeforeValve-----------------");
		context.invokeNext(params); 
	}

	
}

再实现一个AfterValve,在basic后面打印一行内容。(可以理解为一个Filter)

package tomcat.p4.test.valve;

import java.util.Map;

import tomcat.p4.PiplineContext;
import tomcat.p4.Valve;

public class AfterValve implements Valve{

	@Override
	public void invoke(Map<String, Object> params, PiplineContext context) {
		context.invokeNext(params); 
		System.out.println("----------------AfterValve-----------------");
	}

}

再实现一个AroundValve,在basic的前后都打印一段内容。(可以理解为一个Filter)

package tomcat.p4.test.valve;

import java.util.Map;

import tomcat.p4.PiplineContext;
import tomcat.p4.Valve;

public class AroundValve implements Valve{

	@Override
	public void invoke(Map<String, Object> params, PiplineContext context) {
		System.out.println("----------------AroundValve before-----------------");
		
		context.invokeNext(params); 
		
		System.out.println("----------------AroundValve after-----------------");
	}

	
}


然后写个测试类来试试。


package tomcat.p4.test;

import java.util.HashMap;
import java.util.Map;

import tomcat.p4.Pipline;
import tomcat.p4.Valve;
import tomcat.p4.core.SimplePipline;
import tomcat.p4.test.valve.AfterValve;
import tomcat.p4.test.valve.AroundValve;
import tomcat.p4.test.valve.BeforeValve;
import tomcat.p4.test.valve.HelloValve;

public class Startup {

	public static void main(String[] args) {
		
		Pipline pipline = new SimplePipline();
		
		Valve helloValve = new HelloValve(); 
		
		Valve beforeValve = new BeforeValve();
		Valve afterValve = new AfterValve();
		Valve aroundValve = new AroundValve();
		
		pipline.setBasic(helloValve); 
		
		pipline.addValve(beforeValve);
		pipline.addValve(afterValve);
		pipline.addValve(aroundValve);
		
		Map<String, Object> params = new HashMap<String , Object>(); 
		params.put("a", 1);
		params.put("b", 2);
		params.put("c", 3);
		
		pipline.invoke(params);  
		
	}
	
}


执行结果

----------------BeforeValve-----------------
----------------AroundValve before-----------------
--------------Hello Servlet :--------------
key:a	value:1
key:b	value:2
key:c	value:3
----------------AroundValve after-----------------
----------------AfterValve-----------------


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值