2013年6月20日


          版權(quán)信息: 可以任意轉(zhuǎn)載, 轉(zhuǎn)載時請務(wù)必以超鏈接形式標(biāo)明文章原文出處, 即下面的聲明.

          原文出處:http://blog.chenlb.com/2009/06/java-classloader-architecture.html

          jvm classLoader architecture:

          1. Bootstrap ClassLoader/啟動類加載器 
            主要負(fù)責(zé)jdk_home/lib目錄下的核心 api 或 -Xbootclasspath 選項指定的jar包裝入工作。
          2. Extension ClassLoader/擴展類加載器 
            主要負(fù)責(zé)jdk_home/lib/ext目錄下的jar包或 -Djava.ext.dirs 指定目錄下的jar包裝入工作。
          3. System ClassLoader/系統(tǒng)類加載器 
            主要負(fù)責(zé)java -classpath/-Djava.class.path所指的目錄下的類與jar包裝入工作。
          4. User Custom ClassLoader/用戶自定義類加載器(java.lang.ClassLoader的子類) 
            在程序運行期間, 通過java.lang.ClassLoader的子類動態(tài)加載class文件, 體現(xiàn)java動態(tài)實時類裝入特性。

          類加載器的特性:

          1. 每個ClassLoader都維護了一份自己的名稱空間, 同一個名稱空間里不能出現(xiàn)兩個同名的類。
          2. 為了實現(xiàn)java安全沙箱模型頂層的類加載器安全機制, java默認(rèn)采用了 " 雙親委派的加載鏈 " 結(jié)構(gòu)。
          classloader-architecture

          classloader-architecture

          classloader-class-diagram

          classloader-class-diagram

          類圖中, BootstrapClassLoader是一個單獨的java類, 其實在這里, 不應(yīng)該叫他是一個java類。因為,它已經(jīng)完全不用java實現(xiàn)了。它是在jvm啟動時, 就被構(gòu)造起來的, 負(fù)責(zé)java平臺核心庫。

          自定義類加載器加載一個類的步驟

          classloader-load-class

          classloader-load-class

          ClassLoader 類加載邏輯分析, 以下邏輯是除 BootstrapClassLoader 外的類加載器加載流程:

          1. // 檢查類是否已被裝載過  
          2. Class c = findLoadedClass(name);  
          3. if (c == null ) {  
          4.      // 指定類未被裝載過  
          5.      try {  
          6.          if (parent != null ) {  
          7.              // 如果父類加載器不為空, 則委派給父類加載  
          8.              c = parent.loadClass(name, false );  
          9.          } else {  
          10.              // 如果父類加載器為空, 則委派給啟動類加載加載  
          11.              c = findBootstrapClass0(name);  
          12.          }  
          13.      } catch (ClassNotFoundException e) {  
          14.          // 啟動類加載器或父類加載器拋出異常后, 當(dāng)前類加載器將其  
          15.          // 捕獲, 并通過findClass方法, 由自身加載  
          16.          c = findClass(name);  
          17.      }  
          18. }  

          線程上下文類加載器
          java默認(rèn)的線程上下文類加載器是 系統(tǒng)類加載器(AppClassLoader)。

          1. // Now create the class loader to use to launch the application  
          2. try {  
          3.     loader = AppClassLoader.getAppClassLoader(extcl);  
          4. catch (IOException e) {  
          5.     throw new InternalError(  
          6. "Could not create application class loader" );  
          7. }   
          8.   
          9. // Also set the context class loader for the primordial thread.  
          10. Thread.currentThread().setContextClassLoader(loader);  

          以上代碼摘自sun.misc.Launch的無參構(gòu)造函數(shù)Launch()。

          使用線程上下文類加載器, 可以在執(zhí)行線程中, 拋棄雙親委派加載鏈模式, 使用線程上下文里的類加載器加載類.
          典型的例子有, 通過線程上下文來加載第三方庫jndi實現(xiàn), 而不依賴于雙親委派.
          大部分java app服務(wù)器(jboss, tomcat..)也是采用contextClassLoader來處理web服務(wù)。
          還有一些采用 hotswap 特性的框架, 也使用了線程上下文類加載器, 比如 seasar (full stack framework in japenese).

          線程上下文從根本解決了一般應(yīng)用不能違背雙親委派模式的問題.
          使java類加載體系顯得更靈活.

          隨著多核時代的來臨, 相信多線程開發(fā)將會越來越多地進入程序員的實際編碼過程中. 因此,
          在編寫基礎(chǔ)設(shè)施時, 通過使用線程上下文來加載類, 應(yīng)該是一個很好的選擇。

          當(dāng)然, 好東西都有利弊. 使用線程上下文加載類, 也要注意, 保證多根需要通信的線程間的類加載器應(yīng)該是同一個,
          防止因為不同的類加載器, 導(dǎo)致類型轉(zhuǎn)換異常(ClassCastException)。

          為什么要使用這種雙親委托模式呢?

          1. 因為這樣可以避免重復(fù)加載,當(dāng)父親已經(jīng)加載了該類的時候,就沒有必要子ClassLoader再加載一次。
          2. 考慮到安全因素,我們試想一下,如果不使用這種委托模式,那我們就可以隨時使用自定義的String來動態(tài)替代java核心api中定義類型,這樣會存在非常大的安全隱患,而雙親委托的方式,就可以避免這種情況,因為String已經(jīng)在啟動時被加載,所以用戶自定義類是無法加載一個自定義的ClassLoader。

          java動態(tài)載入class的兩種方式:

          1. implicit隱式,即利用實例化才載入的特性來動態(tài)載入class
          2. explicit顯式方式,又分兩種方式:
            1. java.lang.Class的forName()方法
            2. java.lang.ClassLoader的loadClass()方法

          用Class.forName加載類

          Class.forName使用的是被調(diào)用者的類加載器來加載類的。
          這種特性, 證明了java類加載器中的名稱空間是唯一的, 不會相互干擾。
          即在一般情況下, 保證同一個類中所關(guān)聯(lián)的其他類都是由當(dāng)前類的類加載器所加載的。

          1. public static Class forName(String className)  
          2.      throws ClassNotFoundException {  
          3.      return forName0(className, true , ClassLoader.getCallerClassLoader());  
          4. }   
          5.   
          6. /** Called after security checks have been made. */  
          7. private static native Class forName0(String name, boolean initialize,  
          8. ClassLoader loader)  
          9.      throws ClassNotFoundException;  

          上面中 ClassLoader.getCallerClassLoader 就是得到調(diào)用當(dāng)前forName方法的類的類加載器

          static塊在什么時候執(zhí)行?

          • 當(dāng)調(diào)用forName(String)載入class時執(zhí)行,如果調(diào)用ClassLoader.loadClass并不會執(zhí)行.forName(String,false,ClassLoader)時也不會執(zhí)行.
          • 如果載入Class時沒有執(zhí)行static塊則在第一次實例化時執(zhí)行.比如new ,Class.newInstance()操作
          • static塊僅執(zhí)行一次

          各個java類由哪些classLoader加載?

          • java類可以通過實例.getClass.getClassLoader()得知
          • 接口由AppClassLoader(System ClassLoader,可以由ClassLoader.getSystemClassLoader()獲得實例)載入
          • ClassLoader類由bootstrap loader載入

          NoClassDefFoundError和ClassNotFoundException

          • NoClassDefFoundError:當(dāng)java源文件已編譯成.class文件,但是ClassLoader在運行期間在其搜尋路徑load某個類時,沒有找到.class文件則報這個錯
          • ClassNotFoundException:試圖通過一個String變量來創(chuàng)建一個Class類時不成功則拋出這個異常
          posted @ 2013-06-20 10:25 陳睿 閱讀(371) | 評論 (1)編輯 收藏

          2012年7月3日

          一:quartz簡介
                 OpenSymphony 的Quartz提供了一個比較完美的任務(wù)調(diào)度解決方案。
                 Quartz 是個開源的作業(yè)調(diào)度框架,定時調(diào)度器,為在 Java 應(yīng)用程序中進行作業(yè)調(diào)度提供了簡單卻強大的機制。
                 Quartz中有兩個基本概念:作業(yè)和觸發(fā)器。作業(yè)是能夠調(diào)度的可執(zhí)行任務(wù),觸發(fā)器提供了對作業(yè)的調(diào)度

          二:quartz spring配置詳解
          •  為什么不適用java.util.Timer結(jié)合java.util.TimerTask 
                  1.主要的原因,適用不方便,特別是制定具體的年月日時分的時間,而quartz使用類似linux上的cron配置,很方便的配置每隔時間執(zhí)行觸發(fā)。

                  2.其次性能的原因,使用jdk自帶的Timer不具備多線程,而quartz采用線程池,性能上比timer高出很多。


          •    詳解quartz在spring里面的配置
              在spring里主要分為兩種使用方式:第一種,也是目前使用最多的方式,spring提供的MethodInvokingJobDetailFactoryBean代理類,通過雷利類直接調(diào)用任務(wù)類的某個函數(shù);第二種,程序里實現(xiàn)quartz接口,quartz通過該接口進行調(diào)度。

                主要講解通過spring提供的代理類MethodInvokingJobDetailFactoryBean

                  1.業(yè)務(wù)邏輯類:業(yè)務(wù)邏輯是獨立的,本身就與quartz解耦的,并沒有深入進去,這對業(yè)務(wù)來講是很好的一個方式。

                                  public class  TestJobTask{
               

                /**
                 *業(yè)務(wù)邏輯處理
                 
          */
                  public void   service(){
                      /**業(yè)務(wù)邏輯*/
                          ..
                  }

          }
                 
              2.增加一個線程池
              <!-- 線程執(zhí)行器配置,用于任務(wù)注冊 -->
          <bean id="executor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
           <property name="corePoolSize" value="10" />
           <property name="maxPoolSize" value="100" />
           <property name="queueCapacity" value="500" />
          </bean>

            3.定義業(yè)務(wù)邏輯類

              <!-- 業(yè)務(wù)對象 -->
          <bean id="testJobTask" class="com.mike.scheduling.TestJobTask" />


              4.增加quartz調(diào)用業(yè)務(wù)邏輯

              <!-- 調(diào)度業(yè)務(wù) -->
          <bean id="jobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
           <property name="targetObject" ref="testJobTask" />
           <property name="targetMethod" value="service" />
          </bean>

              5.增加調(diào)用的觸發(fā)器,觸發(fā)的時間,有兩種方式:

               第一種觸發(fā)時間,采用類似linux的cron,配置時間的表示發(fā)出豐富  
            <bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
           <property name="jobDetail" ref="jobDetail" />
           <property name="cronExpression" value="10 0/1 * * * ?" />
          </bean>
            Cron表達式“10 */1 * * * ?”意為:從10秒開始,每1分鐘執(zhí)行一次 
            
              第二種,采用比較簡話的方式,申明延遲時間和間隔時間
            <bean id="taskTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean">
           <property name="jobDetail" ref="jobDetail" />
           <property name="startDelay" value="10000" />
           <property name="repeatInterval" value="60000" />
          </bean>
            延遲10秒啟動,然后每隔1分鐘執(zhí)行一次 

              6.開始調(diào)用

                <!-- 設(shè)置調(diào)度 -->
          <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
           <property name="triggers">
            <list>
             <ref bean="cronTrigger" />
            </list>
           </property>
           <property name="taskExecutor" ref="executor" />
          </bean>

             7.結(jié)束:啟動容器即可,已經(jīng)將spring和quartz結(jié)合完畢。

              Cron常用的表達式
              "0 0 12 * * ?" 每天中午12點觸發(fā)
          "0 15 10 ? * *" 每天上午10:15觸發(fā)
          "0 15 10 * * ?" 每天上午10:15觸發(fā)
          "0 15 10 * * ? *" 每天上午10:15觸發(fā)
          "0 15 10 * * ? 2005" 2005年的每天上午10:15觸發(fā)
          "0 * 14 * * ?" 在每天下午2點到下午2:59期間的每1分鐘觸發(fā)
          "0 0/5 14 * * ?" 在每天下午2點到下午2:55期間的每5分鐘觸發(fā)
          "0 0/5 14,18 * * ?" 在每天下午2點到2:55期間和下午6點到6:55期間的每5分鐘觸發(fā)
          "0 0-5 14 * * ?" 在每天下午2點到下午2:05期間的每1分鐘觸發(fā)
          "0 10,44 14 ? 3 WED" 每年三月的星期三的下午2:10和2:44觸發(fā)
          "0 15 10 ? * MON-FRI" 周一至周五的上午10:15觸發(fā)
          "0 15 10 15 * ?" 每月15日上午10:15觸發(fā)
          "0 15 10 L * ?" 每月最后一日的上午10:15觸發(fā)
          "0 15 10 ? * 6L" 每月的最后一個星期五上午10:15觸發(fā) 
          "0 15 10 ? * 6L 2002-2005" 2002年至2005年的每月的最后一個星期五上午10:15觸發(fā)
          "0 15 10 ? * 6#3" 每月的第三個星期五上午10:15觸發(fā)

          三:quartz原理

              根據(jù)上面spring的配置,我們就比較清楚quartz的內(nèi)部情況,下面我們主要詳解配置涉及到的每個點
              1.我們先從最后一個步驟看起,SchedulerFactoryBean ,scheduler的工廠實現(xiàn),里面可以生產(chǎn)出對應(yīng)的多個jobDetail和trigger,每個jobDetail對應(yīng)trigger代表一個任務(wù)
                   Quartz的SchedulerFactory是標(biāo)準(zhǔn)的工廠類,不太適合在Spring環(huán)境下使用。此外,為了保證Scheduler能夠感知 Spring容器的生命周期,完成自動啟動和關(guān)閉的操作,必須讓Scheduler和Spring容器的生命周期相關(guān)聯(lián)。以便在Spring容器啟動后, Scheduler自動開始工作,而在Spring容器關(guān)閉前,自動關(guān)閉Scheduler。為此,Spring提供 SchedulerFactoryBean,這個FactoryBean大致?lián)碛幸韵碌墓δ埽?nbsp;
               1)以更具Bean風(fēng)格的方式為Scheduler提供配置信息; 
               2)讓Scheduler和Spring容器的生命周期建立關(guān)聯(lián),相生相息; 
               3)通過屬性配置部分或全部代替Quartz自身的配置文件。 
            2.jobDetail,表示一個可執(zhí)行的業(yè)務(wù)調(diào)用
            
            3.trigger:調(diào)度的時間計劃,什么時候,每隔多少時間可執(zhí)行等時間計劃

            4.ThreadPoolTaskExecutor,線程池,用來并行執(zhí)行每個對應(yīng)的job,提高效率,這也是上面提到不推薦使用jdk自身timer的一個很重要的原因
          posted @ 2012-07-03 14:42 陳睿 閱讀(3096) | 評論 (2)編輯 收藏

          2012年3月30日

          之前有接觸過hadoop,但都比較淺顯,對立面的東東不是很清楚!
          打算后面在hadoop上花時間把里面的內(nèi)容,好好學(xué)學(xué),這篇博客將在后面陸續(xù)更新hadoop學(xué)習(xí)筆記。
          posted @ 2012-03-30 10:14 陳睿 閱讀(367) | 評論 (0)編輯 收藏

          2012年3月12日

          一:基本原理
              主要是要實現(xiàn)網(wǎng)絡(luò)之間的通訊,網(wǎng)絡(luò)通信需要做的就是將流從一臺計算機傳輸?shù)搅硗庖慌_計算機,基于傳輸協(xié)議和網(wǎng)絡(luò)IO來實現(xiàn),其中傳輸協(xié)議比較出名的有http、 tcp、udp等等,http、tcp、udp都是在基于Socket概念上為某類應(yīng)用場景而擴展出的傳輸協(xié)議,網(wǎng)絡(luò)IO,主要有bio、nio、aio 三種方式,所有的分布式應(yīng)用通訊都基于這個原理而實現(xiàn)。

          二:實踐
          在分布式服務(wù)框架中,一個最基礎(chǔ)的問題就是遠程服務(wù)是怎么通訊的,在Java領(lǐng)域中有很多可實現(xiàn)遠程通訊的技術(shù):RMI、MINA、ESB、Burlap、Hessian、SOAP、EJB和JMS
          既然引入出了這么多技術(shù),那我們就順道深入挖掘下去,了解每個技術(shù)框架背后的東西:
          1.首先看RMI
               RMI主要包含如下內(nèi)容: 
               遠程服務(wù)的接口定義 
                ·遠程服務(wù)接口的具體實現(xiàn) 
                ·樁(Stub)和框架(Skeleton)文件 
                ·一個運行遠程服務(wù)的服務(wù)器 
                ·一個RMI命名服務(wù),它允許客戶端去發(fā)現(xiàn)這個遠程服務(wù) 
                ·文件的提供者(一個HTTP或者FTP服務(wù)器) 
                ·一個需要這個遠程服務(wù)的客戶端程序 
              
              來看下基于RMI的一次完整的遠程通信過程的原理:
              1)客戶端發(fā)起請求,請求轉(zhuǎn)交至RMI客戶端的stub類;
              2)stub類將請求的接口、方法、參數(shù)等信息進行序列化;
              3)基于tcp/ip將序列化后的流傳輸至服務(wù)器端;
              4)服務(wù)器端接收到流后轉(zhuǎn)發(fā)至相應(yīng)的skelton類;
              5)skelton類將請求的信息反序列化后調(diào)用實際的處理類;
              6)處理類處理完畢后將結(jié)果返回給skelton類;
              7)Skelton類將結(jié)果序列化,通過tcp/ip將流傳送給客戶端的stub;
              8)stub在接收到流后反序列化,將反序列化后的Java Object返回給調(diào)用者。

               RMI應(yīng)用級協(xié)議內(nèi)容:
          1、傳輸?shù)臉?biāo)準(zhǔn)格式是什么?
                是Java ObjectStream。
          2、怎么樣將請求轉(zhuǎn)化為傳輸?shù)牧鳎?/div>
                基于Java串行化機制將請求的java object信息轉(zhuǎn)化為流。
          3、怎么接收和處理流?
                根據(jù)采用的協(xié)議啟動相應(yīng)的監(jiān)聽端口,當(dāng)有流進入后基于Java串行化機制將流進行反序列化,并根據(jù)RMI協(xié)議獲取到相應(yīng)的處理對象信息,進行調(diào)用并處理,處理完畢后的結(jié)果同樣基于java串行化機制進行返回。
          4、傳輸協(xié)議是?
                tcp/ip。

          原理講了,開始實踐:
          創(chuàng)建RMI程序的6個步驟: 
          1、定義一個遠程接口的接口,該接口中的每一個方法必須聲明它將產(chǎn)生一個RemoteException異常。 
          2、定義一個實現(xiàn)該接口的類。 
          3、使用RMIC程序生成遠程實現(xiàn)所需的殘根和框架。 
          4、創(chuàng)建一個服務(wù)器,用于發(fā)布2中寫好的類。 
          5. 創(chuàng)建一個客戶程序進行RMI調(diào)用。 
          6、啟動rmiRegistry并運行自己的遠程服務(wù)器和客戶程序     
              
           
          1)首先創(chuàng)建遠程接口:
           /**
           * 遠程接口
           * 
           * @author mike
           *
           * 
          @since 2012-3-14
           
          */
          public interface Hello extends Remote {
              /**
               * 測試rmi 
               * 
               * 
          @return   hello
               * 
          @throws RemoteException
               
          */
              public String hello()throws RemoteException;
          }
                 
               2)創(chuàng)建接口實現(xiàn)
                  package com.gewara.rmi;

          import java.rmi.RemoteException;
          import java.rmi.server.UnicastRemoteObject;

          /**
           * 遠程接口實現(xiàn)
           * 
           * 
          @author mike
           *
           * 
          @since 2012-3-14
           
          */
          public class HelloImpl extends UnicastRemoteObject implements Hello {

              /**
               * seria id
               
          */
              private static final long serialVersionUID = -7931720891757437009L;

              protected HelloImpl() throws RemoteException {
                  super();
              }

              /**
               * hello實現(xiàn)
               * 
               * 
          @return hello world
               * 
          @throws RemoteException
               
          */
              public String hello() throws RemoteException {
                  return "hello world";
              }

          }

                 3)創(chuàng)建服務(wù)器端
                  package com.gewara.rmi;

          import java.rmi.Naming;
          import java.rmi.registry.LocateRegistry;

          public class Server {
              
              private static final String RMI_URL="rmi://192.168.2.89:10009/server";
              
              /**
               * RMI Server
               
          */
              public Server() {  
                  try {  
                      //創(chuàng)建遠程對象
                      Hello hello=new HelloImpl();
                      
                      //啟動注冊表
                      LocateRegistry.createRegistry(10009);
                      
                      //將名稱綁定到對象
                      Naming.bind(RMI_URL, hello);
                      
                  } catch (Exception e) {  
                      e.printStackTrace();  
                  }  
              }  
              
              /**
               * 
          @param args
               
          */
              public static void main(String[] args) {
                  new Server();

              }

          }

                  4)創(chuàng)建客服端
                  package com.gewara.rmi;

          import java.rmi.Naming;

          public class Client {

              private static final String RMI_URL="rmi://192.168.2.89:10009/server";
              /**
               * 
          @param args
               
          */
              public static void main(String[] args) {
                   try {  
                             String result=((Hello)Naming.lookup(RMI_URL)).hello();
                          System.out.println(result);  
                      } catch (Exception e) {  
                          e.printStackTrace();  
                      }  
              }

          }

              5)先啟動服務(wù)器端,然后再啟動客戶端
                 顯示結(jié)果:hello world

          由于涉及到的內(nèi)容比較多,打算每一篇里講一個遠程通訊框架,繼續(xù)詳解RMI
              
          三:詳解RMI內(nèi)部原理
          1. RMI基本結(jié)構(gòu):包含兩個獨立的程序,服務(wù)器和客戶端,服務(wù)器創(chuàng)建多個遠程對象,讓遠程對象能夠被引用,等待客戶端調(diào)用這些遠程對象的方法。客戶端從服務(wù)器獲取到一個或則多個遠程對象的引用,然后調(diào)用遠程對象方法,主要涉及到RMI接口、回調(diào)等技術(shù)。

          2.RMI回調(diào):服務(wù)器提供遠程對象引用供客戶端調(diào)用,客戶端主動調(diào)用服務(wù)器,如果服務(wù)器主動打算調(diào)用客戶端,這就叫回調(diào)。

          3.命名遠程對象:客戶端通過一個命名或則一個查找服務(wù)找到遠程服務(wù),遠程服務(wù)包含Java的命名和查找接口(Java Naming and Directory Interface)JNDI
          RMI提供了一種服務(wù):RMI注冊rmiregistry,默認(rèn)端口:1099,主機提供遠程服務(wù),接受服務(wù),啟動注冊服務(wù)的命令:start rmiregistry
          客戶端使用一個靜態(tài)類Naming到達RMI注冊處,通過方法lookup()方法,客戶來詢問注冊。
          posted @ 2012-03-12 09:57 陳睿 閱讀(2301) | 評論 (2)編輯 收藏

          2012年2月29日

          一:spring概要
              簡單來說,Spring是一個輕量級的控制反轉(zhuǎn)(IoC)和面向切面(AOP)的容器框架。
              
          控制反轉(zhuǎn)——Spring通過一種稱作控制反轉(zhuǎn)(IoC)的技術(shù)促進了松耦合。當(dāng)應(yīng)用了IoC,一個對象依賴的其它對象會通過被動的方式傳遞進來,而不是這個對象自己創(chuàng)建或者查找依賴對象。你可以認(rèn)為IoC與JNDI相反——不是對象從容器中查找依賴,而是容器在對象初始化時不等對象請求就主動將依賴傳遞給它。
            ◆面向切面——Spring提供了面向切面編程的豐富支持,允許通過分離應(yīng)用的業(yè)務(wù)邏輯與系統(tǒng)級服務(wù)(例如審計(auditing)和事務(wù)(transaction)管理)進行內(nèi)聚性的開發(fā)。應(yīng)用對象只實現(xiàn)它們應(yīng)該做的——完成業(yè)務(wù)邏輯——僅此而已。它們并不負(fù)責(zé)(甚至是意識)其它的系統(tǒng)級關(guān)注點,例如日志或事務(wù)支持。
            ◆容器——Spring包含并管理應(yīng)用對象的配置和生命周期,在這個意義上它是一種容器,你可以配置你的每個bean如何被創(chuàng)建——基于一個可配置原型(prototype),你的bean可以創(chuàng)建一個單獨的實例或者每次需要時都生成一個新的實例——以及它們是如何相互關(guān)聯(lián)的。然而,Spring不應(yīng)該被混同于傳統(tǒng)的重量級的EJB容器,它們經(jīng)常是龐大與笨重的,難以使用。
            ◆框架——Spring可以將簡單的組件配置、組合成為復(fù)雜的應(yīng)用。在Spring中,應(yīng)用對象被聲明式地組合,典型地是在一個XML文件里。Spring也提供了很多基礎(chǔ)功能(事務(wù)管理、持久化框架集成等等),將應(yīng)用邏輯的開發(fā)留給了你。
            所有Spring的這些特征使你能夠編寫更干凈、更可管理、并且更易于測試的代碼。它們也為Spring中的各種模塊提供了基礎(chǔ)支持。

          二:spring的整個生命周期
              首先說一下spring的整個初始化過程,web應(yīng)用中創(chuàng)建spring容器有兩種方式:
              第一種:在web.xml里直接配置spring容器,servletcontextlistener
              第二種:通過load-on-startup servlet實現(xiàn)。
              主要就說一下第一種方式:
              spring提供了ServletContextListener的實現(xiàn)類ContextLoaderListener,該類作為listener使用,在創(chuàng)建時自動查找WEB-INF目錄下的applicationContext.xml,該文件是默認(rèn)查找的,如果只有一個就不需要配置初始化xml參數(shù),如果需要配置,設(shè)置contextConfigLocation為application的xml文件即可。可以好好閱讀一下ContextLoaderListener的源代碼,就可以很清楚的知道spring的整個加載過程。
              spring容器的初始化代碼如下:
              /**
               * Initialize the root web application context.
               */
              
          public void contextInitialized(ServletContextEvent event) {
                  
          this.contextLoader = createContextLoader();
                  
          if (this.contextLoader == null) {
                      
          this.contextLoader = this;
                  }
                  
          this.contextLoader.initWebApplicationContext(event.getServletContext());//contextLoader初始化web應(yīng)用容器

              }
              繼續(xù)分析initWebApplicationContext做了什么事情:
              

              
          /**
               * Initialize Spring's web application context for the given servlet context,
               * according to the "{
          @link #CONTEXT_CLASS_PARAM contextClass}" and
               * "{
          @link #CONFIG_LOCATION_PARAM contextConfigLocation}" context-params.
               * 
          @param servletContext current servlet context
               * 
          @return the new WebApplicationContext
               * 
          @see #CONTEXT_CLASS_PARAM
               * 
          @see #CONFIG_LOCATION_PARAM
               
          */
              
          public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
                
          //首先創(chuàng)建一個spring的父容器,類似根節(jié)點root容器,而且只能是一個,如果已經(jīng)創(chuàng)建,拋出對應(yīng)的異常
                  
          if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
                      
          throw new IllegalStateException(
                              
          "Cannot initialize context because there is already a root application context present - " +
                              
          "check whether you have multiple ContextLoader* definitions in your web.xml!");
                  }

                  Log logger 
          = LogFactory.getLog(ContextLoader.class);
                  servletContext.log(
          "Initializing Spring root WebApplicationContext");
                  
          if (logger.isInfoEnabled()) {
                      logger.info(
          "Root WebApplicationContext: initialization started");
                  }
                  
          long startTime = System.currentTimeMillis();

                  
          try {
                      
          // Determine parent for root web application context, if any.
                      ApplicationContext parent = loadParentContext(servletContext);//創(chuàng)建通過web.xml配置的父容器            
          具體里面的代碼是怎么實現(xiàn)的,就不在這里進行詳解了
          // Store context in local instance variable, to guarantee that
                      
          // it is available on ServletContext shutdown.
                      this.context = createWebApplicationContext(servletContext, parent);//主要的創(chuàng)建過程都在改方法內(nèi),可以自己去看源代碼
                      servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, 
          this.context);
                      
          //把spring初始好的容器加載到servletcontext內(nèi),相當(dāng)于servletcontext包含webapplicationcontext
                      ClassLoader ccl 
          = Thread.currentThread().getContextClassLoader();
                      
          if (ccl == ContextLoader.class.getClassLoader()) {
                          currentContext 
          = this.context;
                      }
                      
          else if (ccl != null) {
                          currentContextPerThread.put(ccl, 
          this.context);
                      }

                      
          if (logger.isDebugEnabled()) {
                          logger.debug(
          "Published root WebApplicationContext as ServletContext attribute with name [" +
                                  WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE 
          + "]");
                      }
                      
          if (logger.isInfoEnabled()) {
                          
          long elapsedTime = System.currentTimeMillis() - startTime;
                          logger.info(
          "Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");
                      }

                      
          return this.context;
                  }
                  
          catch (RuntimeException ex) {
                      logger.error(
          "Context initialization failed", ex);
                      servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
                      
          throw ex;
                  }
                  
          catch (Error err) {
                      logger.error(
          "Context initialization failed", err);
                      servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);
                      
          throw err;
                  }
              }
              
              看到這里基本已經(jīng)清楚了整個spring容器的加載過程,如果還想了解更加深入,請查看我紅色標(biāo)注的方法體。

              其次再說一下spring的IOC和AOP使用的場景,由于原理大家都很清楚了,那就說一下它們使用到的地方:
             

              IOC使用的場景:
                  管理bean的依賴關(guān)系,目前主流的電子商務(wù)網(wǎng)站基本都采用spring管理業(yè)務(wù)層代碼的依賴關(guān)系,包括:淘寶,支付寶,阿里巴巴,百度等公司。
                  
          posted @ 2012-02-29 14:45 陳睿 閱讀(1626) | 評論 (0)編輯 收藏

          2012年2月27日

          一:struts2概要
              以WebWork優(yōu)秀設(shè)計思想為核心,吸收了struts1的部分優(yōu)點。

          二:struts2詳解
              主要就是詳解struts2與struts1之間的區(qū)別,以及為什么要采用webwork重新設(shè)計新框架,以及吸收了struts1的哪部分優(yōu)點。
              首先將區(qū)別:
          •     最大的區(qū)別是與servlet成功解耦,不在依賴容器來初始化HttpServletRequest和HttpServletResponse
              struts1里依賴的核心控制器為ActionServlet而struts2依賴ServletDispatcher,一個是servlet一個是filter,正是采用了filter才不至于和servlet耦合,所有的數(shù)據(jù) 都是通過攔截器來實現(xiàn),如下圖顯示:
              

          •     web層表現(xiàn)層的豐富,struts2已經(jīng)可以使用jsp、velocity、freemarker
          •     線程模式方面:struts1的action是單例模式而且必須是線程安全或同步的,是struts2的action對每一個請求都產(chǎn)生一個新的實例,因此沒有線程安全問       題。
          •     封裝請求參數(shù):是struts1采用ActionForm封裝請求參數(shù),都必須繼承ActionForm基類,而struts2通過bean的屬性封裝,大大降低了耦合。
          •     類型轉(zhuǎn)換:struts1封裝的ActionForm都是String類型,采用Commons- Beanutils進行類型轉(zhuǎn)換,每個類一個轉(zhuǎn)換器;struts2采用OGNL進行類型轉(zhuǎn)       換,支持基本數(shù)據(jù)類型和封裝類型的自動轉(zhuǎn)換。
          •     數(shù)據(jù)校驗:struts1在ActionForm中重寫validate方法;struts2直接重寫validate方法,直接在action里面重寫即可,不需要繼承任何基類,實際的調(diào)用順序是,validate()-->execute(),會在執(zhí)行execute之前調(diào)用validate,也支持xwork校驗框架來校驗。
              其次,講一下為什么要采用webwork來重新設(shè)計struts2
                    
          首先的從核心控制器談起,struts2的FilterDispatcher,這里我們知道是一個filter而不是一個servlet,講到這里很多人還不是很清楚web.xml里它們之間的聯(lián)系,先簡短講一下它們的加載順序,context-param(應(yīng)用范圍的初始化參數(shù))-->listener(監(jiān)聽?wèi)?yīng)用端的任何修改通知)-->filter(過濾)-->servlet。
              filter在執(zhí)行servlet之間就以及調(diào)用了,所以才有可能解脫完全依賴servlet的局面,那我們來看看這個filter做了什么事情:
              /**
               * Process an action or handle a request a static resource.
               * <p/>
               * The filter tries to match the request to an action mapping.
               * If mapping is found, the action processes is delegated to the dispatcher's serviceAction method.
               * If action processing fails, doFilter will try to create an error page via the dispatcher.
               * <p/>
               * Otherwise, if the request is for a static resource,
               * the resource is copied directly to the response, with the appropriate caching headers set.
               * <p/>
               * If the request does not match an action mapping, or a static resource page,
               * then it passes through.
               *
               * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)
               
          */
              
          public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {

                  HttpServletRequest request 
          = (HttpServletRequest) req;
                  HttpServletResponse response 
          = (HttpServletResponse) res;
                  ServletContext servletContext 
          = getServletContext();

                  String timerKey 
          = "FilterDispatcher_doFilter: ";
                  
          try {

                      
          // FIXME: this should be refactored better to not duplicate work with the action invocation
                      ValueStack stack = dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack();
                      ActionContext ctx 
          = new ActionContext(stack.getContext());
                      ActionContext.setContext(ctx);

                      UtilTimerStack.push(timerKey);
                      request 
          = prepareDispatcherAndWrapRequest(request, response);
                      ActionMapping mapping;
                      
          try {
                          mapping 
          = actionMapper.getMapping(request, dispatcher.getConfigurationManager());
                      } 
          catch (Exception ex) {
                          log.error(
          "error getting ActionMapping", ex);
                          dispatcher.sendError(request, response, servletContext, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex);
                          
          return;
                      }

                      
          if (mapping == null) {
                          
          // there is no action in this request, should we look for a static resource?
                          String resourcePath = RequestUtils.getServletPath(request);

                          
          if ("".equals(resourcePath) && null != request.getPathInfo()) {
                              resourcePath 
          = request.getPathInfo();
                          }

                          
          if (staticResourceLoader.canHandle(resourcePath)) {
                              staticResourceLoader.findStaticResource(resourcePath, request, response);
                          } 
          else {
                              
          // this is a normal request, let it pass through
                              chain.doFilter(request, response);
                          }
                          
          // The framework did its job here
                          return;
                      }

                      dispatcher.serviceAction(request, response, servletContext, mapping);
          //過濾用戶請求,攔截器執(zhí)行,把對應(yīng)的action請求轉(zhuǎn)到業(yè)務(wù)action執(zhí)行        } 

          finally {
                      
          try {
                          ActionContextCleanUp.cleanUp(req);
                      } 
          finally {
                          UtilTimerStack.pop(timerKey);
                      }
                  }
              }
              對應(yīng)的action參數(shù)由攔截器獲取。
              解耦servlet是struts2采用webwork思路的最重要的一個原因,也迎合了整個技術(shù)的一個發(fā)展方向,解耦一直貫穿于整個框架。
                  
          posted @ 2012-02-27 17:07 陳睿 閱讀(1667) | 評論 (2)編輯 收藏

          JVM specification對JVM內(nèi)存的描述

          首先我們來了解JVM specification中的JVM整體架構(gòu)。如下圖:

           


              
          主要包括兩個子系統(tǒng)和兩個組件: Class loader(類裝載器) 子系統(tǒng),Execution engine(執(zhí)行引擎) 子系統(tǒng);Runtime data area (運行時數(shù)據(jù)區(qū)域)組件, Native interface(本地接口)組件。
               Class loader
          子系統(tǒng)的作用 :根據(jù)給定的全限定名類名( java.lang.Object)來裝載class文件的內(nèi)容到 Runtime data area中的method area(方法區(qū)域)Javsa程序員可以extends java.lang.ClassLoader類來寫自己的Class loader
                Execution engine
          子系統(tǒng)的作用 :執(zhí)行classes中的指令。任何JVM specification實現(xiàn)(JDK)的核心是Execution engine 換句話說:Sun JDK IBMJDK好壞主要取決于他們各自實現(xiàn)的Execution  engine的好壞。每個運行中的線程都有一個Execution engine的實例。
               Native interface
          組件 :與native libraries交互,是其它編程語言交互的接口。 
               Runtime data area
          組件:這個組件就是JVM中的內(nèi)存

          • 運行時數(shù)據(jù)組件的詳解介紹:
              

          Runtime data area 主要包括五個部分:Heap (堆), Method Area(方法區(qū)域), Java Stack(java的棧), Program Counter(程序計數(shù)器), Native method stack(本地方法棧)。Heap 和Method Area是被所有線程的共享使用的;而Java stack, Program counter 和Native method stack是以線程為粒度的,每個線程獨自擁有。

          Heap
          Java程序在運行時創(chuàng)建的所有類實或數(shù)組都放在同一個堆中。
          而一個Java虛擬實例中只存在一個堆空間,因此所有線程都將共享這個堆。每一個java程序獨占一個JVM實例,因而每個java程序都有它自己的堆空間,它們不會彼此干擾。但是同一java程序的多個線程都共享著同一個堆空間,就得考慮多線程訪問對象(堆數(shù)據(jù))的同步問題。 (這里可能出現(xiàn)的異常java.lang.OutOfMemoryError: Java heap space)

          JVM堆一般又可以分為以下三部分:

          Ø Perm

          Perm代主要保存class,method,filed對象,這部門的空間一般不會溢出,除非一次性加載了很多的類,不過在涉及到熱部署的應(yīng)用服務(wù)器的時候,有時候會遇到java.lang.OutOfMemoryError : PermGen space 的錯誤,造成這個錯誤的很大原因就有可能是每次都重新部署,但是重新部署后,類的class沒有被卸載掉,這樣就造成了大量的class對象保存在了perm中,這種情況下,一般重新啟動應(yīng)用服務(wù)器可以解決問題。

          Ø Tenured

          Tenured區(qū)主要保存生命周期長的對象,一般是一些老的對象,當(dāng)一些對象在Young復(fù)制轉(zhuǎn)移一定的次數(shù)以后,對象就會被轉(zhuǎn)移到Tenured區(qū),一般如果系統(tǒng)中用了application級別的緩存,緩存中的對象往往會被轉(zhuǎn)移到這一區(qū)間。

          Ø Young

          Young區(qū)被劃分為三部分,Eden區(qū)和兩個大小嚴(yán)格相同的Survivor區(qū),其中Survivor區(qū)間中,某一時刻只有其中一個是被使用的,另外一個留做垃圾收集時復(fù)制對象用,在Young區(qū)間變滿的時候,minor GC就會將存活的對象移到空閑的Survivor區(qū)間中,根據(jù)JVM的策略,在經(jīng)過幾次垃圾收集后,任然存活于Survivor的對象將被移動到Tenured區(qū)間。


          Method area
          在Java虛擬機中,被裝載的class的信息存儲在Method area的內(nèi)存中。
          當(dāng)虛擬機裝載某個類型時,它使用類裝載器定位相應(yīng)的class文件,然后讀入這個class文件內(nèi)容并把它傳輸?shù)教摂M機中。緊接著虛擬機提取其中的類型信息,并將這些信息存儲到方法區(qū)。該類型中的類(靜態(tài))變量同樣也存儲在方法區(qū)中。與Heap 一樣,method area是多線程共享的,因此要考慮多線程訪問的同步問題。比如,假設(shè)同時兩個線程都企圖訪問一個名為Lava的類,而這個類還沒有內(nèi)裝載入虛擬機,那么,這時應(yīng)該只有一個線程去裝載它,而另一個線程則只能等待。 (這里可能出現(xiàn)的異常java.lang.OutOfMemoryError: PermGen full)

          Java stack
                 Java stack以幀為單位保存線程的運行狀態(tài)。虛擬機只會直接對Java stack執(zhí)行兩種操作:以幀為單位的壓棧或出棧。
          每當(dāng)線程調(diào)用一個方法的時候,就對當(dāng)前狀態(tài)作為一個幀保存到java stack中(壓棧);當(dāng)一個方法調(diào)用返回時,從java stack彈出一個幀(出棧)。棧的大小是有一定的限制,這個可能出現(xiàn)StackOverFlow問題。 下面的程序可以說明這個問題。

           

          public class TestStackOverFlow {
           
               public static void main(String[] args) {
           
                      Recursive r = new Recursive();
                      r.doit(10000);
                      // Exception in thread "main" java.lang.StackOverflowError
               }
           
          }
           
          class Recursive {
           
               public int doit(int t) {
                      if (t <= 1) {
                              return 1;
                      }
                      return t + doit(t - 1);
               }
           
          }

           

          Program counter
          每個運行中的Java程序,每一個線程都有它自己的PC寄存器,也是該線程啟動時創(chuàng)建的。PC寄存器的內(nèi)容總是指向下一條將被執(zhí)行指令的餓&ldquo;地址&rdquo;,這里的&ldquo;地址&rdquo;可以是一個本地指針,也可以是在方法區(qū)中相對應(yīng)于該方法起始指令的偏移量。

          Native method stack
          對于一個運行中的Java程序而言,它還能會用到一些跟本地方法相關(guān)的數(shù)據(jù)區(qū)。當(dāng)某個線程調(diào)用一個本地方法時,它就進入了一個全新的并且不再受虛擬機限制的世界。本地方法可以通過本地方法接口來訪問虛擬機的運行時數(shù)據(jù)區(qū),不止與此,它還可以做任何它想做的事情。比如,可以調(diào)用寄存器,或在操作系統(tǒng)中分配內(nèi)存等。總之,本地方法具有和JVM相同的能力和權(quán)限。 (這里出現(xiàn)JVM無法控制的內(nèi)存溢出問題native heap OutOfMemory )

          JVM提供了相應(yīng)的參數(shù)來對內(nèi)存大小進行配置。



          正如上面描述,JVM中堆被分為了3個大的區(qū)間,同時JVM也提供了一些選項對Young,Tenured的大小進行控制。

          Ø Total Heap 

          -Xms :指定了JVM初始啟動以后初始化內(nèi)存

          -Xmx:指定JVM堆得最大內(nèi)存,在JVM啟動以后,會分配-Xmx參數(shù)指定大小的內(nèi)存給JVM,但是不一定全部使用,JVM會根據(jù)-Xms參數(shù)來調(diào)節(jié)真正用于JVM的內(nèi)存

          -Xmx -Xms之差就是三個Virtual空間的大小

          Ø Young Generation

          -XX:NewRatio=8意味著tenured  young的比值81,這樣eden+2*survivor=1/9

          堆內(nèi)存

          -XX:SurvivorRatio=32意味著eden和一個survivor的比值是321,這樣一個Survivor就占Young區(qū)的1/34.

          -Xmn 參數(shù)設(shè)置了年輕代的大小

          Ø Perm Generation

          -XX:PermSize=16M -XX:MaxPermSize=64M

          Thread Stack

          -XX:Xss=128K

           


          posted @ 2012-02-27 15:35 陳睿 閱讀(532) | 評論 (1)編輯 收藏

          2012年2月24日

          數(shù)據(jù)庫端性能非常低
          •     優(yōu)化數(shù)據(jù)庫服務(wù)器端的配置參數(shù)
          •     應(yīng)用服務(wù)器端數(shù)據(jù)連接池的配置參數(shù)修改
          •     應(yīng)用服務(wù)器端的sql審核,建立更好的索引以及修改不好的sql語句:關(guān)聯(lián)表過多,查詢的數(shù)據(jù)量過大,表設(shè)計不合理等
          •     應(yīng)用服務(wù)器端拆解過大的表,分為多張表,甚至把一個數(shù)據(jù)庫分為多個數(shù)據(jù)庫
          •     數(shù)據(jù)庫服務(wù)器端拆解為讀/寫分離,Master/Slave方式,一臺寫主機對應(yīng)兩臺或則多臺讀的備用機器
          應(yīng)用服務(wù)器端
          •     訪問壓力過大,1臺機器不能承受,該為多臺機器,應(yīng)用服務(wù)器配置為集群模式
              
              
          posted @ 2012-02-24 16:17 陳睿 閱讀(233) | 評論 (0)編輯 收藏

          2012年2月21日

          1.    多線程概念:
                 線程是指進程中的一個執(zhí)行流程,一個進程中可以運行多個線程。比如java.exe進程中可以運行很多線程。線程總是屬于某個進程,進程中的多個線程共享進程的內(nèi)存。
          •     多線程的實現(xiàn)方式和啟動
          •     多線程是依靠什么方式解決資源競爭
          •     多線程的各種狀態(tài)以及優(yōu)先級
          •     多線程的暫停方式
           2.    多線程詳解
                  1)多線程的實現(xiàn)方式和啟動:
          •       繼承Thread和是實現(xiàn)Runnable接口,重寫run方法
          •       啟動只有一種方式:通過start方法,虛擬機會調(diào)用run方法

                 2) 多線程依靠什么解決資源競爭
          •        鎖機制:分為對象鎖和類鎖,在多個線程調(diào)用的情況,每個對象鎖都是唯一的,只有獲取了鎖才能調(diào)用synchronized方法
          •        synchronize同步:分為同步方法和同步方法塊
          •        什么時候獲取鎖:每次調(diào)用到synchronize方法,這個時候去獲取鎖資源,如果線程獲取到鎖則別的線程只有等到同步方法介紹后,釋放鎖后,別的線程        才能繼續(xù)使用
                  3)線程的幾種狀態(tài)
          •        主要分為:新狀態(tài)(還沒有調(diào)用start方法),可執(zhí)行狀態(tài)(調(diào)用start方法),阻塞狀態(tài),死亡狀態(tài)
                  默認(rèn)優(yōu)先級為normal(5),優(yōu)先級數(shù)值在1-10之間
           4) 多線程的暫停方式

          •     sleep:睡眠單位為毫秒
          •     wait,waitAll,notify,notifyAll,wait等待,只有通過wait或者waitAll喚醒
          •     yield:cpu暫時停用
          •     join
          posted @ 2012-02-21 15:32 陳睿 閱讀(1433) | 評論 (0)編輯 收藏
          1. HashSet概要
          •     采用HashMap存儲,key直接存取值,value存儲一個object
          •     存儲的key值是唯一的
          •      HashSet中元素的順序是隨機的,包括添加(add())和輸出都是無序的

            代碼就不具體詳解了,主要就是通過封裝HashMap組成。

          posted @ 2012-02-21 11:37 陳睿 閱讀(1109) | 評論 (0)編輯 收藏
          僅列出標(biāo)題  下一頁

          導(dǎo)航

          <2025年7月>
          293012345
          6789101112
          13141516171819
          20212223242526
          272829303112
          3456789

          統(tǒng)計

          常用鏈接

          留言簿

          隨筆分類

          隨筆檔案

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 视频| 保德县| 静乐县| 沐川县| 梁山县| 什邡市| 上杭县| 金堂县| 哈密市| 缙云县| 乌兰县| 开原市| 获嘉县| 三亚市| 拜城县| 石林| 丽水市| 乌兰察布市| 丰都县| 陕西省| 永靖县| 水城县| 布拖县| 衡南县| 太湖县| 北京市| 焉耆| 宁夏| 壶关县| 泾阳县| 丰城市| 玉龙| 广饶县| 灵石县| 嘉鱼县| 泸西县| 商都县| 太仓市| 汕头市| 郁南县| 雷波县|