服务提供者框架(Service Provider Framework)

释放双眼,带上耳机,听听看~!

服务提供者框架是指:多个服务提供者实现一个服务,系统为客户端提供多个实现,并把他们从多个实现中解耦出来。服务提供者的改变对它们的客户端是透明的,这样提供了更好的可扩展性。例如,JDBC,JMS等就是用了服务提供者框架
从字面意思看就是:实何实现服务提供者提供服务功能的设计结构,主体有服务、服务提供者。反映到我们程序中,通常有四个组件:
Service Interface:服务接口,将服务通过抽象统一声明,供客户端调用、由各个服务提供者具体实现。
Provider Registration API:服务提供者注册API,用于系统注册服务提供者,使得客户端可以访问它实现的服务。
Service Access API:服务访问API,用户客户端获取相应的服务。
Service Provider Interface:服务提供者接口,这些服务提供者负责创建其服务实现的实例。(可选)

     服务接口定义服务。服务提供者接口产生服务实例。 
   首先我们来看一下典型的服务提供者框架的程序结构是怎么样的:
   大家都知道都在我们国家盐是归定必须加碘才能在市场上卖的。食盐又分为海盐和内陆盐,食盐生产厂家在要生产食盐之前都必须去盐监局登记。

[java] view plain copy
/**

  • @author <a href=”mailto:amoszhou@foxmail.com”>amos zhou</a>
  • @Function:食盐
  • @Since Jan 30, 2012
  • */
    public interface Salt {

    /**

    • @Function:加碘
    • @Since Jan 30, 2012
      */
      void addIodine();
      }

现在这个加碘是盐监局制定的一个规定,那么海盐厂商和内陆盐厂商分别有自己的加碘方式,他们对这个规定有自己不同的实现。

[java] view plain copy
/**

  • @author <a href=”mailto:amoszhou@foxmail.com”>amos zhou</a>
  • @Function:海盐
  • @Since Jan 30, 2012
  • */
    public class Baysalt implements Salt {

    public void addIodine() {
    // TODO 加碘操作

    }

}

//内陆盐
public class InlandSalt implements Salt {

public void addIodine() {  
    // TODO 加碘操作        
}  

}

要加碘,我们就首先得有盐啊,那么各厂商有自己生产盐的方式。
[java] view plain copy
public interface SaltProvider {

Salt  getSalt();  

}

/**

  • @author <a href=”mailto:amoszhou@foxmail.com”>amos zhou</a>
  • @Function:海盐厂商
  • @Since Jan 30, 2012
  • */
    public class BaysaltProvider implements SaltProvider {

    /**

    • 去盐监司登记
      */
      static{
      SaltManager.registerProvider(“baysaltProvider”, new BaysaltProvider());
      }

    public Salt getSalt() {
    // 用太阳晒,把水份蒸发
    return new Baysalt();
    }
    }

/**

  • @author <a href=”mailto:amoszhou@foxmail.com”>amos zhou</a>
  • @Function:内陆盐厂商
  • @Since Jan 30, 2012
  • */
    public class InlandSaltProvider implements SaltProvider {

    /**

    • 去盐监司登记
      */
      static{
      SaltManager.registerProvider(“inlandSalt”, new InlandSaltProvider());
      }

    public Salt getSalt() {
    //用挖掘机挖
    return new InlandSalt();
    }

}

[java] view plain copy
/**

  • @author <a href=”mailto:amoszhou@foxmail.com”>amos zhou</a>
  • @Function:盐监司
  • @Since Jan 30, 2012
  • */
    public class SaltManager {

    /**

    • 食盐生产厂商登记档案
      */
      private static final Map<String, SaltProvider> providers = new ConcurrentHashMap<String, SaltProvider>();

    /**

    • @Function:食盐厂商登记
    • @Since Jan 30, 2012
    • @param name
    • @param p
      */
      public static void registerProvider(String name, SaltProvider p) {
      providers.put(name, p);
      }

    /**

    • @Function: 获取食盐
    • @Since Jan 30, 2012
    • @param name
    • @return
      */
      public static Salt getSalt(String name) {

      SaltProvider p = providers.get(name);
      if (p == null) {
      throw new IllegalArgumentException(
      “No SaltProvider registered with name:” + name);
      }
      return p.getSalt();

    }
    }

测试代码:

[java] view plain copy
public class Test {

/** 
 * @Function: 
 * @Since Jan 30, 2012 
 * @param args 
 * @throws ClassNotFoundException 
 */  
public static void main(String[] args) throws ClassNotFoundException {  
    Class.forName("com.amos.spf.InlandSaltProvider");  
    Salt salt = SaltManager.getSalt("inlandSalt");  
    salt.addIodine();  

}  

}

      上面这个例子可能不在严谨,在理解上有点费力,但是请细心想想,感悟。服务提供者框架就是这样的:服务的具体实现对于客户端是透明的,用户只知道Salt接口,并不是知道海盐和内陆盐,具体的实现由服务提供者实现。服务提供者的主要任务就是将自己注册到服务管理器中,并产生服务接口的实例。
     也就是说:对服务标准制定者而言:我制定了一个服务标准,然后定义了一个类似于字典的服务管理器,提供一个注册的接口给你,你实现了服务以后就自己注册到字典里面去。我并不知道你是如何实现这个服务的。对于服务实现厂商而言,我只知道我要实现的服务标准,以及我如何注册,我只需要做两件事:1、实现服务。2、注册到服务管理器中。对最终用户而言,我只需要知道怎么用就行了,至于你们是怎么实现的跟我没半毛钱关系。

       引申至JDBC中:
    Connection接口就是一个服务接口,定义了很多操作,但是JDBC本身不对该服务进行实现,而是由MySQL,sqlServer、Oracle、DB2等各数据库厂商去实现。然后注册到DriverManager中。 用户只需要根据注册时的KEY 去查找到相关的服务即可。

      以mysql为例:

[java] view plain copy
Class.forName(“com.mysql.jdbc.Driver”);
DriverManager.getConnection(“jdbc:mysql://localhost:3306/test”,”root”,”123″);

[java] view plain copy
Class.forName(“com.mysql.jdbc.Driver”);
从这句我们可以看出:com.sql.jdbc.Driver是一个服务提供者,他提供服务,并注册到DriverManager中。至于mysql中Connection的具体实现,我们甚连类名都不知道(在不看源码的情况下)。

我们来看一下DriverManager中getConnection的源码实现吧。
[java] view plain copy
private static Connection getConnection(
String url, java.util.Properties info, ClassLoader callerCL) throws SQLException {
java.util.Vector drivers = null;
/*

  • When callerCl is null, we should check the application’s
  • (which is invoking this class indirectly)
  • classloader, so that the JDBC driver class outside rt.jar
  • can be loaded from here.
    */
    synchronized(DriverManager.class) {
    // synchronize loading of the correct classloader.
    if(callerCL == null) {
    callerCL = Thread.currentThread().getContextClassLoader();
    }
    }

    if(url == null) {
    throw new SQLException(“The url cannot be null”, “08001”);
    }

    println(“DriverManager.getConnection(/”” + url + “/”)”);

    if (!initialized) {
    initialize();
    }

    synchronized (DriverManager.class){
    // use the readcopy of drivers
    drivers = readDrivers;
    }

    // Walk through the loaded drivers attempting to make a connection.
    // Remember the first exception that gets raised so we can reraise it.
    SQLException reason = null;
    for (int i = 0; i < drivers.size(); i++) {
    DriverInfo di = (DriverInfo)drivers.elementAt(i);

    // If the caller does not have permission to load the driver then
    // skip it.
    if ( getCallerClass(callerCL, di.driverClassName ) != di.driverClass ) {
    println(” skipping: ” + di);
    continue;
    }
    try {
    println(” trying ” + di);
    Connection result = di.driver.connect(url, info);
    if (result != null) {
    // Success!
    println(“getConnection returning ” + di);
    return (result);
    }
    } catch (SQLException ex) {
    if (reason == null) {
    reason = ex;
    }
    }
    }

    // if we got here nobody could connect.
    if (reason != null) {
    println(“getConnection failed: ” + reason);
    throw reason;
    }

    println(“getConnection: no suitable driver found for “+ url);
    throw new SQLException(“No suitable driver found for “+ url, “08001”);
    }

其中最关键的是一那个For循环,drivers是一个Vector向量,他充当字典。对整个字典进行循环,对我们要查找的类和字曲中的所有的Class进行匹配,若能匹配成功即连接数据库,否则抛出异常。

    好啦,关于这个概念就写到这,关键还是要自己去感觉、体会。代码这东西很多东西,只可意会,不可言传!

【转自慕课】https://www.imooc.com

JAVA

Mybatis在Java代码审计中的审计方法及修复方法

2022-3-3 16:04:06

JAVA

spring源码之refresh方法

2022-3-3 16:18:18

搜索