interface21 - web - Log4jConfigListener(Log4j加載流程)


前言 

最近打算花點時間好好看看spring的源碼,然而現在Spring的源碼經過迭代的版本太多了,比較龐大,看起來比較累,所以准備從最初的版本(interface21)開始入手,僅用於學習,理解其設計思想,后續慢慢研究其每次版本變更的內容。。。

先從interface21的一個典型web工程例子看起,寵物診所 - petclinic,因為該工程基本涵蓋了Spring的APO、IOC、JDBC、Web MVC、事務、國際化、主題切換、參數校驗等主要功能。。。

先從簡單的走起,看下該web工程中, Log4j是如何加載的吧~~~~~~~

對應的web.xml配置

    <context-param>
        <param-name>webAppRootKey</param-name>
        <param-value>petclinic.root</param-value>
    </context-param>

    <context-param>
        <param-name>log4jConfigLocation</param-name>
        <param-value>/WEB-INF/classes/log4j.properties</param-value>
    </context-param>

    <listener>
        <listener-class>com.interface21.web.util.Log4jConfigListener</listener-class>
    </listener>

執行時序圖(看不清的話可以點擊查看原圖)

時序圖中的各個步驟簡要分析

執行的入口在Log4jConfigListener類的contextInitialized方法,由於Log4jConfigListener類實現了ServletContextListener接口,所以在Servlet容器(tomcat)啟動時,會自動調用contextInitialized方法。

步驟描述:

  1. 進入Log4jConfigListener類的contextInitialized方法,該類只有一句代碼,執行Log4jWebConfigurer.initLogging方法;
        public void contextInitialized(ServletContextEvent event) {
            Log4jWebConfigurer.initLogging(event.getServletContext());
        }
  2. 進入Log4jWebConfigurer類的initLogging方法,首先,調用WebUtils.setWebAppRootSystemProperty方法,內部調用servletContext.getRealPath("/")方法獲取工程實際運行的絕對路徑(如:F:\004_SVN\IBP\springweb\target\spring-web-1.0-SNAPSHOT\),設置到系統變量中(System.setProperty),注意這里的key值是可以配置的,通過webAppRootKey參數配置,如在本例子的web.xml中配成了petclinic.root;
        public static void setWebAppRootSystemProperty(ServletContext servletContext) {
            String param = servletContext.getInitParameter(WEB_APP_ROOT_KEY_PARAM);
            String key = (param != null ? param : DEFAULT_WEB_APP_ROOT_KEY);
            String oldValue = System.getProperty(key);
            if (oldValue != null) {
                servletContext.log("WARNING: Web app root system property already set: " + key + " = " + oldValue);
                servletContext.log("WARNING: Choose unique webAppRootKey values in your web.xml files!");
            } else {
                String root = servletContext.getRealPath("/");
                System.setProperty(key, root);
                servletContext.log("Set web app root system property: " + key + " = " + root);
            }
        }
  3. 獲取日志配置文件路徑、刷新間隔等配置信息,日志配置文件路徑可根據log4jConfigLocation參數配置,這里配置的是相對路徑,通過調用ServletContext.getRealPath()獲得完整路徑,注意getRealPath方法的參數要以“/”開頭;刷新間隔可根據log4jRefreshInterval參數配置,默認為60s;
        public static void initLogging(ServletContext servletContext) {
            // set the web app root system property
            WebUtils.setWebAppRootSystemProperty(servletContext);
    
            // only perform custom Log4J initialization in case of a config file
            String location = servletContext.getInitParameter(CONFIG_LOCATION_PARAM);
            if (location != null) {
    
                // interpret location as relative to the web application root directory
                if (location.charAt(0) != '/') {
                    location = "/" + location;
                }
                location = servletContext.getRealPath(location);
    
                // use default refresh interval if not specified
                long refreshInterval = Log4jConfigurer.DEFAULT_REFRESH_INTERVAL;
                String intervalString = servletContext.getInitParameter(REFRESH_INTERVAL_PARAM);
                if (intervalString != null) {
                    refreshInterval = Long.parseLong(intervalString);
                }
    
                // write log message to server log
                servletContext.log("Initializing Log4J from " + location);
    
                // perform actual Log4J initialization
                try {
                    Log4jConfigurer.initLogging(location, refreshInterval);
                } catch (FileNotFoundException ex) {
                    throw new IllegalArgumentException("Invalid log4jConfigLocation parameter: " + ex.getMessage());
                }
            }
        }
  4. 進入Log4jConfigurer類的initLogging方法,initLogging比較簡單,根據配置文件后綴名,使用相應的解析器解析配置文件中的元素。
        public static void initLogging(String location, long refreshInterval) throws FileNotFoundException {
            if (!(new File(location)).exists()) {
                throw new FileNotFoundException("Log4j config file [" + location + "] not found");
            }
            if (location.toLowerCase().endsWith(XML_FILE_EXTENSION)) {
                DOMConfigurator.configureAndWatch(location, refreshInterval);
            } else {
                PropertyConfigurator.configureAndWatch(location, refreshInterval);
            }
        }

另外補充下,當Servlet容器銷毀時,會調用Log4jConfigListener的contextDestroyed方法,最終是調用LogManager.shutdown,執行一些資源關閉等操作;

interface21代碼參考

 https://github.com/peterchenhdu/interface21


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM