动态加载外部SDK
最近由于公司要升级cyberark(密码托管的服务器)版本,开发环境已经升级,生产环境还没有升级。而连接cyberark的SDK新旧版本不兼容,导致用户需要在不同环境用不同版本的服务,用户抱怨,于是想需要一个兼容的方案。
-- jars
利用spark的--jars参数,将外部SDK在启动spark-submit作用时引入到classpath中。 但这种方式有一个缺点,这种方式需要我们把pom中的外部SDK依赖的scope改为provided, 由于旧代码一些不完善的实现方式,导致执行作业的时候,即使不需要连cyberark,也调用了当前SDK的一些API,因此provided会导致一部分用户的作业在不需要调用cyberark同时他们的服务器中没有这个sdk的jar包时会出现失败。要想兼容,要修复旧代码。
动态加载外部SDK
利用URLCloassLoader手动加载外部SDK jar包,需满足两个条件: 1. 在所有调用cybberark相关API方法前SDK已经加载, 2. 只需要加载一次,避免重复加载 鉴于上述两个条件,有两个思路来实现,一个是利用Spring的Bean,另一个是AOP,这里我们采用Spring的Bean的方式。
public class PluginImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar, EnvironmentAware {
private static final Logger LOGGER = LoggerFactory.getLogger(PluginImportBeanDefinitionRegistrar.class);
@Override
public void setEnvironment(Environment environment) {
}
@Override
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
JarFile jarFile = null;
try {
InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("com/test/SDK.class");
if (Objects.nonNull(is)) {
System.out.println("already loaded XXX SDK");
}
// 根据外部环境判断当前cyberark版本
String ver = "10";
File file = new File("path");
URL url = file.toURI().toURL();
jarFile = new JarFile(file);
URLClassLoader classLoader = (URLClassLoader) Thread.currentThread().getContextClassLoader();
Method method = URLClassLoader.class.getDeclaredMethod("addURL", URLClassLoader.class);
if (!method.isAccessible()) {
method.setAccessible(true);
}
method.invoke(classLoader, url);
for (Enumeration<JarEntry> ea = jarFile.entries(); ea.hasMoreElements(); ) {
JarEntry jarEntry = ea.nextElement();
String name = jarEntry.getName();
if (StringUtils.endsWithIgnoreCase(name, ".class")) {
classLoader.loadClass(name.replace("/", ".").substring(0, name.length()
- 6));
}
}
System.out.println("load XXX SDK sussessfully");
} catch (Exception e) {
LOGGER.error("load XXX SDK jar error", e);
}
}
}