配色: 字号:
Struts2 源码分析——Action代理类的工作
2016-09-21 | 阅:  转:  |  分享 
  
Struts2源码分析——Action代理类的工作章节简言 上一章笔者讲到关于如何加载配置文件里面的package元素节点信息。相信读者到这里心里面对struts2在启动的时候加载相关的信息有了一定的了解和认识。而本章将讲到关于struts2启动成功之后,接受到用户action请求之后如何处理并找到对应的action类。可以说这章是讲述《Struts2源码分析——调结者(Dispatcher)之执行action》章节之后的事情。即是核心机制图片的蓝色(Strutscore)分部的知识点。通过前面几章节的内容至少我们知道了struts2启动成之后,会把相关的信息存放在Container容器和DefaultConfiguration类的实例里面。而Dispatcher类的实例便是这俩个类的中间调节者。(不懂得的读者请先查看一下前面几章节来在)

?Action代理类的新建 通过《Struts2源码分析——调结者(Dispatcher)之执行action》章节我们知道执行action请求,最后会落到Dispatcher类的serviceAction方法上面。可惜笔者并没有在这一章里面对他自己详细的讲解。先让我们看一下代码吧?知道他在做什么吧。如下

Dispatcher类:

1publicvoidserviceAction(HttpServletRequestrequest,HttpServletResponseresponse,ActionMappingmapping)

2throwsServletException{

3

4MapextraContext=createContextMap(request,response,mapping);

5

6//如果之前就有了值栈,就是新建一个新的值栈,放入extraContext

7ValueStackstack=(ValueStack)request.getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);

8booleannullStack=stack==null;

9if(nullStack){

10ActionContextctx=ActionContext.getContext();

11if(ctx!=null){

12stack=ctx.getValueStack();

13}

14}

15if(stack!=null){

16extraContext.put(ActionContext.VALUE_STACK,valueStackFactory.createValueStack(stack));

17}

18

19StringtimerKey="HandlingrequestfromDispatcher";

20try{

21UtilTimerStack.push(timerKey);

22Stringnamespace=mapping.getNamespace();//获得request请求里面的命名空间,即是struts.xml是的package节点元素

23Stringname=mapping.getName();//获得request请求里面的action名

24Stringmethod=mapping.getMethod();//要执行action的方法

25

26ActionProxyproxy=getContainer(www.hunanwang.net).getInstance(ActionProxyFactory.class).createActionProxy(namespace,name,

27method,extraContext,true,false);//获得action的代理

28

29request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY,proxy.getInvocation().getStack());

30

31//如果action映射是直接就跳转到网页的话,

32if(mapping.getResult()!=null){

33Resultresult=mapping.getResult();

34result.execute(proxy.getInvocation());

35}else{

36proxy.execute();//这里就是执行action

37}

38

39

40//

41if(!nullStack){

42request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY,stack);

43}

44}catch(ConfigurationExceptione){

45logConfigurationException(request,e);

46sendError(request,response,HttpServletResponse.SC_NOT_FOUND,e);

47}catch(Exceptione){

48if(handleException|www.visa158.com|devMode){

49sendError(request,response,HttpServletResponse.SC_INTERNAL_SERVER_ERROR,e);

50}else{

51thrownewServletException(e);

52}

53}finally{

54UtilTimerStack.pop(timerKey);

55}

56}

1.根据传入的参数request,response,mapping来新建一个上下文Map。上下文Map就是一个存了关于RequestMap类,SessionMap类,ApplicationMap类等实例。即是request请求相关的信息,只是把他变成了对应的MAP类而以。

2.从request请求中找到对应的值栈(ValueStack)。如果没有就新建值栈。然后存放到上下文Map里面,对应的KEY为ActionContext.VALUE_STACK常量的值。即是"com.opensymphony.xwork2.util.ValueStack.ValueStack"。

3.从Mapping参数中提取对应的request请求的命名空间,action名字和方法名。

4.从Container容器中找到ActionProxyFactory类,并根据request请求的命名空间,action名字和方法名,上下文Map来获得对应的action代理类(ActionProxy)。然后更新request请求中的对应的值栈(ValueStack)。

5.根据Mapping参数来判断是否为直接输出结果。还是执行action代理类。

6.最后在判断之前是否request请求没有找到对应的值栈(ValueStack)。如果有找到值栈(ValueStack),则更新request请求中的对应的值栈(ValueStack)。

所以我们的目标很明确就是要去看一下action代理类(ActionProxy)。了解他到底做了什么。才能明白如何找到对应的action类,并执行对应的方法。从上面我们也知道action代理类的新建是通过ActionProxyFactory接口实例来进行的。即是DefaultActionProxyFactory类的实例。显然就是一个简章的工厂模式。让我们看一下新建action代理类的代码吧。

DefaultActionProxyFactory类:

1publicActionProxycreateActionProxy(Stringnamespace,StringactionName,StringmethodName,MapextraContext,booleanexecuteResult,booleancleanupContext){

2

3ActionInvocationinv=createActionInvocation(extraContext,true);

4container.inject(inv);

5returncreateActionProxy(inv,namespace,actionName,methodName,executeResult,cleanupContext);

6}

看到了吧。在新建action代理类的时候还要用到ActionInvocation接口的实例。即是DefaultActionInvocation类的实例。前面几章笔者曾经讲过Dispatcher类才是正真执行action类实例的人。这里笔者不得不在提一下。Dispatcher类是重要的调结者,DefaultActionInvocation类是执行action类实例的行动者。而action代理类(ActionProxy类)则是他们之间的中间人。相当于Dispatcher类通过action代理类(ActionProxy类)命令DefaultActionInvocation类去执行action类实例。

Action代理类的准备工作 action代理类(ActionProxy类)在命令DefaultActionInvocation类去执行action类实例之前,还是有做了一些准备工作。好吧。笔者还是希望通过代码来说话。看一下代码吧。

DefaultActionProxyFactory类:

1publicActionProxycreateActionProxy(ActionInvocationinv,Stringnamespace,StringactionName,StringmethodName,booleanexecuteResult,booleancleanupContext){

2

3DefaultActionProxyproxy=newDefaultActionProxy(inv,namespace,actionName,methodName,executeResult,cleanupContext);

4container.inject(proxy);

5proxy.prepare();

6returnproxy;

7}

DefaultActionProxy类:

1protectedvoidprepare(){

2StringprofileKey="createDefaultActionProxy:";

3try{

4UtilTimerStack.push(profileKey);

5config=configuration.getRuntimeConfiguration().getActionConfig(namespace,actionName);//根据空间命名和action名来找到对应的配置信息

6

7if(config==null&&unknownHandlerManager.hasUnknownHandlers()){

8config=unknownHandlerManager.handleUnknownAction(namespace,actionName);

9}

10if(config==null){

11thrownewConfigurationException(getErrorMessage());

12}

13

14resolveMethod();//找到对应的方法名。

15

16if(config.isAllowedMethod(method)){

17invocation.init(this);

18}else{

19thrownewConfigurationException(prepareNotAllowedErrorMessage());

20}

21}finally{

22UtilTimerStack.pop(profileKey);

23}

24}

从上面的代码,我们可以看出在执行action类之前,大概做了俩件准备工作:

1.获得ActionConfig类实例。并通过ActionConfig类实例找到对应的方法名。ActionConfig类就是存放配置文件里面的action元素节点的信息。

2.实初始化DefaultActionInvocation类的实例。即是根据ActionProxy类实例找到对应的action类实例(用户自己定义的类)。

代码中俩个方法是笔者希望读者明白的。一个是DefaultActionProxy类的resolveMethod方法。一个是DefaultActionInvocation类的init方法。为什么要讲这俩个方法。上面的俩件事情主要的功能都是在这俩个方法里面。让我们看一代码吧?

?DefaultActionProxy类:

1privatevoidresolveMethod(){

2//从配置中获得方法名。如果还是空的话,就用默认的值。即是"execute"方法。

3if(StringUtils.isEmpty(this.method)){

4this.method=config.getMethodName();

5if(StringUtils.isEmpty(this.method)){

6this.method=ActionConfig.DEFAULT_METHOD;

7}

8methodSpecified=false;

9}

10}

DefaultActionInvocation类:

1publicvoidinit(ActionProxyproxy){

2this.proxy=proxy;

3MapcontextMap=createContextMap();

4

5//Settingthissothatotherclasses,likeobjectfactories,canusetheActionProxyandother

6//contextualinformationtooperate

7ActionContextactionContext=ActionContext.getContext();

8

9if(actionContext!=null){

10actionContext.setActionInvocation(this);

11}

12

13createAction(contextMap);//找到对应的action类实例

14

15if(pushAction){

16stack.push(action);

17contextMap.put("action",action);

18}

19

20invocationContext=newActionContext(contextMap);

21invocationContext.setName(proxy.getActionName());

22

23createInterceptors(proxy);

24}

看了代码就能清楚的知道一件事情。如果我们在struts.xml配置文件里面action元素节点里面没有指定方法的时候,就用会默认的方法。即是execute方法。而关于init方法就能明确明白为了找到action类并实例他。init方法里面调用了俩个非重要的方法。一个是用于新建action类实例的方法createAction。一个是用于获得相关拦截器的方法createInterceptors。看一下代码吧。

DefaultActionInvocation类:

1protectedvoidcreateAction(MapcontextMap){

2//loadaction

3StringtimerKey="actionCreate:"+proxy.getActionName();

4try{

5UtilTimerStack.push(timerKey);

6action=objectFactory.buildAction(proxy.getActionName(),proxy.getNamespace(),proxy.getConfig(),contextMap);

7}catch(InstantiationExceptione){

8thrownewXWorkException("UnabletoinstantiateAction!",e,proxy.getConfig());

9}catch(IllegalAccessExceptione){

10thrownewXWorkException("Illegalaccesstoconstructor,isitpublic?",e,proxy.getConfig());

11}catch(Exceptione){

12Stringgripe;

13

14if(proxy==null){

15gripe="Whoa!NoActionProxyinstancefoundincurrentActionInvocation.Thisisbad...verybad";

16}elseif(proxy.getConfig()==null){

17gripe="Sheesh.Where''dthatActionProxygetto?Ican''tfinditinthecurrentActionInvocation!?";

18}elseif(proxy.getConfig().getClassName()==null){

19gripe="NoActiondefinedfor''"+proxy.getActionName()+"''innamespace''"+proxy.getNamespace()+"''";

20}else{

21gripe="UnabletoinstantiateAction,"+proxy.getConfig().getClassName()+",definedfor''"+proxy.getActionName()+"''innamespace''"+proxy.getNamespace()+"''";

22}

23

24gripe+=((("--"+e.getMessage())!=null)?e.getMessage():"[nomessageinexception]");

25thrownewXWorkException(gripe,e,proxy.getConfig());

26}finally{

27UtilTimerStack.pop(timerKey);

28}

29

30if(actionEventListener!=null){

31action=actionEventListener.prepare(action,stack);

32}

33}

DefaultActionInvocation类:

1protectedvoidcreateInterceptors(ActionProxyproxy){

2//GetanewListsowedon''tgetproblemswiththeiteratorifsomeonechangestheoriginallist

3ListinterceptorList=newArrayList<>(proxy.getConfig().getInterceptors());

4interceptors=interceptorList.iterator();

5}

相信读者一定能看的懂代码吧。俩个方法中在笔者看来最重要的体现便是ObjectFactory类。ObjectFactory类是用于新一个实例的。上面的方法里面就是用ObjectFactory类来创建一个action类的实例。

好了。到了这里面action代理类(ActionProxy类)的准备工作算是做完了。让笔者理一下。准备工作完成之后。笔者至少知道action类实例有了。要执行的方法名也有了。要执行的拦截器也有了。有了这些信息难道strtus2会不知道去执行对应的工作吗?

Action代理类的主要工作 action代理类(ActionProxy类)的准备工作完成之后,就开始执行了。最顶部的代码中就很明确的看的出来(serviceAction方法)。先是根据参数mapping来判断是否为直接回返。如果不是才去执行action代理类(ActionProxy类)的execute方法。这便是action代理类(ActionProxy类)的主要工作。即是执行action请求。那么让我们看一下action代理类(ActionProxy类)的execute方法源码吧。

1publicStringexecute()throwsException{

2ActionContextnestedContext=ActionContext.getContext();

3ActionContext.setContext(invocation.getInvocationContext());

4

5StringretCode=null;

6

7StringprofileKey="execute:";

8try{

9UtilTimerStack.push(profileKey);

10

11retCode=invocation.invoke();

12}finally{

13if(cleanupContext){

14ActionContext.setContext(nestedContext);

15}

16UtilTimerStack.pop(profileKey);

17}

18

19returnretCode;

20}

很好。从红色的代码部分我们就知道就是去执行DefaultActionInvocation类实例的invoke方法。DefaultActionInvocation类和action代理类(ActionProxy类)的关系看起相当的复杂。可以说是我中有你,你中有我。DefaultActionProxy类新建的时候需要DefaultActionInvocation类的实例。而DefaultActionInvocation类的实例初始化的时候,action代理类(ActionProxy类)的实例会传DefaultActionInvocation类里面并存放起来。即是在init方法的时候。值得注意的是这个时候的Action上下文(ActionContext类)有发生一件细微的变化。不是以前的了。而是从DefaultActionInvocation类的实例中得来的。cleanupContext参数表示要不要执行完成之后就清除掉当前的。把原来的放在去。最后回返结果。

本章总结 本章主要是讲到关于action代理类(ActionProxy类)的工作。知道了DefaultActionInvocation类是用去执行action类的行动者。而Dispatcher类是调结者。action代理类(ActionProxy类)是DefaultActionInvocation类和Dispatcher类的中间人。即是Dispatcher类通过action代理类(ActionProxy类)命令DefaultActionInvocation类去执行action类实例。

献花(0)
+1
(本文系白狐一梦首藏)