开发平台在 yarn客户端 提交机上 提交Flink任务时,需要加载 非结构化数据解析器jar 和 自定义函数jar
考虑到 java 自带的 ClassLoader 无法灵活的添加某路径下的类,考虑以下三种方式实现:
方案一和方案二本质上是一样的,此处只实现方案一
demo类:ExtClasspathLoader.java
方案三扩展性更强,不仅仅可以实现加载类,也可实现加载资源文件。但是使用各个类时,必须手动引入,而方案一和方案二可以把路径上的所有类都自动引入
demo类:DiskClassLoader.java
参考:https://blog.csdn.net/briblue/article/details/54973413
package com.wj.classloader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
/**
* 根据properties中配置的路径把jar和配置文件加载到classpath中。
* 此工具类加载类时使用的是SystemClassLoader,如有需要对加载类进行校验,请另外实现自己的加载器 *
*/
public class ExtClasspathLoader {
private static final Logger LOG = LoggerFactory.getLogger(ExtClasspathLoader.class);
private static final String JAR_SUFFIX = ".jar";
private static final String ZIP_SUFFIX = ".zip";
/**
* URLClassLoader的addURL方法
*/
private static Method addURL = initAddMethod();
/**
* Application Classloader
*/
private static URLClassLoader classloader = (URLClassLoader) ClassLoader.getSystemClassLoader();
/**
* 初始化addUrl 方法.
*
* @return 可访问addUrl方法的Method对象
*/
private static Method initAddMethod() {
try {
Method add = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
add.setAccessible(true);
return add;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 通过filepath加载文件到classpath。
*
* @param file 文件路径
*/
private static void addURL(File file) throws Exception {
addURL.invoke(classloader, file.toURI().toURL());
}
/**
* load Resource by Dir
*
* @param file dir
*/
public static void loadResource(File file) throws Exception {
// 资源文件只加载路径
LOG.info("load Resource of dir : " + file.getAbsolutePath());
if (file.isDirectory()) {
addURL(file);
File[] subFiles = file.listFiles();
if (subFiles != null) {
for (File tmp : subFiles) {
loadResource(tmp);
}
}
}
}
/**
* load Classpath by Dir
*
* @param file Dir
*/
public static void loadClasspath(File file) throws Exception {
LOG.info("load Classpath of dir : " + file.getAbsolutePath());
if (file.isDirectory()) {
File[] subFiles = file.listFiles();
if (subFiles != null) {
for (File subFile : subFiles) {
loadClasspath(subFile);
}
}
} else {
if (file.getAbsolutePath().endsWith(JAR_SUFFIX) || file.getAbsolutePath().endsWith(ZIP_SUFFIX)) {
addURL(file);
}
}
}
}
方法 loadClasspath 和 loadResource 分别可以加载 指定路径 下的jar 和 资源文件,此方案使用最为方便。
具体使用方法,可查看最后的测试demo。
package com.wj.classloader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URL;
/**
* 从本地加载文件
*/
public class DiskClassLoader extends ClassLoader {
private String mLibPath;
public DiskClassLoader(String path) {
mLibPath = path;
}
@Override
protected Class> findClass(String name) throws ClassNotFoundException {
String fileName = getFileName(name);
File file = new File(mLibPath, fileName);
try {
FileInputStream is = new FileInputStream(file);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
int len = 0;
try {
while ((len = is.read()) != -1) {
bos.write(len);
}
} catch (IOException e) {
e.printStackTrace();
}
byte[] data = bos.toByteArray();
is.close();
bos.close();
return defineClass(name, data, 0, data.length);
} catch (IOException e) {
e.printStackTrace();
}
return super.findClass(name);
}
/**
* 获取要加载 的class文件名
*
* @param name String
* @return String
*/
private String getFileName(String name) {
int index = name.lastIndexOf('.');
if (index == -1) {
return name + ".class";
} else {
return name.substring(index + 1) + ".class";
}
}
@Override
public URL getResource(String name) {
return super.getResource(name);
}
}
此方式适合加载指定路径上的某个文件,具体使用方法,可查看最后的测试demo。
package com.wj.classloader;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* 测试 ExtClasspathLoader 和 DiskClassLoader
*/
public class Main {
private static final String PATH = "E:\\IDEJ_workspace\\wj_proj\\java-se\\src\\main\\java\\com\\wj\\classloader\\Person.java";
private static final String CLASS_FULL_NAME = "com.wj.classloader.Person";
public static void main(String[] args) throws Exception {
System.out.println("测试 DiskClassLoader.");
testDiskClassLoader();
System.out.println("---------------------");
System.out.println("测试 ExtClasspathLoader.");
testExtClasspathLoader();
}
private static void testExtClasspathLoader() throws Exception {
ExtClasspathLoader.loadClasspath(new File(PATH));
Object obj = Class.forName(CLASS_FULL_NAME).newInstance();
System.out.println(obj);
}
private static void testDiskClassLoader() {
// 创建自定义classloader对象
DiskClassLoader diskLoader = new DiskClassLoader(PATH);
try {
// 加载class文件
Class c = diskLoader.loadClass(CLASS_FULL_NAME);
if (c != null) {
try {
Object obj = c.newInstance();
Method method = c.getDeclaredMethod("say", null);
//通过反射调用Test类的say方法
method.invoke(obj, null);
System.out.println(obj);
} catch (InstantiationException | IllegalAccessException
| NoSuchMethodException
| SecurityException |
IllegalArgumentException |
InvocationTargetException e) {
e.printStackTrace();
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
Person.java
package com.wj.classloader;
/**
* 测试类 Person
*/
public class Person {
public void say() {
System.out.println("I am wj.");
}
}
测试结果如下:
结论:DiskClassLoader 和 ExtClasspathLoader 方式都能实现加载Person类,且都能正常使用