概述
在很多框架的设计中,都有类似Channel链的设计,类似Decorator模式或Chain Of Responsibility模式,可以向这个Channel注册不同的Handler,用户请求可以穿越这个由多个Handler组成的Channel,执行响应的切面逻辑,在最后一个Handler或者另一个Processor处理用于自定义的业务逻辑,然后生成的响应可以逆着方向从这个Channel回来。Structs2中的Interceptor、Servlet中Filter都采用这种设计。这种设计为面向切面编程提供了遍历,然而目前的Handler设计更多的像是Chain Of Responsibility模式,它的处理逻辑只能从链头走到链尾,而没有返回的路程。引入ScopedHandler的目的就是用于解决这个问题。实现
Structs2中Interceptor实现使用传入ActionInvaction来调用Channel中的后继Interceptor:
public
class MyInterceptor
extends AbstractInterceptor {
@Override
public String intercept(ActionInvocation ai) throws Exception {
try {
// Add user customized logic here when request come into the channel
return ai.invoke();
} finally {
// Add user customized logic here when response pass out across the channel
}
}
}
类似的,Servlet中的Filter实现也是使用FilterChain来调用Channel中后继的Filter:
@Override
public String intercept(ActionInvocation ai) throws Exception {
try {
// Add user customized logic here when request come into the channel
return ai.invoke();
} finally {
// Add user customized logic here when response pass out across the channel
}
}
}
public
class MyFilter
implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
try {
// Add user customized logic here when request come into the channel
chain.doFilter(request, response);
} finally {
// Add user customized logic here when response pass out across the channel
}
}
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
try {
// Add user customized logic here when request come into the channel
chain.doFilter(request, response);
} finally {
// Add user customized logic here when response pass out across the channel
}
}
}
ScopedHandler采用了不同的实现方式,首先它继承自HandlerWrapper,因而它使用HandlerWrapper中的Handler字段来构建Handler链,然而并不是所有的Handler都是ScopedHandler,因而ScopedHandler内部还定义了_nextScope字段用于创建在这条Handler链表中的ScopedHandler链表,以及_outerScope字段用于将自己始终和这个ScopedHandler链表中的最外层ScopedHandler相连,对这个ScopedHandler的最外层列表,其_outScope字段为null。而ScopedHandler要实现的行为是,假设A、B、C都是ScopedHandler,并且它们组成链表:A->B->C,那么当调用A.handle()方法时的调用堆栈是:
A.handle()
|-A.doScope()
|--B.doScope()
|----C.doScope()
|-----A.doHandle()
|------B.doHandle()
|-------C.doHandle()
而如果A、B是ScopedHandler,X、Y是其他的Handler,并且它们组成链表:A->X->B->Y,那么当调用A.handle()方法时的调用栈是:
A.handle()
|-A.doScope()
|--B.doScope()
|---A.doHandle()
|----X.handle()
|-----B.doHandle()
|------Y.handle()
这种行为主要用于Servlet框架的实现,它可以保证在doScope()方法中做一些初始化工作,并且配置环境,而在后继调用中都可以使用这些配置好的环境,并且doHandle()的顺序还是使用原来定义的Handler链表顺序,即使有些Handler并不是ScopedHandler。
在实现中,其Handler链表由HandlerWrapper构建,在doStart()方法中计算_nextScope字段以及_outerScope字段,在handle()方法中,如果_outerScope为null,则调用doScope()方法,否则调用doHandle()方法:
@Override
public final void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
if (_outerScope== null)
doScope(target,baseRequest,request, response);
else
doHandle(target,baseRequest,request, response);
}
在执行完doScope()方法后,调用nextScope()方法,该方法顺着_nextScope链表走,直到尽头,后调用doHandle()方法:
public final void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
if (_outerScope== null)
doScope(target,baseRequest,request, response);
else
doHandle(target,baseRequest,request, response);
}
public
final
void nextScope(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException
{
if (_nextScope!= null)
_nextScope.doScope(target,baseRequest,request, response);
else if (_outerScope!= null)
_outerScope.doHandle(target,baseRequest,request, response);
else
doHandle(target,baseRequest,request, response);
}
而doHandle()方法在完成是调用nextHandle()方法,它也沿着_nextScope链表走,只要_nextScope和_handler相同,则调用其doHandle()方法,但是如果_nextScope和_handler不同,则调用_handler中的handle()方法,用于处理在ScopedHandler链表中插入非ScopedHandler的情况:
{
if (_nextScope!= null)
_nextScope.doScope(target,baseRequest,request, response);
else if (_outerScope!= null)
_outerScope.doHandle(target,baseRequest,request, response);
else
doHandle(target,baseRequest,request, response);
}
public
final
void nextHandle(String target,
final Request baseRequest, HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException
{
if (_nextScope!= null && _nextScope==_handler)
_nextScope.doHandle(target,baseRequest,request, response);
else if (_handler!= null)
_handler.handle(target,baseRequest, request, response);
}
{
if (_nextScope!= null && _nextScope==_handler)
_nextScope.doHandle(target,baseRequest,request, response);
else if (_handler!= null)
_handler.handle(target,baseRequest, request, response);
}
使用
在ScopedHandler的测试用例中给出了一个非常好的例子。首先有一个TestHandler继承自ScopedHandler:
private
class TestHandler
extends ScopedHandler {
private final String _name;
private TestHandler(String name) {
_name=name;
}
@Override
public void doScope(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
try {
_history.append(">S").append(_name);
super.nextScope(target,baseRequest,request, response);
} finally {
_history.append("<S").append(_name);
}
}
@Override
public void doHandle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
try {
_history.append(">W").append(_name);
super.nextHandle(target,baseRequest,request,response);
} finally {
_history.append("<W").append(_name);
}
}
}
然后有非ScopedHandler的实现:
private final String _name;
private TestHandler(String name) {
_name=name;
}
@Override
public void doScope(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
try {
_history.append(">S").append(_name);
super.nextScope(target,baseRequest,request, response);
} finally {
_history.append("<S").append(_name);
}
}
@Override
public void doHandle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
try {
_history.append(">W").append(_name);
super.nextHandle(target,baseRequest,request,response);
} finally {
_history.append("<W").append(_name);
}
}
}
private
class OtherHandler
extends HandlerWrapper {
private final String _name;
private OtherHandler(String name) {
_name=name;
}
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
try {
_history.append(">H").append(_name);
super.handle(target,baseRequest,request, response);
} finally {
_history.append("<H").append(_name);
}
}
}
查看一下Test Case的执行结果:
private final String _name;
private OtherHandler(String name) {
_name=name;
}
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
try {
_history.append(">H").append(_name);
super.handle(target,baseRequest,request, response);
} finally {
_history.append("<H").append(_name);
}
}
}
@Test
public void testDouble() throws Exception
{
TestHandler handler0 = new TestHandler("0");
OtherHandler handlerA = new OtherHandler("A");
TestHandler handler1 = new TestHandler("1");
OtherHandler handlerB = new OtherHandler("B");
handler0.setHandler(handlerA);
handlerA.setHandler(handler1);
handler1.setHandler(handlerB);
handler0.start();
handler0.handle("target", null, null, null);
handler0.stop();
String history=_history.toString();
System.err.println(history);
assertEquals(">S0>S1>W0>HA>W1>HB<HB<W1<HA<W0<S1<S0",history);
}
public void testDouble() throws Exception
{
TestHandler handler0 = new TestHandler("0");
OtherHandler handlerA = new OtherHandler("A");
TestHandler handler1 = new TestHandler("1");
OtherHandler handlerB = new OtherHandler("B");
handler0.setHandler(handlerA);
handlerA.setHandler(handler1);
handler1.setHandler(handlerB);
handler0.start();
handler0.handle("target", null, null, null);
handler0.stop();
String history=_history.toString();
System.err.println(history);
assertEquals(">S0>S1>W0>HA>W1>HB<HB<W1<HA<W0<S1<S0",history);
}