您的位置:首页 > 其它

关于使用SPI来减少项目中的if else判断

2018-02-28 14:26 351 查看
当我们项目中的业务模式比较多且复杂的时候,比如我现在做的项目,多条业务模式,对应的状态流程还都不一样,用FSM也搞不定。在这样的情况下,不同的业务进来,就会需要做很多的分支判断,很明显,这是不符合我们的开发规范的。
那么在这种情况下我们引入了SPI来解决这样的问题。
SPI全称为(Service Provider Interface),是JDK内置的一种服务提供发现机制,我主要使用的是类似Dubbo中的使用方式,简单画一下调用图:



废话不多说,直接代码走起:
1.首先先实现SPI服务接口加载类public class ExtensionServiceLoader<T extends PatternComponent> {
private static final Logger LOGGER = Logger.getLogger(ExtensionServiceLoader.class);

private static final String SERVICES_DIRECTORY = "/META-INF/services/";

//存放不同类型的loader
private static final ConcurrentMap<Class<? extends PatternComponent>, ExtensionServiceLoader<? extends PatternComponent>> loaderMap = new ConcurrentHashMap<>();

//存放不同实例。<模式KEY,具体实现对象>
private final ConcurrentMap<String, T> instanceMap = new ConcurrentHashMap<>();

private Class<T> type;

static {
init();
}

private static void init() {
final String methodName = "init";
Class<PatternComponent> patternClass = PatternComponent.class;
String packageName = patternClass.getPackage().getName();
String folder = "/" + packageName.replace(".", "/") + "/" + patternClass.getSimpleName() + ".class";
URL url = ExtensionServiceLoader.class.getResource(folder);
File[] files = new File(url.getFile()).getParentFile().listFiles();
if (files == null || files.length == 0) {
LOGGER.error(methodName, "初始化模式结构异常");
return;
}
String name;
Class<?> clazz;
for (File file : files) {
name = file.getName();
if (name.endsWith(".class")) {
try {
clazz = Class.forName(packageName + "." + name.substring(0, name.length() - ".class".length()));
} catch (ClassNotFoundException e) {
LOGGER.error(methodName, "未找到相应的类", e);
continue;
}

if (patternClass.isAssignableFrom(clazz)) {
if (clazz.getAnnotation(SPI.class) != null) {
getDefaultServiceInstance(clazz.asSubclass(patternClass));
LOGGER.info(methodName, clazz.getSimpleName() + "组装完毕");
} else {
if (patternClass != clazz) {
LOGGER.error(methodName, clazz + "是PatternComponent子类,但没有SPI注解");
}
}
}
}
}
}

/**
* 获取Service实例
*
* @param exten
4000
sionType 组件类型
* @param modeKey 模式名称
* @return 返回模式组件实例,可能返回null
*/
public static <T extends PatternComponent> T getServiceInstance(Class<T> extensionType, String modeKey) {
ExtensionServiceLoader<T> loader = getServiceLoader(extensionType);
if (loader == null) {
return null;
}
return loader.getServiceInstance(modeKey);
}

/**
* 获取默认Service实例
*
* @param extensionType 组件类型
* @return 返回默认模式组件实例,可能返回null
*/
public static <T extends PatternComponent> T getDefaultServiceInstance(Class<T> extensionType) {
ExtensionServiceLoader<T> loader = getServiceLoader(extensionType);
if (loader == null) {
return null;
}
String defaultModeKey = extensionType.getAnnotation(SPI.class).value();
if (StringUtils.isNotBlank(defaultModeKey)) {
return loader.getServiceInstance(defaultModeKey);
}

LOGGER.error("getDefaultServiceInstance", extensionType.getName() + "无默认值组件");
return null;
}

/**
* Description: . spi服务加载实现<br/>
*
* @param extensionType 模式类型
* @return null表示异常
* @throws IllegalArgumentException 参数为null或没有<code>SPI</code>注解
*/
private static <T extends PatternComponent> ExtensionServiceLoader<T> getServiceLoader(Class<T> extensionType) {
if (extensionType == null) {
throw new IllegalArgumentException("extensionType is null!");
}
if (!extensionType.isAnnotationPresent(SPI.class)) {
throw new IllegalArgumentException("extensionType (" + extensionType + ")Invalid services,because: No annotations (@" + SPI.class.getSimpleName() + ")!");
}
//从map中获取
ExtensionServiceLoader<T> serviceLoader = (ExtensionServiceLoader<T>) loaderMap.get(extensionType);
if (serviceLoader == null) {
try {
serviceLoader = new ExtensionServiceLoader<>(extensionType);
} catch (Exception e) {
LOGGER.error("getServiceLoader", extensionType + "ExtensionServiceLoader()异常", e);
return null;
}
loaderMap.put(extensionType, serviceLoader);
}
return serviceLoader;
}

private T getServiceInstance(String name) {
final String methodName = "getServiceInstance";
if (name == null || name.trim().length() == 0) {
LOGGER.error(methodName, "模式名字为空");
return null;
}
//从map中取实例如果取不到 就创建并存放到map中
return instanceMap.get(name.trim());
}

/**
* 构造方法
*
* @param extensionType 类型
* @throws IOException 读取配置文件异常
* @throws IllegalStateException 配置文件有效内容为空
*/
private ExtensionServiceLoader(Class<T> extensionType) throws IOException {
this.type = extensionType;//该loader对应的类型
this.loadExtensionClasses(extensionType);
}

/**
* Description: 加载不同的类定义. <br/>
*
* @param extensionType 扩展类型
* @throws IOException 读取配置文件异常
* @throws IllegalStateException 配置文件有效内容为空
*/
private void loadExtensionClasses(Class<T> extensionType) throws IOException {
final String methodName = "loadExtensionClasses";
//文件名 也就是接口名
String fileName = SERVICES_DIRECTORY + extensionType.getName();
Properties properties = new Properties();

InputStreamReader inputStreamReader = null;
InputStream inputStream = null;
try {
inputStream = ExtensionServiceLoader.class.getResourceAsStream(fileName);
if (inputStream == null) {
inputStream = ClassLoader.getSystemClassLoader().getResourceAsStream(fileName);
}
inputStreamReader = new InputStreamReader(inputStream, "UTF-8");
properties.load(inputStreamReader);
} catch (IOException e) {
LOGGER.error(methodName, "加载文件异常", e);
throw e;
} finally {
IOUtils.close(inputStreamReader, inputStream);
}

if (properties.isEmpty()) {
throw new IllegalStateException(fileName + "配置文件有效内容为空");
}

Class<?> clazz;
for (Map.Entry<Object, Object> entry : properties.entrySet()) {
try {
clazz = Class.forName(entry.getValue().toString().trim());
} catch (ClassNotFoundException e) {
LOGGER.error(methodName, "未找到相应的类" + entry.getValue().toString(), e);
continue;
}
if (type.isAssignableFrom(clazz)) {
Component component = clazz.getAnnotation(Component.class);
if (component == null) {
LOGGER.error(methodName, clazz + "不是Spring容器中的实例");
continue;
}
Class<? extends T> clazzT = clazz.asSubclass(type);
instanceMap.put(entry.getKey().toString(), SpringContextHolder.getApplicationContext().getBean(clazzT));
LOGGER.info(methodName, entry.getKey() + ":" + clazzT.getName() + "组装完毕");
} else {
LOGGER.error(methodName, entry.getValue() + "不是指定的类型" + extensionType);
}
}
}

}然后在对应的接口上加@SPI注解@SPI
public interface S*** extends PatternComponent {

void s***(String a1, String a2, String a3, String a4);
}在调用接口的时候直接使用我们实现的SPI服务接口加载类ExtensionServiceLoader获取接口实例。 S*** s = ExtensionServiceLoader.getServiceInstance(S***.class, modeCode);
if (s != null) {
s.signContract(a1, a2, a3, a4);
} else {
LOGGER.error("模式实现类为null,modeCode:" + modeCode);
}当然,如果有新的业务模式加入,我们只需要对这个接口进行实现,并且将新的实现类配置到配置文件中,然后通过新的key就可以展开新的业务开发,而且不会影响到原有业务。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐