总览
请求通过Adapter转换为渠道无关的Context(包含User,Data等)后,将经过责任链的处理,才交给业务逻辑处理。
基本流程
前端将Context交给coreController,经历了以下处理流程:
coreController—>Chain—>Template—>Action
Transaction类
对于一个请求,处理流程通常是:首先校验请求,然后交给具体的逻辑单元处理。在PE中,完成这样一个过程称为执行一个交易(Transaction
)。
为了更好的抽象与复用,交易的校验部分称为责任链(Chain
),将交易的共用逻辑流程抽取为模板(Template
),具体的逻辑单元称为动作(Action
)。
在PE中,请求路径是没有层级结构的,所有的请求地址都是类似http://xxxxxxxxx/transactionId.do
。其中的transactionId
就是唯一的交易id。
PE中Transaction
中对应的类叫做TransactionConfig
,CoreController根据transactionId获取其实例,并执行其责任链。
transactionConfig = (TransactionConfig)transactionConfigs.get(context.getTransactionId());//从ConcurrentHashMap中获取 Chain chain = transactionConfig.getTemplate().getChain(); chain.execute(context, null);
Chain类
链中执行的校验逻辑被抽象为一个个命令(Command
)。链维护了一个Command list
,执行Chain就是循环迭代执行该命令表:
public boolean execute(Context context, Map map)throws PeException{
//...
for(Iterator it = commands.iterator(); it.hasNext();){
Command command = (Command)it.next();
try{
saveResult = command.execute(context, setting);
if(saveResult)
break;
}catch(Exception e){
saveException = e;
break;
}
}
//...
}
Chain可以类比SpringMVC中的Interceptor,通过拦截业务逻辑的执行,完成一些处理。
Command类
表示一个校验命令,基本方法是execute():
public boolean execute(Context context, Map setting)
throws PeException;
delegateCommand
委托命令将执行其模板:
public boolean execute(Context context, Map setting)
throws PeException{
context.getTransactionConfig().getTemplate().execute(context);
return false;
}
Template类
模板是抽取多个交易的共同逻辑流程,这些交易通常使用相同的Chain,执行类似的任务,如查询、提交等。
不同的模板,调用action的方法不同。通常它们都有一个共同的父类AbstractSequenceTemplate
,它定义了基本的处理流程,并预留方法供子类自定义实现来调用action中的方法:
public void execute(final Context context) throws PeException{
final Map actions=getActions();
Action action;
for(Iterator it = actions.keySet().iterator(); it.hasNext(); doInternal(context, action)){
action = getAction((String)it.next(), context);
}
}
protected abstract void doInternal(Context context, Action action)
throws PeException;//预留方法
AbstractSequenceTemplate.execute()
将迭代处理所有的action,而具体的处理方法doInternal()
由子类去实现。
如:
public class PageLoaderSequenceTemplate extends AbstractSequenceTemplate{
protected void doInternal(Context context, Action action)
throws PeException{
if(action instanceof PageLoader)
((PageLoader)action).init(context);
}
}
Action类
action为具体的逻辑单元,在后面介绍。
配置信息
Chain
chain包含了command列表,这些command提供校验、拦截的功能。主要包括以下功能:
- requestCheckCommand UGC(用户提交内容)和谐,防止XSS
- validationCommand 字段格式校验(长度,类型,正则)
- ruleCommand 字段规则校验
- delegateCommand 执行业务逻辑
- wxSetJsonResultCommand 微信渠道返回结果
- tokenControlCommand 防重复提交
- tokenControlVercodeCommand 图形验证码校验
- loginControlCommand 防撞库
- phoneTokenControlCommand 短信验证码校验
- roleControlCommand 权限控制
<chain id="chainForPublic">
<commands>
<ref>requestCheckCommand</ref>
<ref>validationCommand</ref>
<ref>ruleCommand</ref>
<ref>delegateCommand</ref>
<ref>wxSetJsonResultCommand</ref>
</commands>
</chain>
Template
Template(模板)通过实现类确定了如何调用action;确定了chain;action通常以占位符的形式存在,将被Transaction级配置信息覆盖。
<template id="模版Id"
class="Full qualified class name of this Template"
chain="引用的责任链Id">
<actions><!--模版级定义的Actions. -->
<ref name="act01">Placeholder</ref>
</actions>
</template>
Transaction
Transaction的配置信息如下:
<transaction id="交易Id" template="引用的模版Id">
<actions>
<ref name="act01" >交易级定义的Action</ref>
</actions>
<fields>
<field name="域名1">Style名称</field>
...
<field-list name="域名x" counter="域名y">
<fields>
<field name="域名n"> Style名称</field>
<field-list>...</field-list>
</fields>
<field-list>
</fields>
<rules>
<rule id="equals">
<param name="content">dataMap.Password == dataMap.ConfirmPassword</param>
<param name="messageKey">validation.password.equal</param>
</rule>
</rules>
<channels>
<channel type="http">
<param name="success">页面名称</param>
<param name="successN">页面名称</param>
<param name=”success5”>OperatorWelcome</param>
</channel>
</channels>
</transaction>
其确定了:
- action transaction的actions将覆盖(template)的actions属性。transaction的配置信息确定了具体的action。
- fields 确定了要将style校验(正则匹配)和传递给action的字段;
- rules 定义了逻辑判断规则,支持OGNL表达式。由于比较鸡肋,很少使用;
- channels 定义了不同请求来源的可选视图。在action中通过返回定义的
name
,如success
,来返回视图名。
总结
责任链主要用于提供XML配置式安全保护的安全性框架,同类的开源方案有Spring Security。责任链提供的功能主要包括:字段校验、用户登录校验、权限校验、手机验证码、密码校验等。责任链是一种成熟的设计模式,通过配置实现了功能复用,思想上没什么问题。
但class与bean之间的两种抽象,以及class的继承关系与bean的继承关系这两种继承关系可能会让人感到困惑。
个人看来,注解相比xml的配置信息更加灵活,同时也具有可配置的优点,更值得使用。