Log4j源码阅读之三—initialConfiguration方法

来源:互联网 发布:淘宝国际转运店铺 编辑:程序博客网 时间:2024/05/20 08:27

上回我们已经对LogManager类的静态程序块进行了部分解读,现在再次将源码贴上,如下是LogManager类静态程序块全部源码+注释

static {
        // Check debug

        // 1,从运行环境获取log4j.debug的配置值
        String debugProp = System.getProperty("log4j.debug");

       //2,对配置值进行判断
        if(Boolean.valueOf(debugProp).booleanValue()) {
            debug = true;
        }

      //3,如果debug为true,那么进行输出
        if(debug) {
            System.out.println("**Start of LogManager static initializer");
        }

      //4,构建LoggerRespository接口的实现类实例,并将其赋值到对应的属性上
        Hierarchy hierarchy =  new Hierarchy(new RootLogger(Level.DEBUG));
        defaultLoggerRepository = hierarchy;
        hierarchy.setName(Constants.DEFAULT_REPOSITORY_NAME);
        

      //5,构建RepositorySelector接口的实现类实例,并将其赋值到对应的属性上
        // temporary repository
        repositorySelector = new DefaultRepositorySelector(defaultLoggerRepository);
        
        //  Attempt to perform automatic configuration of the default repository

      //6,从运行环境获取log4j.configuratorClass配置值
        String configuratorClassName =
            OptionConverter.getSystemProperty(Constants.CONFIGURATOR_CLASS_KEY, null);

        //7,从运行环境获取og4j.configuration属性值
        String configurationOptionStr = 
            OptionConverter.getSystemProperty(Constants.DEFAULT_CONFIGURATION_KEY, null);
       //如果og4j.configuration属性值为null
        if (configurationOptionStr == null) {

         //获取log4j.xml的文件路径Url,并判断是否存在,如果不存在,那么就获取log4j.properties
            if (Loader.getResource(Constants.DEFAULT_XML_CONFIGURATION_FILE) != null) {
                configurationOptionStr = Constants.DEFAULT_XML_CONFIGURATION_FILE;
            } else if (
                       Loader.getResource(Constants.DEFAULT_CONFIGURATION_FILE) != null) {
                configurationOptionStr = Constants.DEFAULT_CONFIGURATION_FILE;
            }
        }
        
        if(debug) {
            System.out.println("*** configurationOptionStr=" + configurationOptionStr);
        }
      //8,初始化配置
        IntializationUtil.initialConfiguration(
                                               defaultLoggerRepository, configurationOptionStr, configuratorClassName);
        //9,从运行环境获取og4j.repositorySelector配置值
        String repositorySelectorStr = 
            OptionConverter.getSystemProperty("log4j.repositorySelector", null);
        //10,如果为空,那么什么都不做,否则判断是否配置值里边是否等于“JNDI”
        if (repositorySelectorStr == null) {
            // NOTHING TO DO, the default repository has been configured already
        } else if (repositorySelectorStr.equalsIgnoreCase("JNDI")) {
            if(debug) {
                System.out.println("*** Will use ContextJNDISelector **");
            }

       //11,创建一个ContextJNDISelecto实例和一个Object对象
            repositorySelector = new ContextJNDISelector();
            guard = new Object();
        } else {

          //12,实例化repositorySelectorStr
            Object r =
                OptionConverter.instantiateByClassName(
                                                       repositorySelectorStr, RepositorySelector.class, null);
            //13,判断是否是RepositorySelector的实例,如果是,那么进行赋值
            if (r instanceof RepositorySelector) {
                if(debug) {
                    System.out.println(
                                       "*** Using [" + repositorySelectorStr
                                       + "] instance as repository selector.");
                }
                repositorySelector = (RepositorySelector) r;
                guard = new Object();
            } else {

             //
                if(debug) {
                    System.out.println(
                                       "*** Could not insantiate [" + repositorySelectorStr
                                       + "] as repository selector.");
                    System.out.println("*** Using default repository selector");
                }

              // 14 ,穿件一个RepositorySelector接口实现类的实例
                repositorySelector = new DefaultRepositorySelector(defaultLoggerRepository);
            }
        }


        if(debug) {
            System.out.println("** End of LogManager static initializer");
        }
    }

通过源码上的14点注释,我们可以知道,目前这段静态代码最主要的逻辑就在initialConfiguration这个方法里边,即注释8处。

首先,我们来看看这个方法的参数:  public static void initialConfiguration(LoggerRepository repository, String configuratonResourceStr,String configuratorClassNameStr)

1,参数repository,我们在LogManager传递的这个参数是defaultLoggerRepository指向,即LoggerRepository接口实现类Hierarchy的实例

2,参数configuratonResourceStr,我们从源码上可以知道,这个是log4j.properties 字符串

3,参数 configuratorClassNameStr,是获取系统运行环境log4j.configuratorClass的配置值,这个值我们目前可以认为是空字符串

参数确定之后,我们进入这个方法的内部进行源码的查看:

 public static void initialConfiguration(LoggerRepository repository, 
                                          String configuratonResourceStr,
                                          String configuratorClassNameStr) {
              //1,如果log4j.properties 为空,那么直接返回空
    if(configuratonResourceStr == null) {
      return;
    }
    URL url = null;

   //2,获取log4j.properties的Url路径对象
    try {
      url = new URL(configuratonResourceStr);
    } catch (MalformedURLException ex) {
      // so, resource is not a URL:
      // attempt to get the resource from the class loader path
      // Please refer to Loader.getResource documentation.
      url = Loader.getResource(configuratonResourceStr);
    }


    // If we have a non-null url, then delegate the rest of the
    // configuration to the OptionConverter.selectAndConfigure
    // method.

//3,如果url路径对象不为空
    if (url != null) {

//4,判断repository是否 LoggerRepositoryEx类的实现类,根据LoggerRepository的继承关系,我们可以知道,我们传入的参数就是它的实例
      if (repository instanceof LoggerRepositoryEx) {
        LogLog.info(
            "Using URL [" + url
            + "] for automatic log4j configuration of repository named ["+
            ((LoggerRepositoryEx) repository).getName()+"].");
      } else {
          LogLog.info(
              "Using URL [" + url
              + "] for automatic log4j configuration of unnamed repository.");
      }

   //5,根据参数选择初始化工具
      OptionConverter.selectAndConfigure(url, configuratorClassNameStr, repository);
    }
  }

从源码上可以知道,我们在这里值对参数1,2进行了一些判断和封装(将 log4j.properties封装为Url对象),如何直接将判断过和封装好的参数直接提交给了OptionConverter类

的静态方法selectAndConfigure中,那我们再深入这个方法的源码中:

 public static void selectAndConfigure(
    URL url, String clazz, LoggerRepository repository) {
    Configurator configurator = null;

  //1,从url对象中获取文件
    String filename = url.getFile();

//2,clazz参数进行非空判断,根据我们上个方法的描述,可以知道这个参数为空,fileName不为空,并且也不是以.xml结尾,那么这段代码我们暂不进入
    if ((clazz == null) && (filename != null) && filename.endsWith(".xml")) {
      clazz = JoranConfigurator.class.getName();
    }

//3,clazz参数进行非空判断,那么这段代码我们也不进入
    if (clazz != null) {
      Logger logger = repository.getLogger(OptionConverter.class.getName());
      logger.info("Preferred configurator class: " + clazz);


      configurator =
        (Configurator) instantiateByClassName(clazz, Configurator.class, null);


      if (configurator == null) {
        logger.error("Could not instantiate configurator [" + clazz + "].");
        
        return;
      }
    } else {

  //4,我们直接在这里new了一个PropertyConfigurator类的对象
      configurator = new PropertyConfigurator();
    }
    //5,然后直接调用了PropertyConfigurator对象的doConfigure
    configurator.doConfigure(url, repository);

   //6,进行判断,我们查看configuratorBase的继承关系

//从继承关系上我们可以知道,这段代码会进入,会调用PropertyConfigurator对象的dumpErrors()
    if(configurator instanceof ConfiguratorBase) {
      ((ConfiguratorBase)configurator).dumpErrors();
    }
  }
}

从源码的注释上,我们可以了解到,这里的源码只对url和clazz这2个参数进行了一些判断,并根据判断创建了一个configuratorBase类的子类对象,最后调用了这个子类对象

的doConfigure和dumpErrors方法,根据这2个方法的参数,我们可以知道,主要的逻辑必定在doConfigure这个方法中。我们深入到PropertyConfigurator这个类里确定下这个方法的参数:  public void doConfigure(java.net.URL configURL, LoggerRepository repository)

1,configURL,我们传递的是由log4j.properties构建的Url对象

2,repository,还是由我们在LogManager上创建的LoggerRepository接口实现类Hierarchy的实例

我们再看下PropertyConfigurator类中的 doConfigure源码,看看这里做了什么:

  public void doConfigure(java.net.URL configURL, LoggerRepository repository) {
    Properties props = new Properties();
    getLogger(repository).debug(
      "Reading configuration from URL {}", configURL);


    InputStream in = null;
    try {

     //1,将Url转换为输入流,并将log4j.properties里的配置放入的properties对象中
      in = configURL.openStream();
      props.load(in);
    } catch (Exception e) {
      String errMsg =
        "Could not read configuration file from URL [" + configURL + "].";
      addError(new ErrorItem(errMsg, e));
      getLogger(repository).error(errMsg, e);
      return;
    } finally {
        if (in != null) {
            try {
                in.close();
            } catch(IOException ignored) {
            }
        }
    }

//2,将properties对象和repository传入到 doConfigure方法中
    doConfigure(props, repository);
  }

从上述注释中,我们可以知道,这里只将 log4j.properties配置文件里边的值封装为properties对象,然后将封装好的对象和之前传递的repository参数传递给同名方doConfigure

通过上述源码的阅读,我们发现我们深入代码层级过多,得返回来回顾下,以免弄乱了阅读流程。我们将上述流程转换为时序图如下:


我们总结如下:

1,在调用LogManager中我们将loggerRepository接口实现类Hierarchy对象,配置文件log4j.properties,以及一个configuratorClassNameStr空字符串作为参数传入 initialConfiguration方法中

2,在initialConfiguration方法中,我们将配置文件log4j.properties进行了url对象的封装,同时对LoggerRepository参数进行了一些判断,然后通过 OptionConverter类的

     selectAndConfigure

3,在selectAndConfigure方法中,我们队url对象进行了一些判断,同时根据configuratorClassNameStr和url对象的判断,创建了一个ConfiguratorBase子类PropertyConfigurator对象,然后分别调用了这个子类对象的doConfigure和dumpErrors2个方法

4,在doConfigure方法中,我们只是将url对象封装成properties对象,然后就直接调用了同名方法doConfigure

通过总结我们可以知道,代码的主要逻辑已经提交到了doConfigure(Properties properties, LoggerRepository repository) 这个方法中(注意同名方法),这个方法做了那些事?里边有什么逻辑呢?对传入的2个参数进行了做了什么手脚?我们看下回 Log4j源码阅读之四—doConfigure方法


0 0
原创粉丝点击