eclipse用普通java项目实现最简单的SPI Demo

先上源码及实现效果后解释:

1、项目结构

请注意META-INF文件夹建的位置。

2、编写一个接口:IUserManagerService

package service;

public interface IUserManagerService {

	public void add();
	
	public void delete();
	
	public void update();
	
	public void query();
	
}

3、编写三家公司的具体实现:

package service;
import service.IUserManagerService;
public class A_UserManagerServiceImpl implements IUserManagerService {
	@Override
	public void add() {
		System.out.println("A公司提供的新增逻辑");
	}
	@Override
	public void delete() {
		System.out.println("A公司提供的删除逻辑");
	}
	@Override
	public void update() {
		System.out.println("A公司提供的修改逻辑");
	}
	@Override
	public void query() {
		System.out.println("A公司提供的查询逻辑");
	}
}
package service;
import service.IUserManagerService;
public class B_UserManagerServiceImpl implements IUserManagerService {
	@Override
	public void add() {
		System.out.println("B公司提供的新增逻辑");
	}
	@Override
	public void delete() {
		System.out.println("B公司提供的删除逻辑");
	}
	@Override
	public void update() {
		System.out.println("B公司提供的修改逻辑");
	}
	@Override
	public void query() {
		System.out.println("B公司提供的查询逻辑");
	}
}
package service;
import service.IUserManagerService;
public class C_UserManagerServiceImpl implements IUserManagerService {
	@Override
	public void add() {
		System.out.println("C公司提供的新增逻辑");
	}
	@Override
	public void delete() {
		System.out.println("C公司提供的删除逻辑");
	}
	@Override
	public void update() {
		System.out.println("C公司提供的修改逻辑");
	}
	@Override
	public void query() {
		System.out.println("C公司提供的查询逻辑");
	}
}

4、编写META-INF/services/service.IUserManagerService

请注意service.IUserManagerService这个文件名是刚才我们定义的接口的全限定名称。一定不要写错。

文件里要写你定义的这个接口具体要用哪个实现类:比如我用A公司的实现类,那么如下图所示

5、编写测试类:

package main;
import java.util.Iterator;
import java.util.ServiceLoader;
import service.IUserManagerService;
public class Test {
	public static void main(String[] args) {
        ServiceLoader<IUserManagerService> operations = ServiceLoader.load(IUserManagerService.class);
        Iterator<IUserManagerService> operationIterator = operations.iterator();
        while (operationIterator.hasNext()) {
        	IUserManagerService service = operationIterator.next();
        	service.add();
        	service.delete();
        	service.update();
        	service.query();
        }
	}
}

6、运行实现并修改service.IUserManagerService文件进行测试

改造一下META-INF/services/service.IUserManagerService文件内容为:

service.B_UserManagerServiceImpl

再次测试:

 

总结:

这就是java的SPI(Service Provider Interface)技术。总的来看他是基于META-INF下services文件中配置的文件名来找到接口,之后按照接口内的配置的要实现的实现类进行了代理实现。

这样做的好处就是:当我们针对一个接口拥有多种实现的时候,我们不需要改java代码就可以轻松实现切换实现方式。也就是一种动态实现接口的方式。

具体底层实现原理:

1、调用AppClassLoader类加载器加载接口interface service.IUserManagerService

    public static <S> ServiceLoader<S> load(Class<S> service) {
        ClassLoader cl = Thread.currentThread().getContextClassLoader();
        return ServiceLoader.load(service, cl);
    }

    public static <S> ServiceLoader<S> load(Class<S> service,  ClassLoader loader)
    {
        return new ServiceLoader<>(service, loader);
    }

 

2、获取到service和loader之后调用reload方法,去执行LazyIterator方法读取配置的文件中的实现内容LazyIterator迭代器

    private ServiceLoader(Class<S> svc, ClassLoader cl) {
        service = Objects.requireNonNull(svc, "Service interface cannot be null");
        loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;
        acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null;
        reload();
    }
    public void reload() {
        providers.clear();
        lookupIterator = new LazyIterator(service, loader);
    }

3、遍历我们获取的迭代器

//也就是main方法中的这一步operationIterator.hasNext()
while (operationIterator.hasNext()) {
        	IUserManagerService service = operationIterator.next();
        	service.add();
        	service.delete();
        	service.update();
        	service.query();
        }


//之后跳转至刚才new出的迭代器中,进入迭代器的hasNextService方法中
//注意 prefix其实就是   "META-INF/services/"
private static final String PREFIX = "META-INF/services/";
private boolean hasNextService() {
            if (nextName != null) {
                return true;
            }
            if (configs == null) {
                try {
                    String fullName = PREFIX + service.getName();
                    if (loader == null)
                        configs = ClassLoader.getSystemResources(fullName);
                    else
                        configs = loader.getResources(fullName);
                } catch (IOException x) {
                    fail(service, "Error locating configuration files", x);
                }
            }
            while ((pending == null) || !pending.hasNext()) {
                if (!configs.hasMoreElements()) {
                    return false;
                }
                pending = parse(service, configs.nextElement());
            }
            nextName = pending.next();
            return true;
        }

执行到pending = parse的时候

可以看到其实是读取到了文件中的内容。

之后进入main方法的IUserManagerService service = operationIterator.next();中获得这个实现类,今儿可以调用增删改查的方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值