<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    2013年6月20日


    版權信息: 可以任意轉載, 轉載時請務必以超鏈接形式標明文章原文出處, 即下面的聲明.

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

    jvm classLoader architecture:

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

    類加載器的特性:

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

    classloader-architecture

    classloader-class-diagram

    classloader-class-diagram

    類圖中, BootstrapClassLoader是一個單獨的java類, 其實在這里, 不應該叫他是一個java類。因為,它已經完全不用java實現了。它是在jvm啟動時, 就被構造起來的, 負責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.          // 啟動類加載器或父類加載器拋出異常后, 當前類加載器將其  
    15.          // 捕獲, 并通過findClass方法, 由自身加載  
    16.          c = findClass(name);  
    17.      }  
    18. }  

    線程上下文類加載器
    java默認的線程上下文類加載器是 系統類加載器(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的無參構造函數Launch()。

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

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

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

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

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

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

    java動態載入class的兩種方式:

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

    用Class.forName加載類

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

    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 就是得到調用當前forName方法的類的類加載器

    static塊在什么時候執行?

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

    各個java類由哪些classLoader加載?

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

    NoClassDefFoundError和ClassNotFoundException

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

    2012年7月3日

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

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

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


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

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

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

                            public class  TestJobTask{
         

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

    }
           
        2.增加一個線程池
        <!-- 線程執行器配置,用于任務注冊 -->
    <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.定義業務邏輯類

        <!-- 業務對象 -->
    <bean id="testJobTask" class="com.mike.scheduling.TestJobTask" />


        4.增加quartz調用業務邏輯

        <!-- 調度業務 -->
    <bean id="jobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
     <property name="targetObject" ref="testJobTask" />
     <property name="targetMethod" value="service" />
    </bean>

        5.增加調用的觸發器,觸發的時間,有兩種方式:

         第一種觸發時間,采用類似linux的cron,配置時間的表示發出豐富  
      <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分鐘執行一次 
      
        第二種,采用比較簡話的方式,申明延遲時間和間隔時間
      <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分鐘執行一次 

        6.開始調用

          <!-- 設置調度 -->
    <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
     <property name="triggers">
      <list>
       <ref bean="cronTrigger" />
      </list>
     </property>
     <property name="taskExecutor" ref="executor" />
    </bean>

       7.結束:啟動容器即可,已經將spring和quartz結合完畢。

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

    三:quartz原理

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

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

    2012年3月30日

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

    2012年3月12日

    一:基本原理
        主要是要實現網絡之間的通訊,網絡通信需要做的就是將流從一臺計算機傳輸到另外一臺計算機,基于傳輸協議和網絡IO來實現,其中傳輸協議比較出名的有http、 tcp、udp等等,http、tcp、udp都是在基于Socket概念上為某類應用場景而擴展出的傳輸協議,網絡IO,主要有bio、nio、aio 三種方式,所有的分布式應用通訊都基于這個原理而實現。

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

         RMI應用級協議內容:
    1、傳輸的標準格式是什么?
          是Java ObjectStream。
    2、怎么樣將請求轉化為傳輸的流?
          基于Java串行化機制將請求的java object信息轉化為流。
    3、怎么接收和處理流?
          根據采用的協議啟動相應的監聽端口,當有流進入后基于Java串行化機制將流進行反序列化,并根據RMI協議獲取到相應的處理對象信息,進行調用并處理,處理完畢后的結果同樣基于java串行化機制進行返回。
    4、傳輸協議是?
          tcp/ip。

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

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

    /**
     * 遠程接口實現
     * 
     * 
    @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實現
         * 
         * 
    @return hello world
         * 
    @throws RemoteException
         
    */
        public String hello() throws RemoteException {
            return "hello world";
        }

    }

           3)創建服務器端
            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 {  
                //創建遠程對象
                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)創建客服端
            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)先啟動服務器端,然后再啟動客戶端
           顯示結果:hello world

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

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

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

    2012年2月29日

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

    二:spring的整個生命周期
        首先說一下spring的整個初始化過程,web應用中創建spring容器有兩種方式:
        第一種:在web.xml里直接配置spring容器,servletcontextlistener
        第二種:通過load-on-startup servlet實現。
        主要就說一下第一種方式:
        spring提供了ServletContextListener的實現類ContextLoaderListener,該類作為listener使用,在創建時自動查找WEB-INF目錄下的applicationContext.xml,該文件是默認查找的,如果只有一個就不需要配置初始化xml參數,如果需要配置,設置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應用容器

        }
        繼續分析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) {
          
    //首先創建一個spring的父容器,類似根節點root容器,而且只能是一個,如果已經創建,拋出對應的異常
            
    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);//創建通過web.xml配置的父容器            
    具體里面的代碼是怎么實現的,就不在這里進行詳解了
    // Store context in local instance variable, to guarantee that
                
    // it is available on ServletContext shutdown.
                this.context = createWebApplicationContext(servletContext, parent);//主要的創建過程都在改方法內,可以自己去看源代碼
                servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, 
    this.context);
                
    //把spring初始好的容器加載到servletcontext內,相當于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;
            }
        }
        
        看到這里基本已經清楚了整個spring容器的加載過程,如果還想了解更加深入,請查看我紅色標注的方法體。

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

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

    2012年2月27日

    一:struts2概要
        以WebWork優秀設計思想為核心,吸收了struts1的部分優點。

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

    •     web層表現層的豐富,struts2已經可以使用jsp、velocity、freemarker
    •     線程模式方面:struts1的action是單例模式而且必須是線程安全或同步的,是struts2的action對每一個請求都產生一個新的實例,因此沒有線程安全問       題。
    •     封裝請求參數:是struts1采用ActionForm封裝請求參數,都必須繼承ActionForm基類,而struts2通過bean的屬性封裝,大大降低了耦合。
    •     類型轉換:struts1封裝的ActionForm都是String類型,采用Commons- Beanutils進行類型轉換,每個類一個轉換器;struts2采用OGNL進行類型轉       換,支持基本數據類型和封裝類型的自動轉換。
    •     數據校驗:struts1在ActionForm中重寫validate方法;struts2直接重寫validate方法,直接在action里面重寫即可,不需要繼承任何基類,實際的調用順序是,validate()-->execute(),會在執行execute之前調用validate,也支持xwork校驗框架來校驗。
        其次,講一下為什么要采用webwork來重新設計struts2
              
    首先的從核心控制器談起,struts2的FilterDispatcher,這里我們知道是一個filter而不是一個servlet,講到這里很多人還不是很清楚web.xml里它們之間的聯系,先簡短講一下它們的加載順序,context-param(應用范圍的初始化參數)-->listener(監聽應用端的任何修改通知)-->filter(過濾)-->servlet。
        filter在執行servlet之間就以及調用了,所以才有可能解脫完全依賴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);
    //過濾用戶請求,攔截器執行,把對應的action請求轉到業務action執行        } 

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

    JVM specification對JVM內存的描述

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

     


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

    • 運行時數據組件的詳解介紹:
        

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

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

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

    Ø Perm

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

    Ø Tenured

    Tenured區主要保存生命周期長的對象,一般是一些老的對象,當一些對象在Young復制轉移一定的次數以后,對象就會被轉移到Tenured區,一般如果系統中用了application級別的緩存,緩存中的對象往往會被轉移到這一區間。

    Ø Young

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


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

    Java stack
           Java stack以幀為單位保存線程的運行狀態。虛擬機只會直接對Java stack執行兩種操作:以幀為單位的壓棧或出棧。
    每當線程調用一個方法的時候,就對當前狀態作為一個幀保存到java stack中(壓棧);當一個方法調用返回時,從java stack彈出一個幀(出棧)。棧的大小是有一定的限制,這個可能出現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寄存器,也是該線程啟動時創建的。PC寄存器的內容總是指向下一條將被執行指令的餓&ldquo;地址&rdquo;,這里的&ldquo;地址&rdquo;可以是一個本地指針,也可以是在方法區中相對應于該方法起始指令的偏移量。

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

    JVM提供了相應的參數來對內存大小進行配置。



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

    Ø Total Heap 

    -Xms :指定了JVM初始啟動以后初始化內存

    -Xmx:指定JVM堆得最大內存,在JVM啟動以后,會分配-Xmx參數指定大小的內存給JVM,但是不一定全部使用,JVM會根據-Xms參數來調節真正用于JVM的內存

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

    Ø Young Generation

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

    堆內存

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

    -Xmn 參數設置了年輕代的大小

    Ø Perm Generation

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

    Thread Stack

    -XX:Xss=128K

     


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

    2012年2月24日

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

    2012年2月21日

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

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

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

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

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

    導航

    <2025年5月>
    27282930123
    45678910
    11121314151617
    18192021222324
    25262728293031
    1234567

    統計

    常用鏈接

    留言簿

    隨筆分類

    隨筆檔案

    搜索

    最新評論

    閱讀排行榜

    評論排行榜

    主站蜘蛛池模板: xxxx日本免费| 国产一卡2卡3卡4卡2021免费观看 国产一卡2卡3卡4卡无卡免费视频 | 亚洲性猛交xx乱| 一区二区三区四区免费视频| 亚洲乱亚洲乱少妇无码| 无套内射无矿码免费看黄| 亚洲国产成人久久精品99| 又硬又粗又长又爽免费看| 国产亚洲精aa成人网站| 91精品成人免费国产| 久久久久亚洲AV片无码| 18禁止看的免费污网站| 亚洲人成电影网站久久| 日产乱码一卡二卡三免费| 一级毛片视频免费| 亚洲成av人在线视| 妻子5免费完整高清电视| 99热亚洲色精品国产88| 国产jizzjizz视频免费看| 一级做受视频免费是看美女| 国产精品亚洲精品日韩已满| 最近中文字幕免费完整| 亚洲1区2区3区精华液| 中文字幕在线亚洲精品| www.免费在线观看| 国产精品亚洲天堂| 午夜亚洲AV日韩AV无码大全| 国产在线观看麻豆91精品免费| 国产成人免费网站在线观看| 性生大片视频免费观看一级| 亚洲成人在线网站| 免费无码又黄又爽又刺激| 羞羞视频免费观看| 亚洲综合婷婷久久| 国产成人免费a在线视频app| 国产无遮挡裸体免费视频在线观看| 免费国产成人高清在线观看麻豆| 亚洲狠狠狠一区二区三区| 免费一级毛片在线观看| 91久久青青草原线免费| 黄网站在线播放视频免费观看|