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

posted @ 2018-08-22 16:47  风一样的码农  阅读(1576)  评论(3编辑  收藏  举报