SLF4J api 和 binding jar 版本不相容導致的 IllegalAccessError

iteye_401發表於2013-08-05

 

這是前幾天碰到的一個由 SLF4J 引發的異常

Exception in thread "main" java.lang.IllegalAccessError: tried to access field
org.slf4j.impl.StaticLoggerBinder.SINGLETON from class org.slf4j.LoggerFactory
   at org.slf4j.LoggerFactory.<clinit>(LoggerFactory.java:60)

在網上搜尋時,找到了 SLF4J FAQ 這篇文件,其中就有對這個異常的描述,當時是把問題解決了。為了將這個問題的解決過程描述方便,我先寫了之前的一篇文章,簡單介紹了一些 SLF4J 的知識。因為這個異常的產生原因是由於 slf4j-api.jar 與 slf4j-log4j12.jar 版本不相容所致,其中 slf4j-log4j12.jar 是 SLF4J 提供的五種 binding jar 之一。具體的說,是由於使用了較老版本的 slf4j-api.jar(在我的應用裡引的版本是 1.4.1),和較新版本的 slf4j binding jar(在我的應用裡引了 slf4j-log4j12-1.5.6.jar)。

 

錯誤的出處是: 我定義了一個類,為了使用 log4j 列印日誌,呼叫 org.slf4j.LoggerFactory 建立了一個 Logger,並作為類的 static 屬性,除此之外無其他的成員屬性,程式碼如下:

public class Foo {
    private static final Logger logger = LoggerFactory.getLogger(Foo.class);
    // ... methods
}

 在獲取 Logger 時拋了前面的異常。

 

下面通過原始碼分析下異常的來源:

在 slf4j-api 的早期版本(1.5.5 及之前)中,org.slf4j.LoggerFactory 中包含一個 ILoggerFactory 的 static 屬性,並且通過 static 塊進行初始化。

public final class LoggerFactory {

  static ILoggerFactory loggerFactory;

  static {
      try { 
        loggerFactory = StaticLoggerBinder.SINGLETON.getLoggerFactory();
      } catch(NoClassDefFoundError ncde) {
      //...
   }
}

 在早期版本的 slf4j binding 實現中,org.slf4j.impl.StaticLoggerBinder 類中的 SINGLETON 成員是被定義為 public 的。

public class StaticLoggerBinder { 

  /**
   * The unique instance of this class.
   */
  public static final StaticLoggerBinder SINGLETON = new StaticLoggerBinder();

  public ILoggerFactory getLoggerFactory() {
    //...
   }
}

 所以,如果 slf4j-api.jar 和 slf4j-log4j12.jar 都使用早期版本,將不會報錯。

 

從 SLF4J 1.5.6 開始, slf4j binding 實現中的 org.slf4j.impl.StaticLoggerBinder 類的 SINGLETON 被定義為了 private. 並且從這個版本開始,slf4j api 中 org.slf4j.LoggerFactory 不再使用 StaticLoggerBinder.SINGLETON.getLoggerFactory() 的方式獲取 ILoggerFactory 的例項,而是通過 StaticLoggerBinder.getSingleton.getLoggerFactory() 的方式對 ILoggerFactory 例項進行初始化。具體的實現要比早期版本複雜的多,在此不詳細列程式碼了。

 

所以,如果使用早期版本的 slf4j-api.jar 和新版本的 slf4j binding jar,由於 org.slf4j.LoggerFactory 無法訪問新版本 binding 實現中的 StaticLoggerBinder.SINGLETON 私有屬性,就會產生前面的異常資訊。

相關文章