Java Spi
Java Spi可以找到一个接口下的所有实现类,但是需要配置一个文件,下面就是一个使用示例
接口
public interface CarInterface {
public void getColor();
}
实现类
下面有两个接口的实现类
public class BlackCar implements CarInterface {
public void getColor() {
System.out.println("black");
}
}
public class RedCar implements CarInterface {
public void getColor() {
System.out.println("red");
}
}
配置文件
需要在resources文件夹下创建一个META-INF
文件,在这个文件下创建services
文件夹,最后在这个services
文件夹中创建一个名字为上面接口的全名的文件。
在这个文件中将所有需要找到的实现类的全名写上去
调用
可以通过ServiceLoader加载需要找到的接口实现类,会得到一个迭代器,这个迭代器可以得到已经配置在接口文件中的实现类
public class CarDemo {
public static void main(String[] args) {
ServiceLoader<CarInterface> serviceLoader = ServiceLoader.load(CarInterface.class);
Iterator<CarInterface> iterator = serviceLoader.iterator();
// java spi不能单独的获取某个指定的实现类
// java spi没有IOC和AOP机制
while (iterator.hasNext()) {
CarInterface carInterface = iterator.next();
carInterface.getColor();
}
}
}
弊端
因为在接口文件中只要配置了的实现类都会在serviceLoader中全部得到,不能单独获得某个指定的实现类,也不存在IOC和AOP的机制。
Dubbo Spi
对于Java Spi中的存在的弊端,Dubbo同样也开发出了属于自己的Dubbo Spi,而且对于Java Spi存在的问题,Dubbo Spi做出了解决了方案。
依赖
<dependencies>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-common</artifactId>
<version>2.7.2</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-config-spring</artifactId>
<version>2.7.2</version>
<scope>compile</scope>
</dependency>
</dependencies>
基本使用
Dubbo Spi的基本使用和Java Spi完全一样,这里就不再介绍了,主要的区别就是配置文件中对于实现类的配置和如何获得所有实现类的方式了。
在配置文件中可以给填写的实现类前加上别名,这样就可以通过别名拿到对应的实现类了,这也解决了java spi无法拿到指定实现类的问题。
然后就可以通过下面的代码拿到指定的实现类了
public static void main(String[] args) {
// 每个接口对应一个ExtensionLoader
ExtensionLoader<Car> extensionLoader = ExtensionLoader.getExtensionLoader(Car.class);
Car redCar = extensionLoader.getExtension("red");
redCar.getColor(null);
}
还需要注意接口上必须加上@SPI注解
结果
AOP增强
在Dubbo Spi中还可以实现AOP增强,只需要添加一个包装类,在里面就可以实现需要的AOP增强
假设有如下接口
public interface Car {
public void getColor(URL url);
}
有如下实现类
public class RedCar implements Car {
public void getColor(URL url) {
System.out.println("red");
}
}
下面就是AOP增强的类,类似于静态代理
public class CarWrapper implements Car{
private Car car;
public CarWrapper(Car car) {
this.car = car;
}
@Override
public void getColor(URL url) {
System.out.println("before...");
car.getColor(url);
System.out.println("after...");
}
}
在配置文件中添加包装类,不需要加上别名
还是通过同样的调用方式
public static void main(String[] args) {
// 每个接口对应一个ExtensionLoader
ExtensionLoader<Car> extensionLoader = ExtensionLoader.getExtensionLoader(Car.class);
Car redCar = extensionLoader.getExtension("red");
redCar.getColor(null);
}
结果
IOC依赖注入
在Dubbo Spi中同样也可以实现依赖注入,不过这就需要借助URL的帮助
假如有如下接口
@SPI
public interface Driver {
void driveCar(URL url);
}
存在如下实现类,会发现实现类中存在一个car对象,这是上面我们实现的接口,还需要提供这个接口的set方法,这样才能完成注入
public class Trucker implements Driver {
private Car car;
public void setCar(Car car) {
this.car = car;
}
@Override
public void driveCar(URL url) {
car.getColor(url);
}
}
我们知道car接口的实现类不止一个,那么如如何才能指定需要的实现类呢,这里就需要URL的帮助,同时需要在注入的接口上加上一个@Adaptive注解,里面的参数就是相当于一个标识
在调用的时候只需要将需要调用将需要注入的实现类封装成一个url传进第一个调用的方法里就可以了,url中的map就是存放之前在注解中设置的参数名和对应的实现类的别名。
public static void main(String[] args) {
ExtensionLoader<Driver> extensionLoader =
ExtensionLoader.getExtensionLoader(Driver.class);
Driver driver = extensionLoader.getExtension("trucker");
Map<String, String> map = new HashMap<>();
map.put("car", "black");
URL url = new URL("", "", 0, map);
driver.driveCar(url);
}
需要注意的是只有第一次传入url才会注入对象,后面就没有用了