欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

架构设计-java spi实现模块间的松耦合

程序员文章站 2022-06-13 15:09:16
...

 一。前言

       在面向的对象的设计里,一般推荐模块之间基于接口编程,模块之间不对实现类进行硬编码。一旦代码里涉及具体的实现类,就违反了可拔插的原则,如果需要替换一种实现,就需要修改源代码,然后重新编译、发布。

 

        为了实现在模块装配的时候能不在程序里动态指明,这就需要一种服务发现机制。java spi就是提供这样的一个机制:为某个接口寻找服务实现的机制。有点类似spring IOC的思想,就是将装配的控制权移到程序之外,在模块化设计中这个机制尤其重要。

 

很多开源项目都使用这种机制。

 

      java spi的具体约定为:

当服务的提供者,提供了服务接口的一种实现之后,在jar包的META-INF/services/目录里同时创建一个以服务接口命名的文件。该文件里就是实现该服务接口的具体实现类。而当外部程序装配这个模块的时候,就能通过该jar包META-INF/services/里的配置文件找到具体的实现类名,并装载实例化,完成模块的注入。基于这样一个约定就能很好的找到服务接口的实现类,而不需要再代码里制定。

 java.sql.Driver就是这么实现的,我们可以看看mysql是如何扩展它的。

   

 打开mysql的jdbc jar包,在META-INF/services下有一个java.sql.Drvier的文件,

架构设计-java spi实现模块间的松耦合
            
    
    博客分类: 架构设计java spi架构设计 
 

文件内容如下:
架构设计-java spi实现模块间的松耦合
            
    
    博客分类: 架构设计java spi架构设计 

 

 

二。实例开发

在代码中,如何使用java的spi机制,这时就要借助java.util.ServiceLoader了。

开发步骤如下:

1、编写一个接口

2、编写接口的实现类

3、在/src/META-INF/services下新建一个文件,文件名为接口的包名+接口名,如果是maven构建,就在src/main/resources/META-INF/services

4、把实现类的全名写在该文件中,一个实现类占一行。

 

 

package com.wxj.spi;

/**
 * 保存接口
 * @author wxj
 *
 */
public interface Store
{

	/**
	 * 保存数据
	 * @param data 数据
	 * @return 是否保存成功
	 */
	public boolean store(Object data);
	
}

 

 

package com.wxj.spi;

/**
 * 用文件保存数据
 * @author wxj
 *
 */
public class FileStore implements Store
{

	@Override
	public boolean store(Object data)
	{
		System.out.println("file store successfully");
		return true;
	}

}
 

 

 

package com.wxj.spi;

/**
 * 用数据库保存数据
 * @author wxj
 *
 */
public class DBStore implements Store
{

	@Override
	public boolean store(Object data)
	{
		System.out.println("db store successfully");
		return true;
	}

}
 

 

 src/META-INF/services/com.wxj.spi.Store的文件内容:

com.wxj.spi.FileStore
com.wxj.spi.DBStore
 

 

测试类

 

package com.wxj.spi;

import java.util.Iterator;
import java.util.ServiceLoader;

/**
 * 测试类
 * @author wxj
 *
 */
public class TestStore
{
	
	public static void main(String[] args)
	{
		ServiceLoader<Store> stores = ServiceLoader.load(Store.class);
		Iterator<Store> it = stores.iterator();
		Store s = null;
		String data = "datas";
		while(it.hasNext())
		{
			s = it.next();
			s.store(data);
		}
	}

}
测试结果:
file store successfully
db store successfully
  

 问题

问题1:为什么配置文件要放在META-INF/services下面?

问题2:java.util.ServiceLoader是如何找到这些实现类的?

 

三。java.util.ServiceLoader源码分析
 
架构设计-java spi实现模块间的松耦合
            
    
    博客分类: 架构设计java spi架构设计 
 


架构设计-java spi实现模块间的松耦合
            
    
    博客分类: 架构设计java spi架构设计 
 


架构设计-java spi实现模块间的松耦合
            
    
    博客分类: 架构设计java spi架构设计 
 

看完这段代码,相信都明白是怎么回事了。

 

1. META-INF/services这个路径在ServiceLoader的源码中就写死了。

2. ServiceLoader通过懒加载的方式去读services路径下的文件内容,然后通过class.forName()反射的方式生成接口对应的实现类。

 

 四。API与SPI的区别与联系

1. API直接为你提供了功能,你使用API就能完成任务。
2. SPI是一种回调的思想,回调是指我们在使用api时,我们可以向api传入一个类或者方法,api在合适的时间调用类或者方法。
3. SPI是在一些通用的标准中,为标准的实现产商提供的扩展点。标准在上层提供API,API内部使用了SPI,当API被客户使用时,会动态得从当前运行的classpath中寻找该SPI的实现,然后使用该SPI的实现来完成API的功能。
 

 

  • 架构设计-java spi实现模块间的松耦合
            
    
    博客分类: 架构设计java spi架构设计 
  • 大小: 9.3 KB
  • 架构设计-java spi实现模块间的松耦合
            
    
    博客分类: 架构设计java spi架构设计 
  • 大小: 3.8 KB
  • 架构设计-java spi实现模块间的松耦合
            
    
    博客分类: 架构设计java spi架构设计 
  • 大小: 37 KB
  • 架构设计-java spi实现模块间的松耦合
            
    
    博客分类: 架构设计java spi架构设计 
  • 大小: 32 KB
  • 架构设计-java spi实现模块间的松耦合
            
    
    博客分类: 架构设计java spi架构设计 
  • 大小: 33.8 KB
相关标签: spi 架构设计