Java读源码学设计模式:适配器Adapter

适配器模式相关源码: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



Java读源码学设计模式:适配器Adapter,布布扣,bubuko.com

Java读源码学设计模式:适配器Adapter

上一篇:C++11中的继承构造函数


下一篇:java 输入和输出 代码示例