动态加载外部SDK

130 阅读2分钟

动态加载外部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);
        }
    }
}