1、CoreController
1)刚进入类CoreControllerImpl.java
会看到:private static ThreadLocal if = new ThreadLocal();
ThreadLocal 在execute()方法起始的地方:setContext(var1); 意思是将context放入ThreadLocal,那么context就可以在该线程的任何地方可见,不用再通过传参的方式将其往下传。(常见例子:public void init(Context context){...} context直接可以拿来用)
finally结束的地方:setContext((Context)null); 意思是释放掉ThreadLocal线程结束,context也随之消失。
2)CoreController.execute()方法如下:
public void execute(Context var1)throws PeException {....}
下面我们详细分析下execute()方法:
1) 由pe框架简介4--MainController可知,var1的类型为LocalServletContext,通过下面的类图可以看出LocalServletContext实现了TransactionConfigAware,所以execute()方法上来进入if分支,此时 var1 为localServletContext。
2)由var2 = (TransactionConfig)this.getApplicationContext().getBean(var1.getTransactionId()) 知, var2 为transaction中的id所指明的bean。例如下图的PrintBillByQRCodeQuery。
问题:Context.setTransactionId();在哪里体现??我们需要注意:((TransactionConfigAware)var1).setTransactionConfig(var2); 将Context.TransactionConfig属性已经设置为transaction中的id,后续会用Context.getTransactionConfig()获取相应的transaction。
3)最关键的便是:Chain var3 = var2.getTemplate().getChain();
该句是指找到 template.xml 中的相应模板的chain,对应下图的例子就是找到 template.xml 中 id="queryTemplate" 的chain : chain="chainForRoleControlMV" 。
问题:Context.setTemplate().setChain()在哪里体现??
4) var3.execute(var1, (Map)null); 便是调用 Chain.execute()方法,由于Chain接口的实现类只有一个:ChainImpl,所以执行的方法为为ChainImpl.execute();
其中实现类ChainImpl实现了Chain、Command接口。
2、ChainImpl.execute()方法关键步骤如下:
1)var3 = var12.execute(var1,this.else);
其中 var12 为 var9 的子集,而 var9 为 this.new ,this.new (见下图)就是chain.xml中的各个command;
到此,我们就可以执行各个command.execute()方法了。
2)if (case ||char || var3) { break; } 我们注意到:
(a)在循环执行各个command.execute()方法时,如果execute()方法返回结果为true;则跳出循环,不再执行后续的command。即如果chain中的commond在按顺序执行时,遇到一个返回true,则剩余的commond不再继续执行。所以开发的时候需要注意,如果想让commond都执行一遍,程序中commond的实现类必须要返回false,返回true和异常的场景,不会继续执行接下来的commond。
(b)如果execute()方法抛出异常,则catch住(见图2-1),并跳出循环,之后对异常的处理都转化为PE框架的异常,见图2-2
(c)对于图2-2的上半部分,我们可以看到还有一个对var9的循环处理,var9 此时为 this.try,this.try(见图2-3)即实现Terminator的commond,其中this.new中已经排除它,在这里单独处理这些类型的commond。
3)我们来看个Commod的例子:DateStyleCommod例子如下:
(a)根据DateStyleCommod找到实现类:com.ceb.wap.mgmt.core.DateStyleCommod
(b)执行DateStyleCommod.execute()方法,即执行父类AbstractChannelCommand.execute()方法
(c)父类的execute()调用抽象的channelExecute(),通过策略模式,让各个子类自己实现channelExecute()方法
4)特殊的commond:DelegateCommand
每一个Chain必须有一个而且仅限于一个DelegateComand(但是DelegateComand并不一定能执行到,一旦有一个Command返回true或者跑异常,则不会继续执行剩余的Command,证明代码有异常或者校验不通过的情况);
当Chain执行到DelegateCommand时,开始执行Template,所以DelegateCommand要放在所有Command的最后;
5)由DelegateCommand源码可以看出,当执行到DelegateCommand时,开始执行Template.execute(),即执行tempalte.xml中对应class的execute()。
我们选取Template的其中一个实现类ExecutableSequenceTemplate来看一下。
(a) ExecutableSequenceTemplate 继承AbstractSequenceTemplate ,所以执行父类AbstractSequenceTemplate .execute()。
(b) AbstractSequenceTemplate .execute()方法:见图2-10 。首先它获取template.xml中的actions,如果actions为空,则取transaction里的action执行,否则执行Template里的action。
(c) 由图2-11可知, 如果var2不为空,即 transaction里的action,或者Template里的action有一个不为空,就循环执行:
Action var4 =this.getAction((String)var3.next(), var1); 具体逻辑见 图2-12
(d) 图2-12 便是 怎么从Template里的actions 执行到 transaction里的actions 里的关键
Map var3 = var2.getTransactionConfig().getActions(); //首先获取transaction里的action
// 如果 transaction里的action 为空,返回Template里的actions
if (var3 == null) { var4 = this.getActions().get(var1); return (Action)var4;}
// 如果 transaction里的action 不为空
var4 = var3.get(var1);
if (var4 == null) {
var4 = this.getActions().get(var1);
}
// 由图2-11可知 ,var1 为 transaction里的action 的ref的name,如:aftaction、action、preAction等,var3 是 Template里的actions的map(ref的name,ref的值) ,这句话是说,从 var3 ( Template里的actions )中取出与var1( transaction里的action )的ref相同的name,一般 :
transaction :<ref name="action">Placeholder</ref> var1 : action
Template :<ref name="action">PrintBillByQRCodeAction</ref> var3: map(action ,PrintBillByQRCodeAction)
也就是说,获取 var3:map( action ,PrintBillByQRCodeAction) 中key为 var1的value,即将Template 中ref.name 与transaction 中 ref.name 相同的action替换为 transaction 的action,这样便开始执行transaction .action。
如果var4 == null 还继续执行Template 中的action。
为了最后执行transaction .action,必须先将 Template 中的action执行完,那么 Template 中与transaction 重名的action(ref.name相同,即action)必须写在最后。