服务提供者框架是指:多个服务提供者实现一个服务,系统为客户端提供多个实现,并把他们从多个实现中解耦出来。服务提供者的改变对它们的客户端是透明的,这样提供了更好的可扩展性。例如,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进行匹配,若能匹配成功即连接数据库,否则抛出异常。
好啦,关于这个概念就写到这,关键还是要自己去感觉、体会。代码这东西很多东西,只可意会,不可言传!