Java讀源碼學設計模式:適配器Adapter

來源:互聯網
上載者:User

適配器模式相關源碼:slf4j-1.6.1、hibernate-3.6.7


大家都知道,log4j是一個廣泛使用的日誌工具,除此之外,sun公司在JDK中也有自己的日誌工具,也就是java.util.logging.Logger。當然還有其他一些日誌工具。

多種日誌工具功能和使用方式類似,一般都包含debug、info、warn、error等記錄層級的方法,但卻沒有實現共同的介面。這一點不像JDBC,雖然關係型資料庫種類很多,例如MySQL、Oracle等,但是有一套統一的介面,也就是JDBC。

當然,如果你自己寫一個項目,只要隨便找一個日誌工具用就行。但是,一些開源架構的作者就比較糾結了,他不知道你的系統用的是哪種日誌工具,就不知道他在開源架構中使用哪一個日誌工具。

slf4j提供了一個共同的介面,並實現了不同日誌工具的適配器。所以開源架構中日誌只需要調用slf4j提供的介面就行,不需要關心到底是用的哪個實作類別。例如ORM架構Hibernate的日誌就依賴slf4j。

和slf4j類似的還有commons-logging等。


源碼(由於源碼巨長,所以下面省略無關代碼):

slf4j提供統一的介面org.slf4j.Logger,該介面提供給用戶端(如Hibernate)去調用:

package org.slf4j;public interface Logger {  public boolean isTraceEnabled();  public void trace(String msg);  public void trace(String format, Object arg);  public void trace(String format, Object arg1, Object arg2);  public void trace(String format, Object[] argArray);  public void trace(String msg, Throwable t);  public boolean isDebugEnabled();  public void debug(String msg);  public void debug(String format, Object arg);  public void debug(String format, Object arg1, Object arg2);  public void debug(String format, Object[] argArray);  public void debug(String msg, Throwable t);  public boolean isInfoEnabled();  public void info(String msg);  public void info(String format, Object arg);  public void info(String format, Object arg1, Object arg2);  public void info(String format, Object[] argArray);  public void info(String msg, Throwable t);  public boolean isWarnEnabled();  public void warn(String msg);  public void warn(String format, Object arg);  public void warn(String format, Object[] argArray);  public void warn(String format, Object arg1, Object arg2);  public void warn(String msg, Throwable t);  public boolean isErrorEnabled();  public void error(String msg);  public void error(String format, Object arg);  public void error(String format, Object arg1, Object arg2);  public void error(String format, Object[] argArray);  public void error(String msg, Throwable t);}

在用戶端hibernate中不是直接調用log4j或JDK logger,而是使用org.slf4j.Logger介面。任取hibernate中有日誌的一段代碼:

(註:LoggerFactory.getLogger如何?本文不需要關注)

package org.hibernate.impl;import org.slf4j.Logger;import org.slf4j.LoggerFactory;public final class SessionFactoryImpl implements SessionFactory, SessionFactoryImplementor {private static final Logger log = LoggerFactory.getLogger(SessionFactoryImpl.class);private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {log.trace("deserializing");in.defaultReadObject();log.debug("deserialized: " + uuid);}private void writeObject(ObjectOutputStream out) throws IOException {log.debug("serializing: " + uuid);out.defaultWriteObject();log.trace("serialized");}}

而log4j以及JDK的logger並沒有實現slf4j的org.slf4j.Logger介面,所以slf4j要提供適配器來實現org.slf4j.Logger介面,由適配器去調用log4j或JDK的logger去實現日誌,從而實現日誌工具相容。(注意:源碼中可以看出LocationAwareLogger介面繼承org.slf4j.Logger所以實現LocationAwareLogger相當於實現了org.slf4j.Logger)

Log4j適配器org.slf4j.impl.Log4jLoggerAdapter:

package org.slf4j.impl;import java.io.Serializable;import org.apache.log4j.Level;import org.slf4j.Logger;import org.slf4j.Marker;import org.slf4j.helpers.FormattingTuple;import org.slf4j.helpers.MessageFormatter;import org.slf4j.spi.LocationAwareLogger;public final class Log4jLoggerAdapter extends MarkerIgnoringBase implements    LocationAwareLogger, Serializable {  final transient org.apache.log4j.Logger logger;  public boolean isDebugEnabled() {    return logger.isDebugEnabled();  }  public void debug(String msg) {    logger.log(FQCN, Level.DEBUG, msg, null);  }  public void debug(String format, Object arg) {    if (logger.isDebugEnabled()) {      FormattingTuple ft = MessageFormatter.format(format, arg);      logger.log(FQCN, Level.DEBUG, ft.getMessage(), ft.getThrowable());    }  }  public void debug(String format, Object arg1, Object arg2) {    if (logger.isDebugEnabled()) {      FormattingTuple ft = MessageFormatter.format(format, arg1, arg2);      logger.log(FQCN, Level.DEBUG, ft.getMessage(), ft.getThrowable());    }  }  public void debug(String format, Object[] argArray) {    if (logger.isDebugEnabled()) {      FormattingTuple ft = MessageFormatter.arrayFormat(format, argArray);      logger.log(FQCN, Level.DEBUG, ft.getMessage(), ft.getThrowable());    }  }  public void debug(String msg, Throwable t) {    logger.log(FQCN, Level.DEBUG, msg, t);  }}

Jdk logger適配器org.slf4j.impl.JDK14LoggerAdapter:

package org.slf4j.impl;import java.util.logging.Level;import org.slf4j.Marker;import org.slf4j.helpers.FormattingTuple;import org.slf4j.helpers.MarkerIgnoringBase;import org.slf4j.helpers.MessageFormatter;import org.slf4j.spi.LocationAwareLogger;public final class JDK14LoggerAdapter extends MarkerIgnoringBase implements    LocationAwareLogger {  final java.util.logging.Logger logger;  public boolean isDebugEnabled() {    return logger.isLoggable(Level.FINE);  }  public void debug(String msg) {    if (logger.isLoggable(Level.FINE)) {      log(SELF, Level.FINE, msg, null);    }  }  public void debug(String format, Object arg) {    if (logger.isLoggable(Level.FINE)) {      FormattingTuple ft = MessageFormatter.format(format, arg);      log(SELF, Level.FINE, ft.getMessage(), ft.getThrowable());    }  }  public void debug(String format, Object arg1, Object arg2) {    if (logger.isLoggable(Level.FINE)) {      FormattingTuple ft = MessageFormatter.format(format, arg1, arg2);      log(SELF, Level.FINE, ft.getMessage(), ft.getThrowable());    }  }  public void debug(String format, Object[] argArray) {    if (logger.isLoggable(Level.FINE)) {      FormattingTuple ft = MessageFormatter.arrayFormat(format, argArray);      log(SELF, Level.FINE, ft.getMessage(), ft.getThrowable());    }  }  public void debug(String msg, Throwable t) {    if (logger.isLoggable(Level.FINE)) {      log(SELF, Level.FINE, msg, t);    }  }}

在適配器模式中,一般包含以下幾個部分:

Adaptee:真正實現功能的實作類別,但是與用戶端不相容。也就是上面代碼中的java.util.logging.Logger、org.apache.log4j.Logger。

Target:給用戶端調用的介面,適配器實現這個介面。就是上面代碼中的org.slf4j.Logger。

Adapter:適配器,適配器實現Target介面,具體功能調用Adaptee來實現。就是上面的org.slf4j.impl.Log4jLoggerAdapter、org.slf4j.impl.JDK14LoggerAdapter。

Client:調用Target介面。就是上面的Hibernate。


總結:

有多個類似的類(例如java.util.logging.Logger、org.apache.log4j.Logger),沒有統一的介面,但是用戶端又都想要相容。遇到這種情況,最好的辦法是重構,也就是讓他們實現同一介面。但是如果重構成本太大或者根本就無法實現同一介面(例如上面的例子,log4j和JDK logger根本就不是一家的),就必須創造出統一的介面(即org.slf4j.Logger),並為每個類寫一個適配器(即org.slf4j.impl.Log4jLoggerAdapter、org.slf4j.impl.JDK14LoggerAdapter),讓適配器來實現統一的介面,並調用具體的實作類別來實現,已達到相容的目的。


作者:叉叉哥   轉載請註明出處:http://blog.csdn.net/xiao__gui/article/details/32695647



聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.