前言: 本文只是本人的学习总结,目的希望能和大家一起交流分享,顺便备忘,如有不正确的地方,欢迎指正。本文可能需要你对webwork框架有一定的了解。
我们在开发中Proxy模式是经常用到的,代理主要用来对访问的资源进行权限控制以及监控的目的,如果我们在开发系统需要对访问的对象进行控制和监控的话,proxy模式是很有用途的。
举些例子:
我们开发一个应用系统时,用户请求任何页面都要经过权限控制,最早开发的时候我们常常封装一个权限验证方法,然后在每个jsp或者对应的servlet中增加相关代码,但是用户对权限的需求往往是多变的,这样一旦权限验证方法变化(参数变化,增加方法),如果开发的系统很庞大的话,有可能你就需要修改几百个jsp页面或者servlet代码。
还有我们常需要判断session是否过期,如果过期就要重新登陆系统,如果每个页面或者servlet都要加判断代码,那也是件比较痛苦的事情。
如果我们采用代理模式,增加Proxy对象,每次用户请求必须通过proxy对象处理,由它专门处理相关权限控制,一旦权限需求变化了,只需要修改Proxy对象相关的实现方法。
Proxy模式不仅仅用于上述场景,还可以在其他方面应用。
我们可以研究研究webwork的源代码,看看它是如何设计的。
webwork开发框架目前是比较流行的web开发框架之一,最近我的开发项目就采用了该框架,它相比struts有很多优点(晚出来的再没优点也不行啊,呵呵,关于其缺点也有,有时间再说),
主要如下:
1、 易单元测试;
2、 线程安全;
3、 允许使用截取器模块化前/后处理. 拦截器可以通过配置动态添加, 两者之间没有任何耦合;
4、 WebWork 2使用Ognl, 强大的表达式语言, 也可以访问值栈. Ognl对集合和索引属性的支持非常强大。
其中优点3的实现和proxy模式是非常相关的,下面就讲讲webwork如何采用Proxy模式实现其优点3的。
首先我们看看webwork的核心类ServletDispatcher的请求处理代码:
public void serviceAction(
HttpServletRequest request, HttpServletResponse response,
String namespace, String actionName,
Map requestMap, Map parameterMap,
Map sessionMap, Map applicationMap) {
HashMap extraContext = createContextMap(requestMap, parameterMap, sessionMap, applicationMap, request, response, getServletConfig());
extraContext.put(SERVLET_DISPATCHER, this);
try {
ActionProxy proxy = ActionProxyFactory.getFactory).
createActionProxy(namespace, actionName, extraContext);
request.setAttribute("webwork.valueStack", proxy.getInvocation().getStack());
proxy.execute();
}
catch (ConfigurationException e) {
log.error("Could not find action", e);
sendError(request, response, HttpServletResponse.SC_NOT_FOUND, e);
}
catch (Exception e) {
log.error("Could not execute action", e);
sendError(request, response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR,e);
}
}
.可以看到整个对请求的处理非常简练:通过工厂方法获取一个ActionProxy 实例,执行ActionProxy实例的execute()方法,所有请求都需要通过该方法处理。
ActionProxyFactory.getFactory().createActionProxy(namespace, actionName, extraContext);
这段代码调用过程我简单描述一下:
首先ActionProxyFactory.getFactory().获取到一个DefaultActionProxyFactory实例,然后该工厂实例调用createActionProxy(相关参数)方法创建一个DefaultActionProxy示例。
下面我们看看这个DefaultActionProxy究竟是如何处理的:
首先它会根据相关信息获取当前Action配置对象,从中可以知道当前Action中配置了那些拦截器、Result等等配置信息,具体可以查看ActionConfig对象,然后创建一个调用对象DefaultActionInvocation实例(也是通过工厂方法),该实例调用invoke()方法完成该action配置的拦截器的拦截方法以及action的execute()方法或action自定义的method的执行。
public String invoke() throws Exception {
if (executed) {
throw new IllegalStateException("Action has already executed");
}
if (interceptors.hasNext()) {
Interceptor interceptor = (Interceptor) interceptors.next();
resultCode = interceptor.intercept(this);
} else {
if (proxy.getConfig().getMethodName() == null) {
resultCode = getAction().execute();
} else {
resultCode = invokeAction(getAction(), proxy.getConfig());
}
}
// this is needed because the result will be executed, then control will return to the Interceptor, which will
// return above and flow through again
if (!executed) {
if (preResultListeners != null) {
for (Iterator iterator = preResultListeners.iterator();
iterator.hasNext();) {
PreResultListener listener = (PreResultListener) iterator.next();
listener.beforeResult(this, resultCode);
}
}
// now execute the result, if we're supposed to
if (proxy.getExecuteResult()) {
executeResult();
}
executed = true;
}
return resultCode;
}
而一般拦截器对象都是AroundInterceptor的子类,在AroundInterceptor类中的拦截方法如下:
public String intercept(ActionInvocation invocation) throws Exception {
String result = null;
before(invocation);
result = invocation.invoke();
after(invocation, result);
return result;
}
注意该调用对象的invoke方法比较有意思,它采用的是遍历调用的方式,每个Action一般都有多个拦截器,每个拦截器执行完毕后再回调该调用对象的invoke方法,有点像链式,如果中间有自定义拦截器有发现异常,不要再执行下去,直接返回Result相关字符串,中断之后的拦截器以及Action不再执行,正常情况下链尾是调用对应Action实例的execute()方法,获取Result相关字符串后,根据字符串值获取相关Result实例,执行Result实例中excute()方法派发或者重导向到相关视图(jsp、vm等等),一旦链尾处理过请求后,链中的其他节点就不需要再派发。只需继续执行拦截器中的after()方法(如果有的话)的执行,webwork采用这样的方式实现主要是为了满足action后处理功能的需要(有点跑题了,变成webwork框架源码分析)。
题外话:从整个调用过程我们可以发现: Webwork框架的核心功能实际上都是在Xwork框架中实现的,Webwork实际上只是Xwork在B/S系统上的应用。
结束总结:
1、我们在开发时,如果要对访问的对象进行统一预处理、控制、监控管理时可以采用Proxy模式。
2、Proxy模式往往和Factory模式一起使用。个人理解是因为考虑系统的扩展性、通用性,有可能有不同的类型的Proxy以及调用,根据不同的应用场景,可以采用不同的工厂创建。
3、如果运用的不是很恰当的话,会造成Proxy的实现很庞大,并且和相关对象耦合过高,而webwork采用配置每个action对应的拦截器这种设计就非常好,耦合也比较低,实际上它变相的实现了每个对象采用不同的Proxy,个人感觉其这方面的设计很不错,可以借鉴。