日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
Play!Framework學(xué)習(xí)筆記:ActionInvoker源碼分析

上一篇: Play!Framework學(xué)習(xí)筆記:初識(shí)Play

成都創(chuàng)新互聯(lián)公司是專業(yè)的薌城網(wǎng)站建設(shè)公司,薌城接單;提供成都網(wǎng)站制作、網(wǎng)站建設(shè)、外貿(mào)網(wǎng)站建設(shè),網(wǎng)頁(yè)設(shè)計(jì),網(wǎng)站設(shè)計(jì),建網(wǎng)站,PHP網(wǎng)站建設(shè)等專業(yè)做網(wǎng)站服務(wù);采用PHP框架,可快速的進(jìn)行薌城網(wǎng)站開(kāi)發(fā)網(wǎng)頁(yè)制作和功能擴(kuò)展;專業(yè)做搜索引擎喜愛(ài)的網(wǎng)站,專業(yè)的做網(wǎng)站團(tuán)隊(duì),希望更多企業(yè)前來(lái)合作!

再往下看,我被雷到了= =# 第一遍看沒(méi)反應(yīng)過(guò)來(lái),因?yàn)槲乙?jiàn)識(shí)較淺,也從來(lái)沒(méi)這么寫(xiě)過(guò)代碼,之前也沒(méi)看過(guò)這樣寫(xiě)的。就是這句:

 
 
 
 
  1. throw new RenderTemplate(template, templateBinding.data);

這里用RenderTemplate的構(gòu)造方法new了一個(gè)RenderTemplate對(duì)象,

然后.......拋出去了。

看看這里拋出去的是什么,先進(jìn)去看看RenderTemplate類的實(shí)現(xiàn):

 
 
 
 
  1. public class RenderTemplate extends Result {

再看看Result

 
 
 
 
  1. public abstract class Result extends RuntimeException {

原來(lái)RenderTemplate是個(gè)RuntimeException

@

 關(guān)于RuntimeException
http://java.sun.com/docs/books/tutorial/essential/exceptions/runtime.html
提到RuntimeException,我們最熟悉的可能就是NullPointerException,由于程序編寫(xiě)疏漏,造成訪問(wèn)空指針時(shí),就會(huì)拋出此異常。
我們寫(xiě)個(gè)最簡(jiǎn)單的代碼就是這樣
public class TestString {
public static void main(String[] args){
testNull(null);
}
public static void testNull(String a){
a.charAt(0);
}
}
main方法里對(duì)testNull傳了個(gè)null,但是charAt方法需要String obj,此時(shí)卻是null,觸發(fā)NullPointerException退出程序。
在看看console的信息:
Exception in thread "main" java.lang.NullPointerException
at com.langtest.TestString.testNull(TestString.java:9)
at com.langtest.TestString.main(TestString.java:6)
main 線程引發(fā)空指針異常,程序到main后也沒(méi)對(duì)此異常處理的邏輯,導(dǎo)致程序退出,并在控制臺(tái)打印出錯(cuò)信息。
throw之后的異常對(duì)象沿著方法棧往回扔(即調(diào)用此方法的方法),如果一直沒(méi)有截獲異常,則一直扔到棧底。

既然是個(gè)異常,下一步則是拋向上級(jí)調(diào)用者,往下走,我們找這個(gè)“不是異常的異?!笔窃诤翁幈唤孬@的。。。 (對(duì)比JAVA官網(wǎng)那篇對(duì)運(yùn)行時(shí)異常小心翼翼的陳述,這種做法簡(jiǎn)直有點(diǎn)#_#,要么是因?yàn)槲姨?,不能理解這么用的高明之處吧)。

Debug F6后,程序轉(zhuǎn)至play.mvc.ActionInvoker的invoke方法中的catch語(yǔ)句。

 
 
 
 
  1. Result actionResult = null;
  2. ControllerInstrumentation.initActionCall();
  3. try {
  4.     Java.invokeStatic(actionMethod, getActionMethodArgs(actionMethod));
  5. } catch (InvocationTargetException ex) {
  6.     // It's a Result ? (expected)
  7.     if (ex.getTargetException() instanceof Result) {
  8.         actionResult = (Result) ex.getTargetException();
  9.     } else {
  10.         // @Catch
  11.         Object[] args = new Object[]{ex.getTargetException()};
  12.         List catches = Java.findAllAnnotatedMethods(Controller.getControllerClass(), Catch.class);
  13.         Collections.sort(catches, new Comparator() {

try... catch塊中:

try塊:用JAVA的反射機(jī)制invoke靜態(tài)方法,這里其實(shí)就是invoke了我們?cè)诳刂破髦袑?xiě)的index方法。

@

 如果不理解反射,一定得去先弄明白,任何框架的源碼及要實(shí)現(xiàn)一些共同的對(duì)象實(shí)例化或方法調(diào)用的通用接口,基本都得用到反射= =#
自編最土最俗的方式理解反射幫助同學(xué)理解反射(可能不一定準(zhǔn)確):
反射說(shuō):“我是個(gè)萬(wàn)能的對(duì)象生成器和方法調(diào)用器,只要你給我類名,方法名以及相應(yīng)的參數(shù),我就能幫你new出對(duì)象或者調(diào)用相應(yīng)的方法”
不過(guò)任何事情都是雙刃劍,反射的能力過(guò)于強(qiáng)大,可以任意的生成和操作對(duì)象,所以就賦予程序員Do evil的可能.
而JAVA本身的設(shè)計(jì)是個(gè)盡量安全的語(yǔ)言,沒(méi)有指針運(yùn)算,虛擬機(jī)幫著整理內(nèi)存,異常機(jī)制讓做出健壯性的程序變得簡(jiǎn)化不少,所以我覺(jué)得反射機(jī)制對(duì)于JAVA的總體設(shè)計(jì)思想來(lái)說(shuō),還是不小的challenge。
此處的invokeStatic方法的兩個(gè)參數(shù),用debug工具的watch功能可以看到
actionMethod就是通過(guò)反射被invoke的方法。

catch塊:攔截InvocationTargetException,這個(gè)exception是當(dāng)通過(guò)反射的方式invoke的方法throw異常時(shí),反射機(jī)制會(huì)觸發(fā)這個(gè)異常,并將上一級(jí)throw出的異常存為這個(gè)異常的taget變量。

本例的過(guò)程是這樣的,Play框架通過(guò)反射的方式invoke 控制器中的index方法(Application.index()),然后進(jìn)入render(),在render方法里調(diào)用renderTemplate方法,在此方法將RenderTemplate這個(gè)異常(再次汗)拋出,反射機(jī)制發(fā)現(xiàn)有異常拋出,隨后拋出InvocationTargetException異常,并將RenderTemplate存入InvocationTargetException的target變量..Play在使用反射invoke方法處catch了此異常,然后把target引用的RenderTemplate取出,則得到了render完成的模板。

from Java Api

InvocationTargetException is a checked exception that wraps an exception thrown by an invoked method or constructor.

@

 此處雖然搞明白了這段詭異代碼的用途,其實(shí)也只是個(gè)學(xué)習(xí)的開(kāi)始,我覺(jué)得學(xué)習(xí)的入門(mén)是讀懂,能應(yīng)用是其次,能對(duì)某個(gè)事物有批判性思維(知道好壞,知道適用何處才是更高層次)。
無(wú)論何時(shí),我們都得有追本溯源的精神,下面是幾個(gè)疑問(wèn):
①為什么框架這么設(shè)計(jì)
②如果可以,自己想一個(gè)替代的實(shí)現(xiàn)方式,對(duì)比此方式看看為何要用這么怪的設(shè)計(jì)

ActionInvoker源碼分析

既然現(xiàn)在Debug走到ActionInvoker,不妨看看這個(gè)類:

由類上的注釋:

Invoke an action after an HTTP request.

這個(gè)類是根據(jù)Http request Invoke相應(yīng)的action。

這個(gè)類沒(méi)有成員變量和函數(shù),只有三個(gè)共有的靜態(tài)方法,這三個(gè)方法分別是(用附加注釋的方法解釋):

 
 
 
 
  1. public class ActionInvoker {
  2.         //響應(yīng)請(qǐng)求的主函數(shù),其實(shí)ActionInvoker這個(gè)類主要用途就是放置這個(gè)方法,因此這個(gè)類也同樣也不具備面向?qū)ο筇匦缘念?,這個(gè)類注重的是響應(yīng)HTTP請(qǐng)求的邏輯
  3.         public static void invoke(Http.Request request, Http.Response response) {
  4.         //通過(guò)傳入的action(ie:Application.index),得到對(duì)應(yīng)的method,以便反射時(shí)invoke使用
  5.         public static Object[] getActionMethod(String fullAction) {
  6.         //從method中取出方法的參數(shù),這兩個(gè)get方法都是為反射調(diào)用服務(wù)的。
  7.         public static Object[] getActionMethodArgs(Method method) throws Exception {
  8. }

可見(jiàn)invoke是Play框架的運(yùn)行的核心控件(說(shuō)是核心是因?yàn)閣eb框架的主要職責(zé)就是完成處理HTTP請(qǐng)求的過(guò)程)。

為了了解Play的核心運(yùn)行機(jī)制,我們斷開(kāi)debug線程,在invoke方法設(shè)上斷點(diǎn),重新跑Debug。

進(jìn)入方法,傳入該方法的兩個(gè)參數(shù)是由上一層調(diào)用者HttpHandler的內(nèi)部類MinaInvocation的execute方法傳入的。由于HttpHandler里做的工作比ActionInvoker更加基礎(chǔ)(Mina應(yīng)用服務(wù)器下的http協(xié)議處理及session管理),我們到后面再研究。

 
 
 
 
  1. public static void invoke(Http.Request request, Http.Response response) {    
  2.    Monitor monitor = null;
  3.         try {
  4.             if (!Play.started) {
  5.                 return;
  6.             }
  7.             Http.Request.current.set(request);
  8.             Http.Response.current.set(response);
  9.             Scope.Params.current.set(new Scope.Params());
  10.             Scope.RenderArgs.current.set(new Scope.RenderArgs());
  11.             Scope.Session.current.set(Scope.Session.restore());
  12.             Scope.Flash.current.set(Scope.Flash.restore());
  13. ......
  14. }

先new一個(gè)Monitor ,用來(lái)監(jiān)控。

然后判斷Play是否啟動(dòng)。

隨后的是一系列xxx.xxx.current.set方法:

這里的current變量都是ThreadLocal:

 
 
 
 
  1. public static ThreadLocal current = new ThreadLocal();

@

 
 
 
 
  1. //對(duì)于Java開(kāi)發(fā),ThreadLocal是必須要了解的概念。 
  2. //ThreadLocal雖然是個(gè)對(duì)象,但是ThreadLocal的set方法存的東西并不是放在ThreadLocal對(duì)象里 
  3. /** 
  4. * Sets the current thread's copy of this thread-local variable 
  5. * to the specified value. Many applications will have no need for 
  6. * this functionality, relying solely on the {@link #initialValue} 
  7. * method to set the values of thread-locals. 
  8. * @param value the value to be stored in the current threads' copy of 
  9. * this thread-local. 
  10. */ 
  11. public void set(T value) { 
  12. Thread t = Thread.currentThread(); 
  13. ThreadLocalMap map = getMap(t); 
  14. if (map != null) 
  15. map.set(this, value); 
  16. else 
  17. createMap(t, value); 
  18. //由上可見(jiàn),set方法首先取得當(dāng)前的Thread對(duì)象,然后取得該線程的ThreadLocalMap ,如果map不為空,則寫(xiě)入map,以當(dāng)前的ThreadLocal對(duì)象為key,將傳入的value存入map。 
  19. //這里也只是個(gè)引子,沒(méi)概念的可能很難理解清楚,畢竟ThreadLocal也不是我這么三言兩語(yǔ)能說(shuō)清的,建議同學(xué)多谷哥一下,多看多用多體會(huì)。 

將Request,Response以及Scope的引用放入當(dāng)前線程后,實(shí)際上是完成了線程的初始化過(guò)程。

 
 
 
 
  1. // 1. Route and resolve format if not already done
  2. if (request.action == null) {
  3.     for (PlayPlugin plugin : Play.plugins) {
  4.         plugin.routeRequest(request);
  5.     }
  6.     Router.route(request);
  7. }
  8. request.resolveFormat();

Router.route(request); 根據(jù)請(qǐng)求的URL找到router中相應(yīng)的action,并將action的名字賦值給request.action。

request.resolveFormat();此時(shí)request中format為html,如果request中format為null,則根據(jù)http頭來(lái)取得相應(yīng)的format。

往下走:

 
 
 
 
  1. // 2. Find the action method
  2.  Method actionMethod = null;
  3.  try {
  4.      Object[] ca = getActionMethod(request.action);
  5.      actionMethod = (Method) ca[1];
  6.      request.controller = ((Class) ca[0]).getName().substring(12);
  7.      request.actionMethod = actionMethod.getName();
  8.      request.action = request.controller + "." + request.actionMethod;
  9.      request.invokedMethod = actionMethod;
  10.  } catch (ActionNotFoundException e) {
  11.      throw new NotFound(String.format("%s action not found", e.getAction()));
  12.  }

聲明一個(gè)Method變量,供后面反射Invoke。

getActionMethod(request.action) 前面提到過(guò)了,通過(guò)request.action這個(gè)String得到存有application.index()方法相應(yīng)Method對(duì)象的obj數(shù)組。

得到Method對(duì)象(ca[1],ca[0]存放的是對(duì)應(yīng)controllers.Application的Class對(duì)象)后,將request對(duì)象中與Action相關(guān)的成員變量賦值。

此處:request.controller值為Application,request.actionMethod值為index,后面兩個(gè)變量,一個(gè)是照前兩個(gè)拼出來(lái)的action,另一個(gè)傳入的是Method對(duì)象。

繼續(xù):下面的代碼為合并action用到的參數(shù):

 
 
 
 
  1. // 3. Prepare request params
  2. Scope.Params.current().__mergeWith(request.routeArgs);
  3. // add parameters from the URI query string 
  4. Scope.Params.current()._mergeWith(UrlEncodedParser.parseQueryString(new ByteArrayInputStream(request.querystring.getBytes("utf-8"))));
  5. Lang.resolvefrom(request);

routeArgs是在route中附加的http參數(shù):

 
 
 
 
  1. /**
  2.  * Additinal HTTP params extracted from route
  3.  */
  4. public Map routeArgs;

除此之外還將QueryString中的參數(shù)也合并進(jìn)來(lái)。

后面的Lang.resolvefrom(request)沒(méi)仔細(xì)看實(shí)現(xiàn),看Lang的包名中與i18n有關(guān),這部分等以后專門(mén)看國(guó)際化的實(shí)現(xiàn)單獨(dú)寫(xiě)吧(繼續(xù)欠賬)。

下面的代碼,又看到雷人的片段了...

 
 
 
 
  1. // 4. Easy debugging ...
  2. if (Play.mode == Play.Mode.DEV) {
  3.     Controller.class.getDeclaredField("params").set(null, Scope.Params.current());
  4.     Controller.class.getDeclaredField("request").set(null, Http.Request.current());
  5.     Controller.class.getDeclaredField("response").set(null, Http.Response.current());
  6.     Controller.class.getDeclaredField("session").set(null, Scope.Session.current());
  7.     Controller.class.getDeclaredField("flash").set(null, Scope.Flash.current());
  8.     Controller.class.getDeclaredField("renderArgs").set(null, Scope.RenderArgs.current());
  9.     Controller.class.getDeclaredField("validation").set(null, Java.invokeStatic(Validation.class, "current"));
  10. }

!!!!!!

Controller.class.getDeclaredField("xxx").set(null,xxx);

這里Play用反射的方式將Controller中受保護(hù)的靜態(tài)變量強(qiáng)行賦值?。?!

@

 如果之前將程序處理結(jié)果實(shí)現(xiàn)為運(yùn)行時(shí)異常并在產(chǎn)生結(jié)果后直接拋出可以暫時(shí)理解為一種策略,那這種做法簡(jiǎn)直就是簡(jiǎn)單粗暴了,和我們讀的各種經(jīng)典書(shū)籍中的教誨大相徑庭。

而且還是自己設(shè)置了變量的訪問(wèn)權(quán)限后又自己暴力破解賦值......

不過(guò)作者在注釋里加了句// 4. Easy debugging ...,可能是對(duì)此做法無(wú)奈的解釋吧。

讓我不禁又想起那句"Java中的關(guān)鍵字只是給編譯器看的"...

不過(guò)仔細(xì)想想,這可能也是開(kāi)源項(xiàng)目的特色之一,如果在企業(yè)里寫(xiě)這種代碼直接破壞框架,不知道老板的臉會(huì)怎么黑- -|||..

這里廢話這么多,是因?yàn)槲覀儗W(xué)生在校時(shí)往往喜歡比較另類的代碼,所以學(xué)弟學(xué)妹需要醒目一下:
仔細(xì)想想
我們初涉編程是不是很佩服算出i=3,k=(i++)+(++i)+(i++)的同學(xué)
我們是不是佩服過(guò)能看懂16層嵌套并且變量名沒(méi)任何意義的if else
我們是不是對(duì)于能寫(xiě)出令人頭暈的指針運(yùn)算的同學(xué)無(wú)比崇敬過(guò)。

自己玩玩可以,但是以后工作,一份代碼的生命期有可能是伴隨著企業(yè)的生命期。
寫(xiě)一些另類代碼有很多惡果,最明顯的是難以維護(hù)。
至于類似修改私有變量的危險(xiǎn)行為,雖然用起來(lái)比較cool,但是對(duì)于個(gè)人學(xué)習(xí)來(lái)說(shuō),不提倡,盡量不要給自己灌輸用非正常途徑解決問(wèn)題的思想。不過(guò)這里,沒(méi)說(shuō)play不好的意思,只是對(duì)于咱們來(lái)說(shuō),學(xué)生階段對(duì)語(yǔ)言的理解力掌控力還太差,需要不斷深入學(xué)習(xí),假以時(shí)日,能有技術(shù)方面的批判性思維,能把這些java特性用的恰到好處當(dāng)然是好事。

又跑題了,回到主題....這部分是判斷play的模式(play有兩種運(yùn)行模式DEV和實(shí)際運(yùn)行模式,在config里文件配置切換),在開(kāi)發(fā)模式下,直接將request,response和scope等賦值給Cotroller類相應(yīng)的靜態(tài)變量。

可能便于實(shí)際invoke控制器時(shí)訪問(wèn)這些值。

#遍歷各個(gè)PlugIn看在action invoke前做些動(dòng)作。

 
 
 
 
  1. ControllerInstrumentation.stopActionCall();
  2. for (PlayPlugin plugin : Play.plugins) {
  3.     plugin.beforeActionInvocation(actionMethod);
  4. }

ControllerInstrumentation這個(gè)類的作用是對(duì)allow這個(gè)標(biāo)志位進(jìn)行操作,allow是個(gè)ThreadLocal,對(duì)其set值則將其引用存入當(dāng)前Thread內(nèi),換句話說(shuō),其實(shí)是對(duì)Thread做了標(biāo)記。

 
 
 
 
  1. public static class ControllerInstrumentation {
  2.      
  3.      public static boolean isActionCallAllowed() {
  4.          return allow.get();
  5.      }
  6.      
  7.      public static void initActionCall() {
  8.          allow.set(true);
  9.      }
  10.      
  11.      public static void stopActionCall() {
  12.          allow.set(false);
  13.      }
  14.      
  15.      static ThreadLocal allow = new ThreadLocal();       
  16.      
  17.  }

beforeActionInvocation方法則是在action前Plugin做的事情,這里我看了一下都是空的實(shí)現(xiàn)。

#打開(kāi)monitor

 
 
 
 
  1. // Monitoring
  2. monitor = MonitorFactory.start(request.action + "()");

#找到標(biāo)記@Before Annotation的方法,并先于action invoke執(zhí)行。

 
 
 
 
  1. // 5. Invoke the action
  2. try {
  3.     // @Before
  4.     List befores = Java.findAllAnnotatedMethods(Controller.getControllerClass(), Before.class);
  5.     Collections.sort(befores, new Comparator() {
  6.         public int compare(Method m1, Method m2) {
  7.             Before before1 = m1.getAnnotation(Before.class);
  8.             Before before2 = m2.getAnnotation(Before.class);
  9.             return before1.priority() - before2.priority();
  10.         }
  11.     });

Controller.getControllerClass()方法返回class controllers.Application。

Java.findAllAnnotatedMethods()找到所有帶有@Before Annotation的方法。

再根據(jù)各個(gè)方法的優(yōu)先級(jí),來(lái)對(duì)befores中的Method排序。

此處實(shí)現(xiàn)比較器用了匿名內(nèi)部類,按Before的priority進(jìn)行排序。

 
 
 
 
  1. @Retention(RetentionPolicy.RUNTIME)
  2. @Target(ElementType.METHOD)
  3. public @interface Before {
  4.     
  5.     /**
  6.      * Does not intercept these actions
  7.      */
  8.     String[] unless() default {};
  9.     
  10.     /**
  11.      * Interceptor priority (0 is high priority)
  12.      */
  13.     int priority() default 0;
  14.     
  15. }

Annotation Before除了成員變量priority外,還有一個(gè)String數(shù)組變量unless,存放的是action的名字,表示不攔截這些action。

看看這部分的實(shí)現(xiàn)。

 
 
 
 
  1. ControllerInstrumentation.stopActionCall();
  2.    //遍歷包含Before Annotation的方法
  3. for (Method before : befores) {
  4.        //取出當(dāng)前Before action的unless數(shù)組
  5.     String[] unless = before.getAnnotation(Before.class).unless();
  6.        //設(shè)置標(biāo)志位
  7.     boolean skip = false;
  8.        //遍歷unless數(shù)組
  9.     for (String un : unless) {
  10.         if (!un.contains(".")) {
  11.             un = before.getDeclaringClass().getName().substring(12) + "." + un;
  12.         }
  13.           //如果unless與當(dāng)前被調(diào)用的action名字相同,標(biāo)志位skip設(shè)為true,退出循環(huán)
  14.         if (un.equals(request.action)) {
  15.             skip = true;
  16.             break;
  17.         }
  18.     }
  19.       //如果skip為false,調(diào)用before方法
  20.     if (!skip) {
  21.            //加個(gè)保護(hù),判斷被調(diào)用方法是否為靜態(tài),因?yàn)橄旅嬗玫降檬莍nvokeStatic..
  22.         if (Modifier.isStatic(before.getModifiers())) {
  23.             before.setAccessible(true);
  24.             Java.invokeStatic(before, getActionMethodArgs(before));
  25.         }
  26.     }
  27. }

通過(guò)Before攔截器后,再往下就是我們前面看到的實(shí)際執(zhí)行Action的地方:

 
 
 
 
  1. //聲明一個(gè)Result變量用來(lái)保存方法調(diào)用的結(jié)構(gòu)
  2. Result actionResult = null;
  3.       //與之前stopActionCall()相反,這里調(diào)用initActionCall()將allow設(shè)為true,意思是允許此線程invoke方法
  4.       ControllerInstrumentation.initActionCall();
  5.       try {
  6.           //invoke action
  7.           Java.invokeStatic(actionMethod, getActionMethodArgs(actionMethod));
  8.       } catch (InvocationTargetException ex) {
  9.           // It's a Result ? (expected)  
  10.           if (ex.getTargetException() instanceof Result) {
  11.               //得到調(diào)用action后返回的Result
  12.               actionResult = (Result) ex.getTargetException();
  13.               //else部分本例未涉及,先跳過(guò)不管
  14.           } else {
  15.           .....

執(zhí)行完action,下面的代碼部分是After攔截器,和Before基本一致,不贅述。

隨后將monitor關(guān)閉。

之后...繼續(xù)將返回結(jié)果往上扔。

 
 
 
 
  1. // Ok, rethrow the original action result
  2. if (actionResult != null) {
  3.     throw actionResult;
  4. }
  5. throw new NoResult();
 
 
 
 
  1. catch (InvocationTargetException ex) {  
  2.                 // It's a Result ? (expected)  
  3.                 if (ex.getTargetException() instanceof Result) {  
  4.                     throw (Result) ex.getTargetException();  
  5.                 }  
  6.                 // Rethrow the enclosed exception  
  7.                 if (ex.getTargetException() instanceof PlayException) {  
  8.                     throw (PlayException) ex.getTargetException();  
  9.                 }  
  10.                 StackTraceElement element = PlayException.getInterestingStrackTraceElement(ex.getTargetException());  
  11.                 if (element != null) {  
  12.                     throw new JavaExecutionException(Play.classes.getApplicationClass(element.getClassName()), element.getLineNumber(), ex.getTargetException());  
  13.                 }  
  14.                 throw new JavaExecutionException(Http.Request.current().action, ex);  
  15.             }  

一直扔到invoke方法的第一個(gè)try..catch塊。

 
 
 
 
  1. public static void invoke(Http.Request request, Http.Response response) {
  2.      Monitor monitor = null;
  3.      try {
  4.          .......
  5.      }catch (Result result) {
  6.          //遍歷執(zhí)行plugin的onActionInvocationResult()方法,對(duì)結(jié)果進(jìn)行處理
  7.          for (PlayPlugin plugin : Play.plugins) {
  8.              plugin.onActionInvocationResult(result);
  9.          }
  10.          // Ok there is a result to apply
  11.          // Save session & flash scope now
  12.          Scope.Session.current().save();
  13.          Scope.Flash.current().save();
  14.          //相應(yīng)結(jié)果的apply方法,此處result實(shí)際是RenderTemplate對(duì)象,它的apply方法最終的HTML輸出
  15.          result.apply(request, response);
  16.          //這里可見(jiàn)Plugin的功能是非常靈活的,因?yàn)閹缀踉赼ction生命期的每階段都出現(xiàn),其實(shí)到后面可以發(fā)現(xiàn)PlugIn幾乎隨處可見(jiàn),否則怎么能叫做框架的插件呢= =#
  17.          for (PlayPlugin plugin : Play.plugins) {
  18.              plugin.afterActionInvocation();
  19.          }

最后看看RenderTemplate類

 
 
 
 
  1. public class RenderTemplate extends Result {
  2.     
  3.     private Template template;
  4.     private String content;
  5.     Map args;
  6.     
  7.     public RenderTemplate(Template template, Map args) {
  8.         this.template = template;
  9.         this.args = args;
  10.         this.content = template.render(args);
  11.     }
  12.     //apply方法是在invoke方法截獲Result后,確認(rèn)其是需要的返回結(jié)果后,調(diào)用的結(jié)果最終執(zhí)行代碼
  13.     public void apply(Request request, Response response) {
  14.         try {
  15.             setContentTypeIfNotSet(response, MimeTypes.getContentType(template.name, "text/plain"));
  16.             response.out.write(content.getBytes("utf-8"));
  17.         } catch(Exception e) {
  18.             throw new UnexpectedException(e);
  19.         }
  20.     }
  21. }

執(zhí)行完結(jié)果代碼后,來(lái)到Invoke方法的結(jié)尾處,仍處于catch塊,即找到@final的方法并執(zhí)行。

 
 
 
 
  1. // @Finally
  2.    //這個(gè)if判斷不知道有什么意義,前面在get action的時(shí)候,就是找Application(Controller Class)的action方法,此處怎么會(huì)得到null呢,等以后理解加深再解釋吧。
  3.    if (Controller.getControllerClass() != null) {
  4.        try {
  5.            List allFinally = Java.findAllAnnotatedMethods(Controller.getControllerClass(), Finally.class);
  6.            //后面略,與@before和@after同
  7.            }

Controller.getControllerClass()這個(gè)方法,涉及到Play的classloader,大概看了一下,比較復(fù)雜,等以后專門(mén)研究。

不過(guò)其中發(fā)現(xiàn)一些比較核心的與play熱加載功能相關(guān)的代碼,如下:

 
 
 
 
  1. byte[] bc = BytecodeCache.getBytecode(name, applicationClass.javaSource);
  2.                if (bc != null) {
  3.                    applicationClass.enhancedByteCode = bc;
  4.                    applicationClass.javaClass = defineClass(applicationClass.name, applicationClass.enhancedByteCode, 0, applicationClass.enhancedByteCode.length, protectionDomain);
  5.                    resolveClass(applicationClass.javaClass);
  6.                    Logger.trace("%sms to load class %s from cache", System.currentTimeMillis() - start, name);
  7.                    return applicationClass.javaClass;
  8.                }

這里大概能看出,play可以直接通過(guò)讀java源代碼來(lái)動(dòng)態(tài)的生成java class.這應(yīng)該與Play修改代碼不需編譯就能運(yùn)行有關(guān)。

小結(jié):到此處,從兩個(gè)層次學(xué)習(xí)了Play框架的中處理和響應(yīng)請(qǐng)求的模塊。

最里面一層是Controller層,就是Application,這里放置的是Request最終invoke的action

往外一層是ActionInvoker,負(fù)責(zé)通過(guò)Http Request來(lái)判斷需要調(diào)用的action,并執(zhí)行調(diào)用,此外,還對(duì)action起攔截器作用分別在action的生命期的幾個(gè)階段Before,After和Finally階段進(jìn)行攔截并執(zhí)行有相應(yīng)Annotation的方法。除了上述兩個(gè)作用,ActionInvoker還負(fù)責(zé)執(zhí)行PlugIn??梢钥闯鯝ctionInvoker的職責(zé)是控制action。

由此容易想到,ActionInvoker外面應(yīng)該還有一層,負(fù)責(zé)實(shí)際獲取客戶端的HTTP Request,并轉(zhuǎn)給ActionInvoker,是的,這個(gè)類就是HttpHandler,在下一篇我會(huì)詳細(xì)分析。

畫(huà)圖表示從客戶端的Request進(jìn)入Play到Response返回并跳出Play的過(guò)程

呼,寫(xiě)了半天今天就寫(xiě)了這么短個(gè)小節(jié),這么寫(xiě)下去不知寫(xiě)到猴年馬月了,~~~~(>_<)~~~~

下一篇會(huì)重點(diǎn)分析HttpHandler源碼,從而更加深刻理解此流程,這篇就到此為止。


標(biāo)題名稱:Play!Framework學(xué)習(xí)筆記:ActionInvoker源碼分析
URL分享:http://m.5511xx.com/article/cdhejos.html